2021-11-09 16:57:57 +00:00
|
|
|
package sync3
|
|
|
|
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
import (
|
2023-04-05 14:11:05 +01:00
|
|
|
"context"
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
"strings"
|
|
|
|
|
2022-12-15 11:08:50 +00:00
|
|
|
"github.com/matrix-org/sliding-sync/internal"
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
)
|
2021-11-09 16:57:57 +00:00
|
|
|
|
2022-05-31 10:33:56 +01:00
|
|
|
type OverwriteVal bool
|
|
|
|
|
|
|
|
var (
|
|
|
|
DoNotOverwrite OverwriteVal = false
|
|
|
|
Overwrite OverwriteVal = true
|
|
|
|
)
|
|
|
|
|
2022-08-26 13:54:44 +01:00
|
|
|
// ListOp represents the possible operations on a list
|
|
|
|
type ListOp uint8
|
|
|
|
|
|
|
|
var (
|
|
|
|
// The room is added to the list
|
|
|
|
ListOpAdd ListOp = 1
|
|
|
|
// The room is removed from the list
|
|
|
|
ListOpDel ListOp = 2
|
|
|
|
// The room may change position in the list
|
|
|
|
ListOpChange ListOp = 3
|
|
|
|
)
|
|
|
|
|
|
|
|
type RoomListDelta struct {
|
2022-12-20 13:32:39 +00:00
|
|
|
ListKey string
|
|
|
|
Op ListOp
|
2022-08-26 13:54:44 +01:00
|
|
|
}
|
|
|
|
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
type RoomDelta struct {
|
bugfix: fix a bug with list ops when sorting with unread counts; fix a bug which could cause typing/receipts to not be live streamed
Previously, we would not send unread count INCREASES to the client,
as we would expect the actual event update to wake up the client conn.
This was great because it meant the event+unread count arrived atomically
on the client. This was implemented as "parse unread counts first, then events".
However, this introduced a bug when there were >1 user in the same room. In this
scenario, one poller may get the event first, which would go through to the client.
The subsequent unread count update would then be dropped and not sent to the client.
This would just be an unfortunate UI bug if it weren't for sorting by_notification_count
and sorting by_notification_level. Both of these sort operations use the unread counts
to determine room list ordering. This list would be updated on the server, but no
list operation would be sent to the client, causing the room lists to de-sync, and
resulting in incorrect DELETE/INSERT ops. This would manifest as duplicate rooms
on the room list.
In the process of fixing this, also fix a bug where typing notifications would not
always be sent to the client - it would only do so when piggybacked due to incorrect
type switches.
Also fix another bug which prevented receipts from always being sent to the client.
This was caused by the extensions handler not checking if the receipt extension had
data to determine if it should return. This the interacted with an as-yet unfixed bug
which cleared the extension on subequent updates, causing the receipt to be lost entirely.
A fix for this will be inbound soon.
2023-02-07 13:34:26 +00:00
|
|
|
RoomNameChanged bool
|
2023-07-18 10:58:33 +01:00
|
|
|
RoomAvatarChanged bool
|
bugfix: fix a bug with list ops when sorting with unread counts; fix a bug which could cause typing/receipts to not be live streamed
Previously, we would not send unread count INCREASES to the client,
as we would expect the actual event update to wake up the client conn.
This was great because it meant the event+unread count arrived atomically
on the client. This was implemented as "parse unread counts first, then events".
However, this introduced a bug when there were >1 user in the same room. In this
scenario, one poller may get the event first, which would go through to the client.
The subsequent unread count update would then be dropped and not sent to the client.
This would just be an unfortunate UI bug if it weren't for sorting by_notification_count
and sorting by_notification_level. Both of these sort operations use the unread counts
to determine room list ordering. This list would be updated on the server, but no
list operation would be sent to the client, causing the room lists to de-sync, and
resulting in incorrect DELETE/INSERT ops. This would manifest as duplicate rooms
on the room list.
In the process of fixing this, also fix a bug where typing notifications would not
always be sent to the client - it would only do so when piggybacked due to incorrect
type switches.
Also fix another bug which prevented receipts from always being sent to the client.
This was caused by the extensions handler not checking if the receipt extension had
data to determine if it should return. This the interacted with an as-yet unfixed bug
which cleared the extension on subequent updates, causing the receipt to be lost entirely.
A fix for this will be inbound soon.
2023-02-07 13:34:26 +00:00
|
|
|
JoinCountChanged bool
|
|
|
|
InviteCountChanged bool
|
|
|
|
NotificationCountChanged bool
|
|
|
|
HighlightCountChanged bool
|
|
|
|
Lists []RoomListDelta
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
}
|
|
|
|
|
2022-05-30 21:14:50 +01:00
|
|
|
// InternalRequestLists is a list of lists which matches each index position in the request
|
|
|
|
// JSON 'lists'. It contains all the internal metadata for rooms and controls access and updatings of said
|
|
|
|
// lists.
|
|
|
|
type InternalRequestLists struct {
|
2023-02-02 11:26:08 +00:00
|
|
|
allRooms map[string]*RoomConnMetadata
|
2022-12-20 13:32:39 +00:00
|
|
|
lists map[string]*FilteredSortableRooms
|
2021-11-09 16:57:57 +00:00
|
|
|
}
|
|
|
|
|
2022-08-11 15:07:36 +01:00
|
|
|
func NewInternalRequestLists() *InternalRequestLists {
|
|
|
|
return &InternalRequestLists{
|
2023-02-02 11:26:08 +00:00
|
|
|
allRooms: make(map[string]*RoomConnMetadata, 10),
|
2022-12-20 13:32:39 +00:00
|
|
|
lists: make(map[string]*FilteredSortableRooms),
|
2022-08-11 15:07:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-24 15:16:47 +01:00
|
|
|
func (s *InternalRequestLists) SetRoom(r RoomConnMetadata) (delta RoomDelta) {
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
existing, exists := s.allRooms[r.RoomID]
|
|
|
|
if exists {
|
bugfix: fix a bug with list ops when sorting with unread counts; fix a bug which could cause typing/receipts to not be live streamed
Previously, we would not send unread count INCREASES to the client,
as we would expect the actual event update to wake up the client conn.
This was great because it meant the event+unread count arrived atomically
on the client. This was implemented as "parse unread counts first, then events".
However, this introduced a bug when there were >1 user in the same room. In this
scenario, one poller may get the event first, which would go through to the client.
The subsequent unread count update would then be dropped and not sent to the client.
This would just be an unfortunate UI bug if it weren't for sorting by_notification_count
and sorting by_notification_level. Both of these sort operations use the unread counts
to determine room list ordering. This list would be updated on the server, but no
list operation would be sent to the client, causing the room lists to de-sync, and
resulting in incorrect DELETE/INSERT ops. This would manifest as duplicate rooms
on the room list.
In the process of fixing this, also fix a bug where typing notifications would not
always be sent to the client - it would only do so when piggybacked due to incorrect
type switches.
Also fix another bug which prevented receipts from always being sent to the client.
This was caused by the extensions handler not checking if the receipt extension had
data to determine if it should return. This the interacted with an as-yet unfixed bug
which cleared the extension on subequent updates, causing the receipt to be lost entirely.
A fix for this will be inbound soon.
2023-02-07 13:34:26 +00:00
|
|
|
if existing.NotificationCount != r.NotificationCount {
|
|
|
|
delta.NotificationCountChanged = true
|
2023-01-13 18:00:59 +00:00
|
|
|
}
|
bugfix: fix a bug with list ops when sorting with unread counts; fix a bug which could cause typing/receipts to not be live streamed
Previously, we would not send unread count INCREASES to the client,
as we would expect the actual event update to wake up the client conn.
This was great because it meant the event+unread count arrived atomically
on the client. This was implemented as "parse unread counts first, then events".
However, this introduced a bug when there were >1 user in the same room. In this
scenario, one poller may get the event first, which would go through to the client.
The subsequent unread count update would then be dropped and not sent to the client.
This would just be an unfortunate UI bug if it weren't for sorting by_notification_count
and sorting by_notification_level. Both of these sort operations use the unread counts
to determine room list ordering. This list would be updated on the server, but no
list operation would be sent to the client, causing the room lists to de-sync, and
resulting in incorrect DELETE/INSERT ops. This would manifest as duplicate rooms
on the room list.
In the process of fixing this, also fix a bug where typing notifications would not
always be sent to the client - it would only do so when piggybacked due to incorrect
type switches.
Also fix another bug which prevented receipts from always being sent to the client.
This was caused by the extensions handler not checking if the receipt extension had
data to determine if it should return. This the interacted with an as-yet unfixed bug
which cleared the extension on subequent updates, causing the receipt to be lost entirely.
A fix for this will be inbound soon.
2023-02-07 13:34:26 +00:00
|
|
|
if existing.HighlightCount != r.HighlightCount {
|
|
|
|
delta.HighlightCountChanged = true
|
2023-01-13 18:00:59 +00:00
|
|
|
}
|
2022-08-30 17:27:58 +01:00
|
|
|
delta.InviteCountChanged = !existing.SameInviteCount(&r.RoomMetadata)
|
|
|
|
delta.JoinCountChanged = !existing.SameJoinCount(&r.RoomMetadata)
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
delta.RoomNameChanged = !existing.SameRoomName(&r.RoomMetadata)
|
|
|
|
if delta.RoomNameChanged {
|
|
|
|
// update the canonical name to allow room name sorting to continue to work
|
2023-09-15 15:08:07 +02:00
|
|
|
roomName, _ := internal.CalculateRoomName(&r.RoomMetadata, 5)
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
r.CanonicalisedName = strings.ToLower(
|
2023-09-15 15:08:07 +02:00
|
|
|
strings.Trim(roomName, "#!():_@"),
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
)
|
2023-07-18 13:40:12 +01:00
|
|
|
} else {
|
|
|
|
// XXX: during TestConnectionTimeoutNotReset there is some situation where
|
|
|
|
// r.CanonicalisedName is the empty string. Looking at the SetRoom
|
|
|
|
// call in connstate_live.go, this is because the UserRoomMetadata on
|
|
|
|
// the RoomUpdate has an empty CanonicalisedName. Either
|
|
|
|
// a) that is expected, in which case we should _always_ write to
|
|
|
|
// r.CanonicalisedName here; or
|
|
|
|
// b) that is not expected, in which case... erm, I don't know what
|
|
|
|
// to conclude.
|
|
|
|
r.CanonicalisedName = existing.CanonicalisedName
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
}
|
2024-01-22 11:33:09 +00:00
|
|
|
delta.RoomAvatarChanged = !existing.SameRoomAvatar(&r)
|
2023-07-18 10:58:33 +01:00
|
|
|
if delta.RoomAvatarChanged {
|
2024-01-22 10:34:06 +00:00
|
|
|
r.ResolvedAvatarURL = internal.CalculateAvatar(&r.RoomMetadata, r.IsDM)
|
2023-07-18 10:58:33 +01:00
|
|
|
}
|
2023-03-21 14:25:45 +00:00
|
|
|
|
2023-05-24 15:16:47 +01:00
|
|
|
// Interpret the timestamp map on r as the changes we should apply atop the
|
|
|
|
// existing timestamps.
|
2023-05-25 15:17:21 +01:00
|
|
|
newTimestamps := r.LastInterestedEventTimestamps
|
2023-05-24 15:16:47 +01:00
|
|
|
r.LastInterestedEventTimestamps = make(map[string]uint64, len(s.lists))
|
|
|
|
for listKey := range s.lists {
|
2023-05-25 15:17:21 +01:00
|
|
|
newTs, bump := newTimestamps[listKey]
|
2023-05-24 15:16:47 +01:00
|
|
|
if bump {
|
|
|
|
r.LastInterestedEventTimestamps[listKey] = newTs
|
|
|
|
} else {
|
|
|
|
prevTs, hadPreviousTs := existing.LastInterestedEventTimestamps[listKey]
|
|
|
|
if hadPreviousTs {
|
|
|
|
r.LastInterestedEventTimestamps[listKey] = prevTs
|
|
|
|
} else {
|
|
|
|
// This can happen if the listKey is brand-new in this request.
|
|
|
|
r.LastInterestedEventTimestamps[listKey] = existing.LastMessageTimestamp
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 14:25:45 +00:00
|
|
|
}
|
2022-09-08 09:32:16 +01:00
|
|
|
} else {
|
|
|
|
// set the canonical name to allow room name sorting to work
|
2023-09-15 15:08:07 +02:00
|
|
|
roomName, _ := internal.CalculateRoomName(&r.RoomMetadata, 5)
|
2022-09-08 09:32:16 +01:00
|
|
|
r.CanonicalisedName = strings.ToLower(
|
2023-09-15 15:08:07 +02:00
|
|
|
strings.Trim(roomName, "#!():_@"),
|
2022-09-08 09:32:16 +01:00
|
|
|
)
|
2024-01-22 10:34:06 +00:00
|
|
|
r.ResolvedAvatarURL = internal.CalculateAvatar(&r.RoomMetadata, r.IsDM)
|
2023-06-06 15:56:13 +01:00
|
|
|
// We'll automatically use the LastInterestedEventTimestamps provided by the
|
|
|
|
// caller, so that recency sorts work.
|
2022-08-11 15:07:36 +01:00
|
|
|
}
|
2022-12-14 18:53:55 +00:00
|
|
|
// filter.Include may call on this room ID in the RoomFinder, so make sure it finds it.
|
2023-02-02 11:26:08 +00:00
|
|
|
s.allRooms[r.RoomID] = &r
|
2022-12-14 18:53:55 +00:00
|
|
|
|
2022-12-20 13:32:39 +00:00
|
|
|
for listKey, list := range s.lists {
|
|
|
|
_, alreadyExists := list.roomIDToIndex[r.RoomID]
|
|
|
|
shouldExist := list.filter.Include(&r, s)
|
2022-08-31 14:48:14 +01:00
|
|
|
if shouldExist && r.HasLeft {
|
|
|
|
shouldExist = false
|
|
|
|
}
|
2022-08-26 13:54:44 +01:00
|
|
|
// weird nesting ensures we handle all 4 cases
|
|
|
|
if alreadyExists {
|
|
|
|
if shouldExist { // could be a change
|
|
|
|
delta.Lists = append(delta.Lists, RoomListDelta{
|
2022-12-20 13:32:39 +00:00
|
|
|
ListKey: listKey,
|
|
|
|
Op: ListOpChange,
|
2022-08-26 13:54:44 +01:00
|
|
|
})
|
|
|
|
} else { // removal
|
|
|
|
delta.Lists = append(delta.Lists, RoomListDelta{
|
2022-12-20 13:32:39 +00:00
|
|
|
ListKey: listKey,
|
|
|
|
Op: ListOpDel,
|
2022-08-26 13:54:44 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if shouldExist { // addition
|
|
|
|
delta.Lists = append(delta.Lists, RoomListDelta{
|
2022-12-20 13:32:39 +00:00
|
|
|
ListKey: listKey,
|
|
|
|
Op: ListOpAdd,
|
2022-08-26 13:54:44 +01:00
|
|
|
})
|
|
|
|
} // else it doesn't exist and it shouldn't exist, so do nothing e.g room isn't relevant to this list
|
|
|
|
}
|
|
|
|
}
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
return delta
|
2022-05-30 21:14:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove a room from all lists e.g retired an invite, left a room
|
|
|
|
func (s *InternalRequestLists) RemoveRoom(roomID string) {
|
2022-08-11 15:07:36 +01:00
|
|
|
delete(s.allRooms, roomID)
|
2022-05-30 21:14:50 +01:00
|
|
|
// TODO: update lists?
|
|
|
|
}
|
|
|
|
|
2022-12-20 13:32:39 +00:00
|
|
|
func (s *InternalRequestLists) DeleteList(listKey string) {
|
2023-05-25 19:11:45 +01:00
|
|
|
delete(s.lists, listKey)
|
|
|
|
for _, room := range s.allRooms {
|
|
|
|
delete(room.LastInterestedEventTimestamps, listKey)
|
|
|
|
}
|
2022-05-30 21:14:50 +01:00
|
|
|
}
|
|
|
|
|
2023-05-25 19:11:45 +01:00
|
|
|
// Returns the underlying RoomConnMetadata object. Returns a shared pointer, not a copy.
|
2023-02-02 11:26:08 +00:00
|
|
|
// It is only safe to read this data, never to write.
|
|
|
|
func (s *InternalRequestLists) ReadOnlyRoom(roomID string) *RoomConnMetadata {
|
|
|
|
return s.allRooms[roomID]
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
}
|
|
|
|
|
2023-05-25 19:11:45 +01:00
|
|
|
// Get returns the sorted list of rooms. Returns a shared pointer, not a copy.
|
|
|
|
// It is only safe to read this data, never to write.
|
2022-12-20 13:32:39 +00:00
|
|
|
func (s *InternalRequestLists) Get(listKey string) *FilteredSortableRooms {
|
|
|
|
return s.lists[listKey]
|
2022-08-26 13:54:44 +01:00
|
|
|
}
|
|
|
|
|
2023-05-25 14:45:55 +01:00
|
|
|
// ListKeys returns a copy of the list keys currently tracked by this
|
|
|
|
// InternalRequestLists struct, in no particular order. Outside of test code, you
|
|
|
|
// probably don't want to call this---you probably have the set of list keys tracked
|
|
|
|
// elsewhere in the application.
|
|
|
|
func (s *InternalRequestLists) ListKeys() []string {
|
|
|
|
keys := make([]string, len(s.lists))
|
|
|
|
for listKey, _ := range s.lists {
|
|
|
|
keys = append(keys, listKey)
|
|
|
|
}
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
2023-03-29 16:11:27 +01:00
|
|
|
// ListsByVisibleRoomIDs builds a map from room IDs to a slice of list names. Keys are
|
|
|
|
// all room IDs that are currently visible in at least one sliding window. Values are
|
|
|
|
// the names of all lists (in no particular order) in which the given room ID is
|
|
|
|
// currently visible. The value slices are nonnil and contain at least one list name
|
|
|
|
// (possibly more).
|
2023-03-29 15:39:35 +01:00
|
|
|
//
|
2023-03-29 16:11:27 +01:00
|
|
|
// The returned map is a copy, i.e. is safe to modify by the caller.
|
|
|
|
func (s *InternalRequestLists) ListsByVisibleRoomIDs(muxedReqLists map[string]RequestList) map[string][]string {
|
|
|
|
listsByRoomIDs := make(map[string][]string, len(muxedReqLists))
|
|
|
|
// Loop over each list, and mark each room in its sliding window as being visible in this list.
|
2023-05-24 15:16:47 +01:00
|
|
|
for listKey, reqList := range muxedReqLists {
|
|
|
|
sortedRooms := s.lists[listKey].SortableRooms
|
2023-03-29 16:11:27 +01:00
|
|
|
if sortedRooms == nil {
|
2023-03-29 15:39:35 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-03-29 16:41:02 +01:00
|
|
|
// If we've requested all rooms, every room is visible in this list---we don't
|
|
|
|
// have to worry about extracting room IDs in the sliding windows' ranges.
|
2023-03-29 15:39:35 +01:00
|
|
|
if reqList.SlowGetAllRooms != nil && *reqList.SlowGetAllRooms {
|
2023-03-29 16:11:27 +01:00
|
|
|
for _, roomID := range sortedRooms.RoomIDs() {
|
2023-05-24 15:16:47 +01:00
|
|
|
listsByRoomIDs[roomID] = append(listsByRoomIDs[roomID], listKey)
|
2023-03-29 16:11:27 +01:00
|
|
|
}
|
2023-03-29 15:39:35 +01:00
|
|
|
} else {
|
2023-03-29 16:11:27 +01:00
|
|
|
subslices := reqList.Ranges.SliceInto(sortedRooms)
|
2023-03-29 16:41:02 +01:00
|
|
|
for _, subslice := range subslices {
|
|
|
|
sortedRooms = subslice.(*SortableRooms)
|
2023-03-29 16:11:27 +01:00
|
|
|
for _, roomID := range sortedRooms.RoomIDs() {
|
2023-05-24 15:16:47 +01:00
|
|
|
listsByRoomIDs[roomID] = append(listsByRoomIDs[roomID], listKey)
|
2023-03-29 16:11:27 +01:00
|
|
|
}
|
2023-03-29 15:39:35 +01:00
|
|
|
}
|
|
|
|
}
|
2023-03-28 17:28:40 +01:00
|
|
|
}
|
2023-03-29 16:11:27 +01:00
|
|
|
return listsByRoomIDs
|
2023-03-28 17:28:40 +01:00
|
|
|
}
|
|
|
|
|
2022-12-20 13:32:39 +00:00
|
|
|
// Assign a new list at the given key. If Overwrite, any existing list is replaced. If DoNotOverwrite, the existing
|
2022-05-31 17:32:41 +01:00
|
|
|
// list is returned if one exists, else a new list is created. Returns the list and true if the list was overwritten.
|
2023-04-05 14:11:05 +01:00
|
|
|
func (s *InternalRequestLists) AssignList(ctx context.Context, listKey string, filters *RequestFilters, sort []string, shouldOverwrite OverwriteVal) (*FilteredSortableRooms, bool) {
|
2022-12-20 13:32:39 +00:00
|
|
|
if shouldOverwrite == DoNotOverwrite {
|
|
|
|
_, exists := s.lists[listKey]
|
|
|
|
if exists {
|
|
|
|
return s.lists[listKey], false
|
|
|
|
}
|
2022-05-31 10:33:56 +01:00
|
|
|
}
|
refactor: add RoomFinder and use it in InternalRequestLists
This is part of a series of refactors aimed to improve the performance
and complexity of calculating list deltas, which up until now exists in
its current form due to organic growth of the codebase.
This specific refactor introduces a new interface `RoomFinder` which
can map room IDs to `*RoomConnMetadata` which is used by `ConnState`.
All the sliding sync lists now use the `RoomFinder` instead of keeping
their own copies of `RoomConnMetadata`, meaning per-connection, rooms
just have 1 copy in-memory. This cuts down on memory usage as well as
cuts down on GC churn as we would constantly be replacing N rooms for
each update, where N is the total number of lists on that connection.
For Element-Web, N=7 currently to handle Favourites, Low Priority, DMs,
Rooms, Spaces, Invites, Search. This also has the benefit of creating
a single source of truth in `InternalRequestLists.allRooms` which can
be updated once and then a list of list deltas can be calculated off
the back of that. Previously, `allRooms` was _only_ used to seed new
lists, which created a weird imbalance as we would need to update both
`allRooms` _and_ each `FilteredSortableRooms` to keep things in-sync.
This refactor is incomplete in its present form, as we need to make
use of the new `RoomDelta` struct to efficiently package list updates.
2022-08-26 10:09:41 +01:00
|
|
|
roomIDs := make([]string, len(s.allRooms))
|
|
|
|
i := 0
|
|
|
|
for roomID := range s.allRooms {
|
|
|
|
roomIDs[i] = roomID
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
2023-05-24 15:20:49 +01:00
|
|
|
roomList := NewFilteredSortableRooms(s, listKey, roomIDs, filters)
|
2022-05-30 21:14:50 +01:00
|
|
|
if sort != nil {
|
2023-04-05 15:36:05 +01:00
|
|
|
err := roomList.Sort(sort)
|
2022-05-30 21:14:50 +01:00
|
|
|
if err != nil {
|
|
|
|
logger.Err(err).Strs("sort_by", sort).Msg("failed to sort")
|
2023-04-05 14:11:05 +01:00
|
|
|
internal.GetSentryHubFromContextOrDefault(ctx).CaptureException(err)
|
2022-05-30 21:14:50 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-20 13:32:39 +00:00
|
|
|
s.lists[listKey] = roomList
|
2022-05-31 17:32:41 +01:00
|
|
|
return roomList, true
|
2021-11-09 16:57:57 +00:00
|
|
|
}
|
|
|
|
|
2022-05-25 11:36:30 +01:00
|
|
|
// Count returns the count of total rooms in this list
|
2022-12-20 13:32:39 +00:00
|
|
|
func (s *InternalRequestLists) Count(listKey string) int {
|
|
|
|
return int(s.lists[listKey].Len())
|
2022-05-25 11:36:30 +01:00
|
|
|
}
|
|
|
|
|
2022-05-30 21:14:50 +01:00
|
|
|
func (s *InternalRequestLists) Len() int {
|
2022-05-25 11:36:30 +01:00
|
|
|
return len(s.lists)
|
2021-11-09 16:57:57 +00:00
|
|
|
}
|