Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit c485ed1

Browse files
erikjohnstonclokep
andauthored
Clear event caches when we purge history (#15609)
This should help a little with #13476 --------- Co-authored-by: Patrick Cloke <[email protected]>
1 parent d162aec commit c485ed1

File tree

9 files changed

+184
-14
lines changed

9 files changed

+184
-14
lines changed

changelog.d/15609.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correctly clear caches when we delete a room.

synapse/storage/_base.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,14 @@ def _invalidate_state_caches(
8686
room_id: Room where state changed
8787
members_changed: The user_ids of members that have changed
8888
"""
89+
90+
# XXX: If you add something to this function make sure you add it to
91+
# `_invalidate_state_caches_all` as well.
92+
8993
# If there were any membership changes, purge the appropriate caches.
9094
for host in {get_domain_from_id(u) for u in members_changed}:
9195
self._attempt_to_invalidate_cache("is_host_joined", (room_id, host))
96+
self._attempt_to_invalidate_cache("is_host_invited", (room_id, host))
9297
if members_changed:
9398
self._attempt_to_invalidate_cache("get_users_in_room", (room_id,))
9499
self._attempt_to_invalidate_cache("get_current_hosts_in_room", (room_id,))
@@ -117,6 +122,32 @@ def _invalidate_state_caches(
117122
self._attempt_to_invalidate_cache("get_room_summary", (room_id,))
118123
self._attempt_to_invalidate_cache("get_partial_current_state_ids", (room_id,))
119124

125+
def _invalidate_state_caches_all(self, room_id: str) -> None:
126+
"""Invalidates caches that are based on the current state, but does
127+
not stream invalidations down replication.
128+
129+
Same as `_invalidate_state_caches`, except that works when we don't know
130+
which memberships have changed.
131+
132+
Args:
133+
room_id: Room where state changed
134+
"""
135+
self._attempt_to_invalidate_cache("get_partial_current_state_ids", (room_id,))
136+
self._attempt_to_invalidate_cache("get_users_in_room", (room_id,))
137+
self._attempt_to_invalidate_cache("is_host_invited", None)
138+
self._attempt_to_invalidate_cache("is_host_joined", None)
139+
self._attempt_to_invalidate_cache("get_current_hosts_in_room", (room_id,))
140+
self._attempt_to_invalidate_cache("get_users_in_room_with_profiles", (room_id,))
141+
self._attempt_to_invalidate_cache("get_number_joined_users_in_room", (room_id,))
142+
self._attempt_to_invalidate_cache("get_local_users_in_room", (room_id,))
143+
self._attempt_to_invalidate_cache("does_pair_of_users_share_a_room", None)
144+
self._attempt_to_invalidate_cache("get_user_in_room_with_profile", None)
145+
self._attempt_to_invalidate_cache(
146+
"get_rooms_for_user_with_stream_ordering", None
147+
)
148+
self._attempt_to_invalidate_cache("get_rooms_for_user", None)
149+
self._attempt_to_invalidate_cache("get_room_summary", (room_id,))
150+
120151
def _attempt_to_invalidate_cache(
121152
self, cache_name: str, key: Optional[Collection[Any]]
122153
) -> bool:

synapse/storage/databases/main/cache.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@
4646
# based on the current state when notifying workers over replication.
4747
CURRENT_STATE_CACHE_NAME = "cs_cache_fake"
4848

49+
# As above, but for invalidating event caches on history deletion
50+
PURGE_HISTORY_CACHE_NAME = "ph_cache_fake"
51+
52+
# As above, but for invalidating room caches on room deletion
53+
DELETE_ROOM_CACHE_NAME = "dr_cache_fake"
54+
4955

5056
class CacheInvalidationWorkerStore(SQLBaseStore):
5157
def __init__(
@@ -175,6 +181,23 @@ def process_replication_rows(
175181
room_id = row.keys[0]
176182
members_changed = set(row.keys[1:])
177183
self._invalidate_state_caches(room_id, members_changed)
184+
elif row.cache_func == PURGE_HISTORY_CACHE_NAME:
185+
if row.keys is None:
186+
raise Exception(
187+
"Can't send an 'invalidate all' for 'purge history' cache"
188+
)
189+
190+
room_id = row.keys[0]
191+
self._invalidate_caches_for_room_events(room_id)
192+
elif row.cache_func == DELETE_ROOM_CACHE_NAME:
193+
if row.keys is None:
194+
raise Exception(
195+
"Can't send an 'invalidate all' for 'delete room' cache"
196+
)
197+
198+
room_id = row.keys[0]
199+
self._invalidate_caches_for_room_events(room_id)
200+
self._invalidate_caches_for_room(room_id)
178201
else:
179202
self._attempt_to_invalidate_cache(row.cache_func, row.keys)
180203

@@ -226,6 +249,9 @@ def _invalidate_caches_for_event(
226249
relates_to: Optional[str],
227250
backfilled: bool,
228251
) -> None:
252+
# XXX: If you add something to this function make sure you add it to
253+
# `_invalidate_caches_for_room_events` as well.
254+
229255
# This invalidates any local in-memory cached event objects, the original
230256
# process triggering the invalidation is responsible for clearing any external
231257
# cached objects.
@@ -271,6 +297,106 @@ def _invalidate_caches_for_event(
271297
self._attempt_to_invalidate_cache("get_thread_participated", (relates_to,))
272298
self._attempt_to_invalidate_cache("get_threads", (room_id,))
273299

300+
def _invalidate_caches_for_room_events_and_stream(
301+
self, txn: LoggingTransaction, room_id: str
302+
) -> None:
303+
"""Invalidate caches associated with events in a room, and stream to
304+
replication.
305+
306+
Used when we delete events a room, but don't know which events we've
307+
deleted.
308+
"""
309+
310+
self._send_invalidation_to_replication(txn, PURGE_HISTORY_CACHE_NAME, [room_id])
311+
txn.call_after(self._invalidate_caches_for_room_events, room_id)
312+
313+
def _invalidate_caches_for_room_events(self, room_id: str) -> None:
314+
"""Invalidate caches associated with events in a room, and stream to
315+
replication.
316+
317+
Used when we delete events in a room, but don't know which events we've
318+
deleted.
319+
"""
320+
321+
self._invalidate_local_get_event_cache_all() # type: ignore[attr-defined]
322+
323+
self._attempt_to_invalidate_cache("have_seen_event", (room_id,))
324+
self._attempt_to_invalidate_cache("get_latest_event_ids_in_room", (room_id,))
325+
self._attempt_to_invalidate_cache(
326+
"get_unread_event_push_actions_by_room_for_user", (room_id,)
327+
)
328+
329+
self._attempt_to_invalidate_cache("_get_membership_from_event_id", None)
330+
self._attempt_to_invalidate_cache("get_relations_for_event", None)
331+
self._attempt_to_invalidate_cache("get_applicable_edit", None)
332+
self._attempt_to_invalidate_cache("get_thread_id", None)
333+
self._attempt_to_invalidate_cache("get_thread_id_for_receipts", None)
334+
self._attempt_to_invalidate_cache("get_invited_rooms_for_local_user", None)
335+
self._attempt_to_invalidate_cache(
336+
"get_rooms_for_user_with_stream_ordering", None
337+
)
338+
self._attempt_to_invalidate_cache("get_rooms_for_user", None)
339+
self._attempt_to_invalidate_cache("get_references_for_event", None)
340+
self._attempt_to_invalidate_cache("get_thread_summary", None)
341+
self._attempt_to_invalidate_cache("get_thread_participated", None)
342+
self._attempt_to_invalidate_cache("get_threads", (room_id,))
343+
344+
self._attempt_to_invalidate_cache("_get_state_group_for_event", None)
345+
346+
self._attempt_to_invalidate_cache("get_event_ordering", None)
347+
self._attempt_to_invalidate_cache("is_partial_state_event", None)
348+
self._attempt_to_invalidate_cache("_get_joined_profile_from_event_id", None)
349+
350+
def _invalidate_caches_for_room_and_stream(
351+
self, txn: LoggingTransaction, room_id: str
352+
) -> None:
353+
"""Invalidate caches associated with rooms, and stream to replication.
354+
355+
Used when we delete rooms.
356+
"""
357+
358+
self._send_invalidation_to_replication(txn, DELETE_ROOM_CACHE_NAME, [room_id])
359+
txn.call_after(self._invalidate_caches_for_room, room_id)
360+
361+
def _invalidate_caches_for_room(self, room_id: str) -> None:
362+
"""Invalidate caches associated with rooms.
363+
364+
Used when we delete rooms.
365+
"""
366+
367+
# If we've deleted the room then we also need to purge all event caches.
368+
self._invalidate_caches_for_room_events(room_id)
369+
370+
self._attempt_to_invalidate_cache("get_account_data_for_room", None)
371+
self._attempt_to_invalidate_cache("get_account_data_for_room_and_type", None)
372+
self._attempt_to_invalidate_cache("get_aliases_for_room", (room_id,))
373+
self._attempt_to_invalidate_cache("get_latest_event_ids_in_room", (room_id,))
374+
self._attempt_to_invalidate_cache("_get_forward_extremeties_for_room", None)
375+
self._attempt_to_invalidate_cache(
376+
"get_unread_event_push_actions_by_room_for_user", (room_id,)
377+
)
378+
self._attempt_to_invalidate_cache(
379+
"_get_linearized_receipts_for_room", (room_id,)
380+
)
381+
self._attempt_to_invalidate_cache("is_room_blocked", (room_id,))
382+
self._attempt_to_invalidate_cache("get_retention_policy_for_room", (room_id,))
383+
self._attempt_to_invalidate_cache(
384+
"_get_partial_state_servers_at_join", (room_id,)
385+
)
386+
self._attempt_to_invalidate_cache("is_partial_state_room", (room_id,))
387+
self._attempt_to_invalidate_cache("get_invited_rooms_for_local_user", None)
388+
self._attempt_to_invalidate_cache(
389+
"get_current_hosts_in_room_ordered", (room_id,)
390+
)
391+
self._attempt_to_invalidate_cache("did_forget", None)
392+
self._attempt_to_invalidate_cache("get_forgotten_rooms_for_user", None)
393+
self._attempt_to_invalidate_cache("_get_membership_from_event_id", None)
394+
self._attempt_to_invalidate_cache("get_room_version_id", (room_id,))
395+
396+
# And delete state caches.
397+
398+
self._invalidate_state_caches_all(room_id)
399+
274400
async def invalidate_cache_and_stream(
275401
self, cache_name: str, keys: Tuple[Any, ...]
276402
) -> None:
@@ -377,6 +503,14 @@ def _send_invalidation_to_replication(
377503
"Can't stream invalidate all with magic current state cache"
378504
)
379505

506+
if cache_name == PURGE_HISTORY_CACHE_NAME and keys is None:
507+
raise Exception(
508+
"Can't stream invalidate all with magic purge history cache"
509+
)
510+
511+
if cache_name == DELETE_ROOM_CACHE_NAME and keys is None:
512+
raise Exception("Can't stream invalidate all with magic delete room cache")
513+
380514
if isinstance(self.database_engine, PostgresEngine):
381515
assert self._cache_id_gen is not None
382516

synapse/storage/databases/main/events_worker.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,15 @@ def _invalidate_local_get_event_cache(self, event_id: str) -> None:
903903
self._event_ref.pop(event_id, None)
904904
self._current_event_fetches.pop(event_id, None)
905905

906+
def _invalidate_local_get_event_cache_all(self) -> None:
907+
"""Clears the in-memory get event caches.
908+
909+
Used when we purge room history.
910+
"""
911+
self._get_event_cache.clear()
912+
self._event_ref.clear()
913+
self._current_event_fetches.clear()
914+
906915
async def _get_events_from_cache(
907916
self, events: Iterable[str], update_metrics: bool = True
908917
) -> Dict[str, EventCacheEntry]:

synapse/storage/databases/main/purge_events.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ def _purge_history_txn(
308308

309309
logger.info("[purge] done")
310310

311+
self._invalidate_caches_for_room_events_and_stream(txn, room_id)
312+
311313
return referenced_state_groups
312314

313315
async def purge_room(self, room_id: str) -> List[int]:
@@ -485,10 +487,6 @@ def _purge_room_txn(self, txn: LoggingTransaction, room_id: str) -> List[int]:
485487
# index on them. In any case we should be clearing out 'stream' tables
486488
# periodically anyway (#5888)
487489

488-
# TODO: we could probably usefully do a bunch more cache invalidation here
489-
490-
# XXX: as with purge_history, this is racy, but no worse than other races
491-
# that already exist.
492-
self._invalidate_cache_and_stream(txn, self.have_seen_event, (room_id,))
490+
self._invalidate_caches_for_room_and_stream(txn, room_id)
493491

494492
return state_groups

synapse/util/caches/lrucache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,5 +862,5 @@ def invalidate_local(self, key: KT) -> None:
862862
async def contains(self, key: KT) -> bool:
863863
return self._lru_cache.contains(key)
864864

865-
async def clear(self) -> None:
865+
def clear(self) -> None:
866866
self._lru_cache.clear()

tests/handlers/test_sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def test_unknown_room_version(self) -> None:
163163
# Blow away caches (supported room versions can only change due to a restart).
164164
self.store.get_rooms_for_user_with_stream_ordering.invalidate_all()
165165
self.store.get_rooms_for_user.invalidate_all()
166-
self.get_success(self.store._get_event_cache.clear())
166+
self.store._get_event_cache.clear()
167167
self.store._event_ref.clear()
168168

169169
# The rooms should be excluded from the sync response.

tests/rest/client/test_read_marker.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,6 @@ def send_message() -> str:
131131
event = self.get_success(self.store.get_event(event_id_1, allow_none=True))
132132
assert event is None
133133

134-
# TODO See https://github.com/matrix-org/synapse/issues/13476
135-
self.store.get_event_ordering.invalidate_all()
136-
137134
# Test moving the read marker to a newer event
138135
event_id_2 = send_message()
139136
channel = self.make_request(

tests/storage/databases/main/test_events_worker.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
188188
self.event_id = res["event_id"]
189189

190190
# Reset the event cache so the tests start with it empty
191-
self.get_success(self.store._get_event_cache.clear())
191+
self.store._get_event_cache.clear()
192192

193193
def test_simple(self) -> None:
194194
"""Test that we cache events that we pull from the DB."""
@@ -205,7 +205,7 @@ def test_event_ref(self) -> None:
205205
"""
206206

207207
# Reset the event cache
208-
self.get_success(self.store._get_event_cache.clear())
208+
self.store._get_event_cache.clear()
209209

210210
with LoggingContext("test") as ctx:
211211
# We keep hold of the event event though we never use it.
@@ -215,7 +215,7 @@ def test_event_ref(self) -> None:
215215
self.assertEqual(ctx.get_resource_usage().evt_db_fetch_count, 1)
216216

217217
# Reset the event cache
218-
self.get_success(self.store._get_event_cache.clear())
218+
self.store._get_event_cache.clear()
219219

220220
with LoggingContext("test") as ctx:
221221
self.get_success(self.store.get_event(self.event_id))
@@ -390,7 +390,7 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
390390
self.event_id = res["event_id"]
391391

392392
# Reset the event cache so the tests start with it empty
393-
self.get_success(self.store._get_event_cache.clear())
393+
self.store._get_event_cache.clear()
394394

395395
@contextmanager
396396
def blocking_get_event_calls(

0 commit comments

Comments
 (0)