-
Notifications
You must be signed in to change notification settings - Fork 564
Bust _membership_stream_cache cache when current state changes
#17732
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
2bd0e63
d327c62
64eb71f
dd87620
7c53716
bceb4e0
944d1e0
20ff239
f40e649
f5f0e36
7e328d7
ac06ddf
e52f5f0
bf695e6
2d20b71
484d045
6d29960
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 @@ | ||
| Fix membership caches not updating in state reset scenarios. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,11 @@ def process_replication_rows( | |
| room_id = row.keys[0] | ||
| members_changed = set(row.keys[1:]) | ||
| self._invalidate_state_caches(room_id, members_changed) | ||
| self._curr_state_delta_stream_cache.entity_has_changed( # type: ignore[attr-defined] | ||
| room_id, token | ||
| ) | ||
| for user_id in members_changed: | ||
| self._membership_stream_cache.entity_has_changed(user_id, token) # type: ignore[attr-defined] | ||
|
Contributor
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. Kinda weird to just stick this here (same with the others in
MadLittleMods marked this conversation as resolved.
Comment on lines
+222
to
+226
Contributor
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. Wherever we are busting We've forgotten to bust |
||
| elif row.cache_func == PURGE_HISTORY_CACHE_NAME: | ||
| if row.keys is None: | ||
| raise Exception( | ||
|
|
@@ -236,6 +241,35 @@ def process_replication_rows( | |
| room_id = row.keys[0] | ||
| self._invalidate_caches_for_room_events(room_id) | ||
| self._invalidate_caches_for_room(room_id) | ||
|
Comment on lines
242
to
243
Contributor
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. Separate from this PR and in line with the fact that these code paths need to be cleaned up, there is an obvious duplication here because we call |
||
| self._curr_state_delta_stream_cache.entity_has_changed( # type: ignore[attr-defined] | ||
| room_id, token | ||
| ) | ||
| # Note: This code is commented out to improve cache performance. | ||
| # While uncommenting would provide complete correctness, our | ||
| # automatic forgotten room purge logic (see | ||
| # `forgotten_room_retention_period`) means this would frequently | ||
| # clear the entire cache (effectively) and probably have a noticable | ||
| # impact on the cache hit ratio. | ||
| # | ||
| # Not updating the cache here is safe because: | ||
| # | ||
| # 1. `_membership_stream_cache` is only used to indicate the | ||
| # *absence* of changes, i.e. "nothing has changed between tokens | ||
| # X and Y and so return early and don't query the database". | ||
| # 2. `_membership_stream_cache` is used when we query data from | ||
| # `current_state_delta_stream` and `room_memberships` but since | ||
| # nothing new is written to the database for those tables when | ||
| # purging/deleting a room (only deleting rows), there is nothing | ||
| # changed to care about. | ||
| # | ||
| # At worst, the cache might indicate a change at token X, at which | ||
| # point, we will query the database and discover nothing is there. | ||
| # | ||
| # Ideally, we would make it so that we could clear the cache on a | ||
| # more granular level but that's a bit complex and fiddly to do with | ||
| # room membership. | ||
| # | ||
| # self._membership_stream_cache.all_entities_changed(token) # type: ignore[attr-defined] | ||
| else: | ||
| self._attempt_to_invalidate_cache(row.cache_func, row.keys) | ||
|
|
||
|
|
@@ -275,6 +309,7 @@ def _process_event_stream_row(self, token: int, row: EventsStreamRow) -> None: | |
| self._attempt_to_invalidate_cache( | ||
| "get_sliding_sync_rooms_for_user", None | ||
| ) | ||
| self._membership_stream_cache.entity_has_changed(data.state_key, token) # type: ignore[attr-defined] | ||
| elif data.type == EventTypes.RoomEncryption: | ||
| self._attempt_to_invalidate_cache( | ||
| "get_room_encryption", (data.room_id,) | ||
|
|
@@ -291,6 +326,7 @@ def _process_event_stream_row(self, token: int, row: EventsStreamRow) -> None: | |
| # Similar to the above, but the entire caches are invalidated. This is | ||
| # unfortunate for the membership caches, but should recover quickly. | ||
| self._curr_state_delta_stream_cache.entity_has_changed(data.room_id, token) # type: ignore[attr-defined] | ||
| self._membership_stream_cache.all_entities_changed(token) # type: ignore[attr-defined] | ||
| self._attempt_to_invalidate_cache("get_rooms_for_user", None) | ||
| self._attempt_to_invalidate_cache("get_room_type", (data.room_id,)) | ||
| self._attempt_to_invalidate_cache("get_room_encryption", (data.room_id,)) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1605,7 +1605,13 @@ def _update_current_state_txn( | |
| room_id | ||
| delta_state: Deltas that are going to be used to update the | ||
| `current_state_events` table. Changes to the current state of the room. | ||
| stream_id: TODO | ||
| stream_id: This is expected to be the minimum `stream_ordering` for the | ||
| batch of events that we are persisting; which means we do not end up in a | ||
| situation where workers see events before the `current_state_delta` updates. | ||
| FIXME: However, this function also gets called with next upcoming | ||
| `stream_ordering` when we re-sync the state of a partial stated room (see | ||
| `update_current_state(...)`) which may be "correct" but it would be good to | ||
| nail down what exactly is the expected value here. | ||
|
Comment on lines
+1608
to
+1614
Contributor
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. Previous conversation: #17512 (comment) I decided to define it in some way given we're using it for cache busting below and was curious if it is actually correct. Still not confident whether it's perfect for cache busting but might be good enough. |
||
| sliding_sync_table_changes: Changes to the | ||
| `sliding_sync_membership_snapshots` and `sliding_sync_joined_rooms` tables | ||
| derived from the given `delta_state` (see | ||
|
|
@@ -1908,6 +1914,13 @@ def _update_current_state_txn( | |
| stream_id, | ||
| ) | ||
|
|
||
| for user_id in members_to_cache_bust: | ||
| txn.call_after( | ||
| self.store._membership_stream_cache.entity_has_changed, | ||
| user_id, | ||
| stream_id, | ||
| ) | ||
|
Comment on lines
+1917
to
+1922
Contributor
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. This matches what we do for
Comment on lines
+1917
to
+1922
Contributor
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. This is the actual call that busts the the membership cache for the tests. I assume that is because this is what busts in monolith mode vs the other calls I've added are more for workers over replication |
||
|
|
||
| # Invalidate the various caches | ||
| self.store._invalidate_state_caches_and_stream( | ||
| txn, room_id, members_to_cache_bust | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.