-
Notifications
You must be signed in to change notification settings - Fork 357
feat(ui): Client-side sorting in RoomList
#3585
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
Conversation
c65c6f1 to
ead4061
Compare
RoomList to manipulate Room instead of RoomListEntryRoomList
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. |
bnjbvr
left 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.
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
| 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
bnjbvr
left 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.
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 🥳
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
bnjbvr
left 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.
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
sortparameter of sliding sync to sort the rooms. It is set toby_recencyandby_name(see Sliding Window API in MSC3675). But it brings some problems.Problems
nameis 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
namevalue from sliding sync anymore. Thus, sortingby_namemay 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.opsis 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
sortmight 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
eyeballand inside the SDK itself:RoomList::entries_with_dynamic_filter#2392normalized_match_room_namefilter #2428nonefilter #2594The unnecessary
RoomListEntrySo 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 thisRoomListEntryspreads everywhere in thematrix_sdk::sliding_syncandmatrix_sdk_ui::room_list_serviceAPIs. It means the users receiveRoomListEntryin theirStreamof updates and so on. It implies that the users manipulate aRoomIdonly, and thus must call a method such asRoomListService::roomto 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_roomsseems broken and will break for sureRoomListServiceuses sliding sync with 2 lists:all_roomsandvisible_rooms.all_roomsto fetch all rooms withtimeline_limit=1, andvisible_roomsto fetch a particular range of rooms withtimeline_limit=20. In some cases, for some users,visible_roomsfeels 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_roomsby roomsubscriptions(see Room Subscription API in MSC3575).Once we have client-side sorting,
visible_roomswill be entirely broken because a server-side range won't map to a client-side range anymore, making the use ofvisible_roomsimpossible.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:
/syncendpoint (initial implementation) element-hq/synapse#17187is_dmfiltering to Sliding Sync/syncelement-hq/synapse#17277is_encryptedfiltering to Sliding Sync/syncelement-hq/synapse#17281stream_orderingsort to Sliding Sync/syncelement-hq/synapse#17293is_invitefiltering to Sliding Sync/syncelement-hq/synapse#17335/syncelement-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_serviceto provide an API on tops of the rooms inside the SDK.To be here today, we needed to tackle these tasks:
SlidingSyncRoomtech debt #3079 (with a lot of sub tasks…)room_list_service::Room::latest_eventno longer usesSlidingSyncRoom(+ dropSlidingSyncRoomExt) #3540Store::get_roomswayyy faster #3552Then, the first step was
Clientto export aStream<Item = Vec<VectorDiff<Room>>>. This was done with:Client::rooms_stream#3068After that, we needed
room_list_service::Roomto be infallible and non-async. This was done with:RoomListService::roomis no longer async! #3551room_list_service::Room::newis now infallible #3586Finally, we needed
eyeballto provide aSortstream adapter to be able to sort aStream(yup!):SortBystream adapter jplatte/eyeball#43And now, … only now, we have everything to implement client-side sorting dear readers!
Tasks
RoomListto manipulateRoominstead ofRoomListEntryRoominstead ofRoomListEntryvisible_roomsRoomList