-
Notifications
You must be signed in to change notification settings - Fork 429
Pass leave from remote invite rejection down Sliding Sync #18375
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
Changes from all commits
340ae9b
74cd6d5
17cdd4f
5ee188a
de7217f
9e41abd
af7a4af
fd4a633
e0aad4b
1519d4f
7ed2a12
223c87e
b8d1b5e
7640c59
9138ee0
241ec9a
79fc888
3e353b6
a7bc0ca
93b00c3
720f85e
06b9a34
db486c7
06209fc
c9a6a0b
096bbfa
ed7c580
14c4e4d
2c3f74f
1f74e91
5ad092e
b05f339
e876851
ea3c9fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Pass leave from remote invite rejection down Sliding Sync. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -80,6 +80,7 @@ | |||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| from synapse.storage.databases.main.events_worker import EventsWorkerStore | ||||||||||||||||||||||||||||||||||||||
| from synapse.storage.engines import BaseDatabaseEngine, PostgresEngine, Sqlite3Engine | ||||||||||||||||||||||||||||||||||||||
| from synapse.storage.roommember import RoomsForUserStateReset | ||||||||||||||||||||||||||||||||||||||
| from synapse.storage.util.id_generators import MultiWriterIdGenerator | ||||||||||||||||||||||||||||||||||||||
| from synapse.types import PersistedEventPosition, RoomStreamToken, StrCollection | ||||||||||||||||||||||||||||||||||||||
| from synapse.util.caches.descriptors import cached, cachedList | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -993,6 +994,10 @@ async def get_current_state_delta_membership_changes_for_user( | |||||||||||||||||||||||||||||||||||||
| available in the `current_state_delta_stream` table. To actually check for a | ||||||||||||||||||||||||||||||||||||||
| state reset, you need to check if a membership still exists in the room. | ||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| assert from_key.topological is None | ||||||||||||||||||||||||||||||||||||||
| assert to_key.topological is None | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Start by ruling out cases where a DB query is not necessary. | ||||||||||||||||||||||||||||||||||||||
| if from_key == to_key: | ||||||||||||||||||||||||||||||||||||||
| return [] | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -1138,6 +1143,203 @@ def f(txn: LoggingTransaction) -> List[CurrentStateDeltaMembership]: | |||||||||||||||||||||||||||||||||||||
| if membership_change.room_id not in room_ids_to_exclude | ||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @trace | ||||||||||||||||||||||||||||||||||||||
| async def get_sliding_sync_membership_changes( | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this uses the It looks like we're using it in the fallback case as well right now:
As one solution, per the comment in the following code, we could also just make this foreground update now as those tables originally shipped with Synapse 1.115.0rc1 (2024-09-10) (see #17512) and remove the fallback path. synapse/synapse/handlers/sliding_sync/room_lists.py Lines 206 to 223 in d59bbd8
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, after looking into it a bit, that seems like a large undertaking that would block this fix unnecessarily. I think it may be better to split the impl & keep the old logic for the fallback case.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed it to do just that for now. If we aren't happy with this and want to wait until we have completed the DB transition for sliding sync then that PR will need to be created & merged before we can merge this one.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable to me. We may want to consider refactoring We do still have a good set of end-to-end rest layer tests to cover us in a lot of scenarios.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with the idea. I started down that path but it looks like it'll be quite involved to do properly.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not entirely comfortable merging these changes until those tests have been converted so we can ensure everything is still working as intended.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. So after converting that test, 12 of the tests fail when using the new tables (they pass when using the fallback tables). But those same 12 tests also fail against the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tests have now been updated & #18399 has been merged. Let's see if these changes are still happy.
MadLittleMods marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||
| user_id: str, | ||||||||||||||||||||||||||||||||||||||
| from_key: RoomStreamToken, | ||||||||||||||||||||||||||||||||||||||
| to_key: RoomStreamToken, | ||||||||||||||||||||||||||||||||||||||
| excluded_room_ids: Optional[AbstractSet[str]] = None, | ||||||||||||||||||||||||||||||||||||||
| ) -> Dict[str, RoomsForUserStateReset]: | ||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||
| Fetch membership events that result in a meaningful membership change for a | ||||||||||||||||||||||||||||||||||||||
| given user. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| A meaningful membership changes is one where the `membership` value actually | ||||||||||||||||||||||||||||||||||||||
| changes. This means memberships changes from `join` to `join` (like a display | ||||||||||||||||||||||||||||||||||||||
| name change) will be filtered out since they result in no meaningful change. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Note: This function only works with "live" tokens with `stream_ordering` only. | ||||||||||||||||||||||||||||||||||||||
devonh marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| We're looking for membership changes in the token range (> `from_key` and <= | ||||||||||||||||||||||||||||||||||||||
| `to_key`). | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||
| user_id: The user ID to fetch membership events for. | ||||||||||||||||||||||||||||||||||||||
| from_key: The point in the stream to sync from (fetching events > this point). | ||||||||||||||||||||||||||||||||||||||
| to_key: The token to fetch rooms up to (fetching events <= this point). | ||||||||||||||||||||||||||||||||||||||
| excluded_room_ids: Optional list of room IDs to exclude from the results. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||
| All meaningful membership changes to the current state in the token range. | ||||||||||||||||||||||||||||||||||||||
| Events are sorted by `stream_ordering` ascending. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| `event_id`/`sender` can be `None` when the server leaves a room (meaning | ||||||||||||||||||||||||||||||||||||||
| everyone locally left) or a state reset which removed the person from the | ||||||||||||||||||||||||||||||||||||||
| room. We can't tell the difference between the two cases with what's | ||||||||||||||||||||||||||||||||||||||
| available in the `current_state_delta_stream` table. To actually check for a | ||||||||||||||||||||||||||||||||||||||
| state reset, you need to check if a membership still exists in the room. | ||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| assert from_key.topological is None | ||||||||||||||||||||||||||||||||||||||
| assert to_key.topological is None | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Start by ruling out cases where a DB query is not necessary. | ||||||||||||||||||||||||||||||||||||||
| if from_key == to_key: | ||||||||||||||||||||||||||||||||||||||
| return {} | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if from_key: | ||||||||||||||||||||||||||||||||||||||
| has_changed = self._membership_stream_cache.has_entity_changed( | ||||||||||||||||||||||||||||||||||||||
| user_id, int(from_key.stream) | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| if not has_changed: | ||||||||||||||||||||||||||||||||||||||
| return {} | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| room_ids_to_exclude: AbstractSet[str] = set() | ||||||||||||||||||||||||||||||||||||||
| if excluded_room_ids is not None: | ||||||||||||||||||||||||||||||||||||||
| room_ids_to_exclude = excluded_room_ids | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| def f(txn: LoggingTransaction) -> Dict[str, RoomsForUserStateReset]: | ||||||||||||||||||||||||||||||||||||||
| # To handle tokens with a non-empty instance_map we fetch more | ||||||||||||||||||||||||||||||||||||||
| # results than necessary and then filter down | ||||||||||||||||||||||||||||||||||||||
| min_from_id = from_key.stream | ||||||||||||||||||||||||||||||||||||||
| max_to_id = to_key.get_max_stream_pos() | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # This query looks at membership changes in | ||||||||||||||||||||||||||||||||||||||
| # `sliding_sync_membership_snapshots` which will not include users | ||||||||||||||||||||||||||||||||||||||
| # that were state reset out of rooms; so we need to look for that | ||||||||||||||||||||||||||||||||||||||
| # case in `current_state_delta_stream`. | ||||||||||||||||||||||||||||||||||||||
| sql = """ | ||||||||||||||||||||||||||||||||||||||
| SELECT | ||||||||||||||||||||||||||||||||||||||
| room_id, | ||||||||||||||||||||||||||||||||||||||
| membership_event_id, | ||||||||||||||||||||||||||||||||||||||
| event_instance_name, | ||||||||||||||||||||||||||||||||||||||
| event_stream_ordering, | ||||||||||||||||||||||||||||||||||||||
| membership, | ||||||||||||||||||||||||||||||||||||||
| sender, | ||||||||||||||||||||||||||||||||||||||
| prev_membership, | ||||||||||||||||||||||||||||||||||||||
| room_version | ||||||||||||||||||||||||||||||||||||||
| FROM | ||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||
| SELECT | ||||||||||||||||||||||||||||||||||||||
| s.room_id, | ||||||||||||||||||||||||||||||||||||||
| s.membership_event_id, | ||||||||||||||||||||||||||||||||||||||
| s.event_instance_name, | ||||||||||||||||||||||||||||||||||||||
| s.event_stream_ordering, | ||||||||||||||||||||||||||||||||||||||
| s.membership, | ||||||||||||||||||||||||||||||||||||||
| s.sender, | ||||||||||||||||||||||||||||||||||||||
| m_prev.membership AS prev_membership | ||||||||||||||||||||||||||||||||||||||
| FROM sliding_sync_membership_snapshots as s | ||||||||||||||||||||||||||||||||||||||
| LEFT JOIN event_edges AS e ON e.event_id = s.membership_event_id | ||||||||||||||||||||||||||||||||||||||
| LEFT JOIN room_memberships AS m_prev ON m_prev.event_id = e.prev_event_id | ||||||||||||||||||||||||||||||||||||||
| WHERE s.user_id = ? | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| UNION ALL | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| SELECT | ||||||||||||||||||||||||||||||||||||||
| s.room_id, | ||||||||||||||||||||||||||||||||||||||
| e.event_id, | ||||||||||||||||||||||||||||||||||||||
| s.instance_name, | ||||||||||||||||||||||||||||||||||||||
| s.stream_id, | ||||||||||||||||||||||||||||||||||||||
| m.membership, | ||||||||||||||||||||||||||||||||||||||
| e.sender, | ||||||||||||||||||||||||||||||||||||||
| m_prev.membership AS prev_membership | ||||||||||||||||||||||||||||||||||||||
| FROM current_state_delta_stream AS s | ||||||||||||||||||||||||||||||||||||||
| LEFT JOIN events AS e ON e.event_id = s.event_id | ||||||||||||||||||||||||||||||||||||||
| LEFT JOIN room_memberships AS m ON m.event_id = s.event_id | ||||||||||||||||||||||||||||||||||||||
| LEFT JOIN room_memberships AS m_prev ON m_prev.event_id = s.prev_event_id | ||||||||||||||||||||||||||||||||||||||
| WHERE | ||||||||||||||||||||||||||||||||||||||
| s.type = ? | ||||||||||||||||||||||||||||||||||||||
| AND s.state_key = ? | ||||||||||||||||||||||||||||||||||||||
| ) AS c | ||||||||||||||||||||||||||||||||||||||
| INNER JOIN rooms USING (room_id) | ||||||||||||||||||||||||||||||||||||||
| WHERE event_stream_ordering > ? AND event_stream_ordering <= ? | ||||||||||||||||||||||||||||||||||||||
| ORDER BY event_stream_ordering ASC | ||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||
devonh marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| txn.execute( | ||||||||||||||||||||||||||||||||||||||
| sql, | ||||||||||||||||||||||||||||||||||||||
| (user_id, EventTypes.Member, user_id, min_from_id, max_to_id), | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| membership_changes: Dict[str, RoomsForUserStateReset] = {} | ||||||||||||||||||||||||||||||||||||||
| for ( | ||||||||||||||||||||||||||||||||||||||
| room_id, | ||||||||||||||||||||||||||||||||||||||
| membership_event_id, | ||||||||||||||||||||||||||||||||||||||
| event_instance_name, | ||||||||||||||||||||||||||||||||||||||
| event_stream_ordering, | ||||||||||||||||||||||||||||||||||||||
| membership, | ||||||||||||||||||||||||||||||||||||||
| sender, | ||||||||||||||||||||||||||||||||||||||
| prev_membership, | ||||||||||||||||||||||||||||||||||||||
| room_version_id, | ||||||||||||||||||||||||||||||||||||||
| ) in txn: | ||||||||||||||||||||||||||||||||||||||
| assert room_id is not None | ||||||||||||||||||||||||||||||||||||||
| assert event_stream_ordering is not None | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if room_id in room_ids_to_exclude: | ||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if _filter_results_by_stream( | ||||||||||||||||||||||||||||||||||||||
| from_key, | ||||||||||||||||||||||||||||||||||||||
| to_key, | ||||||||||||||||||||||||||||||||||||||
| event_instance_name, | ||||||||||||||||||||||||||||||||||||||
| event_stream_ordering, | ||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||
| # When the server leaves a room, it will insert new rows into the | ||||||||||||||||||||||||||||||||||||||
| # `current_state_delta_stream` table with `event_id = null` for all | ||||||||||||||||||||||||||||||||||||||
| # current state. This means we might already have a row for the | ||||||||||||||||||||||||||||||||||||||
| # leave event and then another for the same leave where the | ||||||||||||||||||||||||||||||||||||||
| # `event_id=null` but the `prev_event_id` is pointing back at the | ||||||||||||||||||||||||||||||||||||||
| # earlier leave event. We don't want to report the leave, if we | ||||||||||||||||||||||||||||||||||||||
| # already have a leave event. | ||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||
| membership_event_id is None | ||||||||||||||||||||||||||||||||||||||
| and prev_membership == Membership.LEAVE | ||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if membership_event_id is None and room_id in membership_changes: | ||||||||||||||||||||||||||||||||||||||
| # SUSPICIOUS: if we join a room and get state reset out of it | ||||||||||||||||||||||||||||||||||||||
| # in the same queried window, | ||||||||||||||||||||||||||||||||||||||
| # won't this ignore the 'state reset out of it' part? | ||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # When `s.event_id = null`, we won't be able to get respective | ||||||||||||||||||||||||||||||||||||||
| # `room_membership` but can assume the user has left the room | ||||||||||||||||||||||||||||||||||||||
| # because this only happens when the server leaves a room | ||||||||||||||||||||||||||||||||||||||
| # (meaning everyone locally left) or a state reset which removed | ||||||||||||||||||||||||||||||||||||||
| # the person from the room. | ||||||||||||||||||||||||||||||||||||||
| membership = ( | ||||||||||||||||||||||||||||||||||||||
| membership if membership is not None else Membership.LEAVE | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if membership == prev_membership: | ||||||||||||||||||||||||||||||||||||||
| # If `membership` and `prev_membership` are the same then this | ||||||||||||||||||||||||||||||||||||||
| # is not a meaningful change so we can skip it. | ||||||||||||||||||||||||||||||||||||||
| # An example of this happening is when the user changes their display name. | ||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| membership_change = RoomsForUserStateReset( | ||||||||||||||||||||||||||||||||||||||
| room_id=room_id, | ||||||||||||||||||||||||||||||||||||||
| sender=sender, | ||||||||||||||||||||||||||||||||||||||
| membership=membership, | ||||||||||||||||||||||||||||||||||||||
| event_id=membership_event_id, | ||||||||||||||||||||||||||||||||||||||
| event_pos=PersistedEventPosition( | ||||||||||||||||||||||||||||||||||||||
| event_instance_name, event_stream_ordering | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| room_version_id=room_version_id, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| membership_changes[room_id] = membership_change | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return membership_changes | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| membership_changes = await self.db_pool.runInteraction( | ||||||||||||||||||||||||||||||||||||||
| "get_sliding_sync_membership_changes", f | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return membership_changes | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @cancellable | ||||||||||||||||||||||||||||||||||||||
| async def get_membership_changes_for_user( | ||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| -- | ||
| -- This file is licensed under the Affero General Public License (AGPL) version 3. | ||
| -- | ||
| -- Copyright (C) 2025 New Vector, Ltd | ||
| -- | ||
| -- This program is free software: you can redistribute it and/or modify | ||
| -- it under the terms of the GNU Affero General Public License as | ||
| -- published by the Free Software Foundation, either version 3 of the | ||
| -- License, or (at your option) any later version. | ||
| -- | ||
| -- See the GNU Affero General Public License for more details: | ||
| -- <https://www.gnu.org/licenses/agpl-3.0.html>. | ||
|
|
||
| -- So we can fetch all rooms for a given user sorted by stream order | ||
| DROP INDEX IF EXISTS sliding_sync_membership_snapshots_user_id; | ||
| CREATE INDEX IF NOT EXISTS sliding_sync_membership_snapshots_user_id ON sliding_sync_membership_snapshots(user_id, event_stream_ordering); |
Uh oh!
There was an error while loading. Please reload this page.