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

Commit 224f2f9

Browse files
authored
Combine LruCache.invalidate and invalidate_many (#9973)
* Make `invalidate` and `invalidate_many` do the same thing ... so that we can do either over the invalidation replication stream, and also because they always confused me a bit. * Kill off `invalidate_many` * changelog
1 parent f42e4c4 commit 224f2f9

File tree

12 files changed

+52
-52
lines changed

12 files changed

+52
-52
lines changed

changelog.d/9973.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make `LruCache.invalidate` support tree invalidation, and remove `invalidate_many`.

synapse/replication/slave/storage/devices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def _invalidate_caches_for_devices(self, token, rows):
6868
if row.entity.startswith("@"):
6969
self._device_list_stream_cache.entity_has_changed(row.entity, token)
7070
self.get_cached_devices_for_user.invalidate((row.entity,))
71-
self._get_cached_user_device.invalidate_many((row.entity,))
71+
self._get_cached_user_device.invalidate((row.entity,))
7272
self.get_device_list_last_stream_id_for_remote.invalidate((row.entity,))
7373

7474
else:

synapse/storage/databases/main/cache.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def _invalidate_caches_for_event(
171171

172172
self.get_latest_event_ids_in_room.invalidate((room_id,))
173173

174-
self.get_unread_event_push_actions_by_room_for_user.invalidate_many((room_id,))
174+
self.get_unread_event_push_actions_by_room_for_user.invalidate((room_id,))
175175

176176
if not backfilled:
177177
self._events_stream_cache.entity_has_changed(room_id, stream_ordering)
@@ -184,8 +184,8 @@ def _invalidate_caches_for_event(
184184
self.get_invited_rooms_for_local_user.invalidate((state_key,))
185185

186186
if relates_to:
187-
self.get_relations_for_event.invalidate_many((relates_to,))
188-
self.get_aggregation_groups_for_event.invalidate_many((relates_to,))
187+
self.get_relations_for_event.invalidate((relates_to,))
188+
self.get_aggregation_groups_for_event.invalidate((relates_to,))
189189
self.get_applicable_edit.invalidate((relates_to,))
190190

191191
async def invalidate_cache_and_stream(self, cache_name: str, keys: Tuple[Any, ...]):

synapse/storage/databases/main/devices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ def _update_remote_device_list_cache_txn(
12821282
)
12831283

12841284
txn.call_after(self.get_cached_devices_for_user.invalidate, (user_id,))
1285-
txn.call_after(self._get_cached_user_device.invalidate_many, (user_id,))
1285+
txn.call_after(self._get_cached_user_device.invalidate, (user_id,))
12861286
txn.call_after(
12871287
self.get_device_list_last_stream_id_for_remote.invalidate, (user_id,)
12881288
)

synapse/storage/databases/main/event_push_actions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ def _remove_old_push_actions_before_txn(
860860
not be deleted.
861861
"""
862862
txn.call_after(
863-
self.get_unread_event_push_actions_by_room_for_user.invalidate_many,
863+
self.get_unread_event_push_actions_by_room_for_user.invalidate,
864864
(room_id, user_id),
865865
)
866866

synapse/storage/databases/main/events.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,9 +1748,9 @@ def _handle_event_relations(self, txn, event):
17481748
},
17491749
)
17501750

1751-
txn.call_after(self.store.get_relations_for_event.invalidate_many, (parent_id,))
1751+
txn.call_after(self.store.get_relations_for_event.invalidate, (parent_id,))
17521752
txn.call_after(
1753-
self.store.get_aggregation_groups_for_event.invalidate_many, (parent_id,)
1753+
self.store.get_aggregation_groups_for_event.invalidate, (parent_id,)
17541754
)
17551755

17561756
if rel_type == RelationTypes.REPLACE:
@@ -1903,7 +1903,7 @@ def _set_push_actions_for_event_and_users_txn(
19031903

19041904
for user_id in user_ids:
19051905
txn.call_after(
1906-
self.store.get_unread_event_push_actions_by_room_for_user.invalidate_many,
1906+
self.store.get_unread_event_push_actions_by_room_for_user.invalidate,
19071907
(room_id, user_id),
19081908
)
19091909

@@ -1917,7 +1917,7 @@ def _set_push_actions_for_event_and_users_txn(
19171917
def _remove_push_actions_for_event_id_txn(self, txn, room_id, event_id):
19181918
# Sad that we have to blow away the cache for the whole room here
19191919
txn.call_after(
1920-
self.store.get_unread_event_push_actions_by_room_for_user.invalidate_many,
1920+
self.store.get_unread_event_push_actions_by_room_for_user.invalidate,
19211921
(room_id,),
19221922
)
19231923
txn.execute(

synapse/storage/databases/main/receipts.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ def _invalidate_get_users_with_receipts_in_room(
460460

461461
def invalidate_caches_for_receipt(self, room_id, receipt_type, user_id):
462462
self.get_receipts_for_user.invalidate((user_id, receipt_type))
463-
self._get_linearized_receipts_for_room.invalidate_many((room_id,))
463+
self._get_linearized_receipts_for_room.invalidate((room_id,))
464464
self.get_last_receipt_event_id_for_user.invalidate(
465465
(user_id, room_id, receipt_type)
466466
)
@@ -659,9 +659,7 @@ def insert_graph_receipt_txn(
659659
)
660660
txn.call_after(self.get_receipts_for_user.invalidate, (user_id, receipt_type))
661661
# FIXME: This shouldn't invalidate the whole cache
662-
txn.call_after(
663-
self._get_linearized_receipts_for_room.invalidate_many, (room_id,)
664-
)
662+
txn.call_after(self._get_linearized_receipts_for_room.invalidate, (room_id,))
665663

666664
self.db_pool.simple_delete_txn(
667665
txn,

synapse/util/caches/deferred_cache.py

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,7 @@
1616

1717
import enum
1818
import threading
19-
from typing import (
20-
Callable,
21-
Generic,
22-
Iterable,
23-
MutableMapping,
24-
Optional,
25-
TypeVar,
26-
Union,
27-
cast,
28-
)
19+
from typing import Callable, Generic, Iterable, MutableMapping, Optional, TypeVar, Union
2920

3021
from prometheus_client import Gauge
3122

@@ -91,7 +82,7 @@ def __init__(
9182
# _pending_deferred_cache maps from the key value to a `CacheEntry` object.
9283
self._pending_deferred_cache = (
9384
cache_type()
94-
) # type: MutableMapping[KT, CacheEntry]
85+
) # type: Union[TreeCache, MutableMapping[KT, CacheEntry]]
9586

9687
def metrics_cb():
9788
cache_pending_metric.labels(name).set(len(self._pending_deferred_cache))
@@ -287,8 +278,17 @@ def prefill(
287278
self.cache.set(key, value, callbacks=callbacks)
288279

289280
def invalidate(self, key):
281+
"""Delete a key, or tree of entries
282+
283+
If the cache is backed by a regular dict, then "key" must be of
284+
the right type for this cache
285+
286+
If the cache is backed by a TreeCache, then "key" must be a tuple, but
287+
may be of lower cardinality than the TreeCache - in which case the whole
288+
subtree is deleted.
289+
"""
290290
self.check_thread()
291-
self.cache.pop(key, None)
291+
self.cache.del_multi(key)
292292

293293
# if we have a pending lookup for this key, remove it from the
294294
# _pending_deferred_cache, which will (a) stop it being returned
@@ -299,20 +299,10 @@ def invalidate(self, key):
299299
# run the invalidation callbacks now, rather than waiting for the
300300
# deferred to resolve.
301301
if entry:
302-
entry.invalidate()
303-
304-
def invalidate_many(self, key: KT):
305-
self.check_thread()
306-
if not isinstance(key, tuple):
307-
raise TypeError("The cache key must be a tuple not %r" % (type(key),))
308-
key = cast(KT, key)
309-
self.cache.del_multi(key)
310-
311-
# if we have a pending lookup for this key, remove it from the
312-
# _pending_deferred_cache, as above
313-
entry_dict = self._pending_deferred_cache.pop(key, None)
314-
if entry_dict is not None:
315-
for entry in iterate_tree_cache_entry(entry_dict):
302+
# _pending_deferred_cache.pop should either return a CacheEntry, or, in the
303+
# case of a TreeCache, a dict of keys to cache entries. Either way calling
304+
# iterate_tree_cache_entry on it will do the right thing.
305+
for entry in iterate_tree_cache_entry(entry):
316306
entry.invalidate()
317307

318308
def invalidate_all(self):

synapse/util/caches/descriptors.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
class _CachedFunction(Generic[F]):
4949
invalidate = None # type: Any
5050
invalidate_all = None # type: Any
51-
invalidate_many = None # type: Any
5251
prefill = None # type: Any
5352
cache = None # type: Any
5453
num_args = None # type: Any
@@ -262,6 +261,11 @@ def __init__(
262261
):
263262
super().__init__(orig, num_args=num_args, cache_context=cache_context)
264263

264+
if tree and self.num_args < 2:
265+
raise RuntimeError(
266+
"tree=True is nonsensical for cached functions with a single parameter"
267+
)
268+
265269
self.max_entries = max_entries
266270
self.tree = tree
267271
self.iterable = iterable
@@ -302,11 +306,11 @@ def _wrapped(*args, **kwargs):
302306
wrapped = cast(_CachedFunction, _wrapped)
303307

304308
if self.num_args == 1:
309+
assert not self.tree
305310
wrapped.invalidate = lambda key: cache.invalidate(key[0])
306311
wrapped.prefill = lambda key, val: cache.prefill(key[0], val)
307312
else:
308313
wrapped.invalidate = cache.invalidate
309-
wrapped.invalidate_many = cache.invalidate_many
310314
wrapped.prefill = cache.prefill
311315

312316
wrapped.invalidate_all = cache.invalidate_all

synapse/util/caches/lrucache.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ class LruCache(Generic[KT, VT]):
152152
"""
153153
Least-recently-used cache, supporting prometheus metrics and invalidation callbacks.
154154
155-
Supports del_multi only if cache_type=TreeCache
156155
If cache_type=TreeCache, all keys must be tuples.
157156
"""
158157

@@ -393,10 +392,16 @@ def cache_pop(key: KT, default: Optional[T] = None):
393392

394393
@synchronized
395394
def cache_del_multi(key: KT) -> None:
395+
"""Delete an entry, or tree of entries
396+
397+
If the LruCache is backed by a regular dict, then "key" must be of
398+
the right type for this cache
399+
400+
If the LruCache is backed by a TreeCache, then "key" must be a tuple, but
401+
may be of lower cardinality than the TreeCache - in which case the whole
402+
subtree is deleted.
396403
"""
397-
This will only work if constructed with cache_type=TreeCache
398-
"""
399-
popped = cache.pop(key)
404+
popped = cache.pop(key, None)
400405
if popped is None:
401406
return
402407
# for each deleted node, we now need to remove it from the linked list
@@ -430,11 +435,10 @@ def cache_contains(key: KT) -> bool:
430435
self.set = cache_set
431436
self.setdefault = cache_set_default
432437
self.pop = cache_pop
438+
self.del_multi = cache_del_multi
433439
# `invalidate` is exposed for consistency with DeferredCache, so that it can be
434440
# invalidated by the cache invalidation replication stream.
435-
self.invalidate = cache_pop
436-
if cache_type is TreeCache:
437-
self.del_multi = cache_del_multi
441+
self.invalidate = cache_del_multi
438442
self.len = synchronized(cache_len)
439443
self.contains = cache_contains
440444
self.clear = cache_clear

synapse/util/caches/treecache.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ def pop(self, key, default=None):
8989
value. If the key is partial, the TreeCacheNode corresponding to the part
9090
of the tree that was removed.
9191
"""
92+
if not isinstance(key, tuple):
93+
raise TypeError("The cache key must be a tuple not %r" % (type(key),))
94+
9295
# a list of the nodes we have touched on the way down the tree
9396
nodes = []
9497

tests/util/caches/test_descriptors.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,17 +622,17 @@ def func2(self, key, cache_context):
622622
self.assertEquals(callcount2[0], 1)
623623

624624
a.func2.invalidate(("foo",))
625-
self.assertEquals(a.func2.cache.cache.pop.call_count, 1)
625+
self.assertEquals(a.func2.cache.cache.del_multi.call_count, 1)
626626

627627
yield a.func2("foo")
628628
a.func2.invalidate(("foo",))
629-
self.assertEquals(a.func2.cache.cache.pop.call_count, 2)
629+
self.assertEquals(a.func2.cache.cache.del_multi.call_count, 2)
630630

631631
self.assertEquals(callcount[0], 1)
632632
self.assertEquals(callcount2[0], 2)
633633

634634
a.func.invalidate(("foo",))
635-
self.assertEquals(a.func2.cache.cache.pop.call_count, 3)
635+
self.assertEquals(a.func2.cache.cache.del_multi.call_count, 3)
636636
yield a.func("foo")
637637

638638
self.assertEquals(callcount[0], 2)

0 commit comments

Comments
 (0)