-
Notifications
You must be signed in to change notification settings - Fork 260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ui): Client-side sorting in RoomList
#3585
Conversation
c65c6f1
to
ead4061
Compare
RoomList
to manipulate Room
instead of RoomListEntry
RoomList
40cd1a5
to
b3f8993
Compare
b3f8993
to
781d170
Compare
b3562d6
to
eaa1bbd
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3585 +/- ##
==========================================
+ Coverage 84.21% 84.26% +0.04%
==========================================
Files 256 259 +3
Lines 26553 26505 -48
==========================================
- Hits 22362 22334 -28
+ Misses 4191 4171 -20 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great piece! I think there's a bug when it comes to the UTD hook, so would like to see just the minimal changes needed to make this work again. To make the next round of review super fast, I would propose to have fixup!
commits so I can quickly check these, and then you could autosquash and we could merge later. How does that sound?
testing/matrix-sdk-integration-testing/src/tests/sliding_sync/room.rs
Outdated
Show resolved
Hide resolved
@@ -292,7 +292,7 @@ impl BaseClient { | |||
|
|||
trace!("ready to submit changes to store"); | |||
store.save_changes(&changes).await?; | |||
self.apply_changes(&changes, false); | |||
self.apply_changes(&changes, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be needed, can you revert it, please? or if you need it, a code comment would be appreciated to explain why! in the e2ee equivalent, it's required because there's no direct causality between "receiving e2ee info" and "updating the room list", but a successful decryption of the latest event requires the room list to be updated, hence the true
there. Here, since we're receiving a room update, the room should automatically update because the room stream will fire.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The room stream won't fire if the room already exists. A room info update is likely to trigger a room list update if the latest event changes for example. Hence the true
here. I'm adding a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would lead to spurious updates, because we're already saving room infos explicitly here. (The room state management is a bit of a mess, we should likely add this to the spring cleaning list). Putting true
will force a spurious update here, which might not be an issue in testing, so let's see if we can keep it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, we want to detect whether an update is necessary instead of always coercing to true
. But let's do that in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way it worked before: changing the room's data meant the vectordiff would include a change for that room (even if it's not moved position in the list: just to notify that there's a change in the room, so the subscribers can be notified). Hence, we didn't need to pass true
here, since the change would happen automatically.
Has this changed? Is this the actual reason why you need to pass true
here?
end; | ||
}; | ||
|
||
assert_pending!(dynamic_entries_stream); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you keep a test with this code, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I meant to select the few lines above too. I think you deleted some test code that is now untested, and that would be nice to keep as some sort of unit testing for the manual update functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it deserves way more tests than only two tests. I'll add a couple more tests with another PR, as I also think we should revisit this room info update thingy.
The idea is to get the `SortBy` stream adapter.
This patch is quite big… `RoomList::entries*` now returns `Room`s instead of `RoomListEntry`s. This patch consequently updates all the filters to manipulate `Room` instead of `RoomListEntry`. No more `Client` is needed in the filters. This patch also disables the `RoomList` integration test suite in order to keep this patch “small”.
2d906b7
to
824cb06
Compare
fe85d41
to
cb2173f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet! The commit history is messy and I would like you to tidy it up before merging, please, to avoid intermediate commits that should've been folded into other commits in the first place.
Otherwise, the new commits look good; I haven't looked again at the previous ones, since you've addressed all the comments there.
Thank you very much, and great work here 🥳
This patch adapts the `RoomList` FFI API to the recent changes to suport a `Stream<Item = RoomListItem>` instead of a `Stream<Item = RoomListEntry>`. Behind the scene, it supports client side sorting for the rooms but this is transparent for this API. This patch also removes the `RoomListInput` enum as no input is supporter anymore. The `entries` method no long returns a `RoomListEntriesResult` but directly a `TaskHandle`. The given listener will receive the initial entries as a `VectorDiff::Append`, which first is simpler but also fixe a potential race condition bug.
Complement uses the FFI `RoomList` API. Since the patch set modifies this API, Complement is broken. We disable it and will re-enable it once we have updated Complement.
f633fb0
to
1924083
Compare
This patch rewrites `merge_stream_and_receiver` to switch the order of `roominfo_update_recv` and `raw_stream`. The idea is to give the priority to `raw_stream` since it will necessarily trigger the room items recomputation. This patch also remove the `for` loop with `Iterator::enumerate`, to simply use `Iterator::position`: it's more compact and it removes a `break` (it makes the code simpler to understand). Finally, this patch renames `merged_stream` into `merged_streams`.
This patch removes the `RoomListService::rooms` cache, since now a `Room` is pretty cheap to build. This cache was also used to keep the `Timeline` alive, but it's now recommended that the consumer of the `Room` keeps its own clone of the `Timeline` somewhere. We may introduce a cache inside `RoomListService` for the `Timeline` later.
1924083
to
765b954
Compare
I've changed the code to use the @bnjbvr Can you review 9744ad4, 2229489 and 063fa52 please? @jmartinesp confirms it now work similarly to what we have previously, and I confirm that too with multiverse. |
d108426
to
1f1ff3d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yay
…tamp`. This patch adds a new field in `RoomInfo`: `recency_timestamp: Option<MilliSecondsSinceUnixEpoch>>`. Its value comes from a Sliding Sync Room response, that's why all this API is behind `cfg(feature = "experimental-sliding-sync")`.
1f1ff3d
to
b6cac72
Compare
This patch changes the `recency` sorter to use `Room::recency_timestamp` instead of `LatestEvent::event_origin_server_ts` to sort rooms.
This patch removes the `LatestEvent::cached_event_origin_ts`. It's no longer necessary to cache this value as the `matrix_sdk_ui::room_list_service::sorter::recency` sorter no longer uses it.
b6cac72
to
3588b88
Compare
Complement crypto is expected but I've disabled it. Force merging the PR as all other CI jobs are fine. |
The goal of this PR is to implement client-side sorting in
RoomList
.So far, we used to use the
sort
parameter of sliding sync to sort the rooms. It is set toby_recency
andby_name
(see Sliding Window API in MSC3675). But it brings some problems.Problems
name
is not the nameSorting by name might be wrong because: the name is computed by the proxy, and was wrongly calculated sometimes, see the following patches that try to fix this problem:
We no longer use the
name
value from sliding sync anymore. Thus, sortingby_name
may result in incorrect results: the sliding sync proxy sorts 2 rooms by their name, but the names that are calculated by the SDK can be different, thus resulting into inconsistent ordering on the client-side.ops
is costly and error-prone!The ordering was ensured by sliding sync under the form of “sync operations” (see Sliding Window API in MSC3575). This is costly for the server, which is a major argument on itself. Removing these “sync operations” would reduce the charge of running the sliding sync proxy. But we also believe it might be the source of multiple bugs, one of them is we get duplicated rooms, see for example:
Modifying
sort
might feel slowIf the user wants to change the ordering, we have to send new requests, which are going to refresh the entire room list. Things can feel very slow, and “webby”/non-native. This is not the case with filters!, where everything happens dynamically, fast, in real-time. It's been possible because of contributions on
eyeball
and inside the SDK itself:RoomList::entries_with_dynamic_filter
#2392normalized_match_room_name
filter #2428none
filter #2594The unnecessary
RoomListEntry
So far, since sorting happens server-side, our sliding sync implementation (
matrix_sdk::sliding_sync
) provides a stream of rooms as aRoomListEntry
, which is an enum mimicking the semantics of sliding sync's own representation of a room withEmpty
,Invalidated(RoomId)
orFilled(RoomId)
. The major problem is that thisRoomListEntry
spreads everywhere in thematrix_sdk::sliding_sync
andmatrix_sdk_ui::room_list_service
APIs. It means the users receiveRoomListEntry
in theirStream
of updates and so on. It implies that the users manipulate aRoomId
only, and thus must call a method such asRoomListService::room
to get a properRoom
! This is unnecessary FFI boundary crossings, which is costly. The whole API feels unnecessarily complex. Ideally, the user wants to manipulate aStream<Item = Vec<VectorDiff<Room>>>
directly instead ofStream<Item = Vec<VectorDiff<RoomListEntry>>>
.visible_rooms
seems broken and will break for sureRoomListService
uses sliding sync with 2 lists:all_rooms
andvisible_rooms
.all_rooms
to fetch all rooms withtimeline_limit=1
, andvisible_rooms
to fetch a particular range of rooms withtimeline_limit=20
. In some cases, for some users,visible_rooms
feels broken or inactive. The rooms in the user's app viewport are not preloaded with a timeline of 20 events. Ideally, we want to replacevisible_rooms
by roomsubscriptions
(see Room Subscription API in MSC3575).Once we have client-side sorting,
visible_rooms
will be entirely broken because a server-side range won't map to a client-side range anymore, making the use ofvisible_rooms
impossible.Simplified native sliding sync implementation
Finally, the last reason to have client-side sorting is that we are trying to simplify the sliding sync specification. Why? Because we are trying to sunset the proxy in favor of a native implementation inside Synapse, see:
/sync
endpoint (initial implementation) element-hq/synapse#17187is_dm
filtering to Sliding Sync/sync
element-hq/synapse#17277is_encrypted
filtering to Sliding Sync/sync
element-hq/synapse#17281stream_ordering
sort to Sliding Sync/sync
element-hq/synapse#17293is_invite
filtering to Sliding Sync/sync
element-hq/synapse#17335/sync
element-hq/synapse#17320And for the SDK side, see:
Sliding sync proxy has served us well, and now we know how to provide stable and fast sync to the Matrix ecosystem. It was an experimental project. Now it's time to move onto production ready implementation, and a clean up is necessary. We want to remove the “sync operations”, which means the client has to support its own sorting.
Here we are
And here we are. Matrix Rust SDK must implement client-side sorting inside
matrix_sdk_ui::room_list_service
.matrix_sdk::sliding_sync
, similarly tomatrix_sdk::sync
, sends requests, receives responses, creates or updates the rooms and so on. Then, it's up tomatrix_sdk_ui::room_list_service
to provide an API on tops of the rooms inside the SDK.To be here today, we needed to tackle these tasks:
SlidingSyncRoom
tech debt #3079 (with a lot of sub tasks…)room_list_service::Room::latest_event
no longer usesSlidingSyncRoom
(+ dropSlidingSyncRoomExt
) #3540Store::get_rooms
wayyy faster #3552Then, the first step was
Client
to export aStream<Item = Vec<VectorDiff<Room>>>
. This was done with:Client::rooms_stream
#3068After that, we needed
room_list_service::Room
to be infallible and non-async. This was done with:RoomListService::room
is no longer async! #3551room_list_service::Room::new
is now infallible #3586Finally, we needed
eyeball
to provide aSort
stream adapter to be able to sort aStream
(yup!):SortBy
stream adapter jplatte/eyeball#43And now, … only now, we have everything to implement client-side sorting dear readers!
Tasks
RoomList
to manipulateRoom
instead ofRoomListEntry
Room
instead ofRoomListEntry
visible_rooms
RoomList