Skip to content

Commit c5bbff9

Browse files
committed
WIP: Idea on managing caches
1 parent 6a11964 commit c5bbff9

2 files changed

Lines changed: 70 additions & 75 deletions

File tree

synapse/util/caches/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
from synapse.config.cache import add_resizable_cache
3434
from synapse.util.metrics import DynamicCollectorRegistry
35+
from synapse.util.caches.deferred_cache import DeferredCache
3536

3637
if TYPE_CHECKING:
3738
from synapse.server import HomeServer
@@ -224,6 +225,26 @@ class CacheManager:
224225
def __init__(self, hs: "HomeServer") -> None:
225226
self._cache_metrics = CacheMetrics(hs.metrics_collector_registry)
226227

228+
self._deferred_cache_map: Dict[str, DeferredCache[CacheKey, Any]] = {}
229+
230+
def get_deferred_cache(
231+
self,
232+
name: str,
233+
max_entries: int = 1000,
234+
tree: bool = False,
235+
iterable: bool = False,
236+
apply_cache_factor_from_config: bool = True,
237+
prune_unread_entries: bool = True,
238+
) -> DeferredCache[CacheKey, Any]:
239+
cache: DeferredCache[CacheKey, Any] = DeferredCache(
240+
name=self.name,
241+
cache_manager=self,
242+
max_entries=self.max_entries,
243+
tree=self.tree,
244+
iterable=self.iterable,
245+
prune_unread_entries=self.prune_unread_entries,
246+
)
247+
227248
def register_cache(
228249
self,
229250
cache_type: str,

synapse/util/caches/descriptors.py

Lines changed: 49 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,16 @@
6060

6161
CacheKey = Union[Tuple, Any]
6262

63-
F = TypeVar("F", bound=Callable[..., Any])
63+
P = ParamSpec("P")
64+
R = TypeVar("R")
65+
66+
67+
class HasCacheManager(Protocol):
68+
# Used to handle registering the caches
69+
cache_manager: CacheManager
70+
71+
72+
F = TypeVar("F", bound=Callable[Concatenate[HasCacheManager, P], Any])
6473

6574

6675
class CachedFunction(Generic[F]):
@@ -80,7 +89,7 @@ class CachedFunction(Generic[F]):
8089
class _CacheDescriptorBase:
8190
def __init__(
8291
self,
83-
orig: Callable[..., Any],
92+
orig: Callable[Concatenate[HasCacheManager, P], Any],
8493
num_args: Optional[int],
8594
uncached_args: Optional[Collection[str]] = None,
8695
cache_context: bool = False,
@@ -205,9 +214,8 @@ def foo(self, key, cache_context):
205214

206215
def __init__(
207216
self,
208-
orig: Callable[..., Any],
217+
orig: Callable[Concatenate[HasCacheManager, P], Any],
209218
*,
210-
cache_manager: CacheManager,
211219
max_entries: int = 1000,
212220
num_args: Optional[int] = None,
213221
uncached_args: Optional[Collection[str]] = None,
@@ -224,7 +232,6 @@ def __init__(
224232
cache_context=cache_context,
225233
name=name,
226234
)
227-
self.cache_manager = cache_manager
228235

229236
if tree and self.num_args < 2:
230237
raise RuntimeError(
@@ -239,19 +246,31 @@ def __init__(
239246
def __get__(
240247
self, obj: Optional[Any], owner: Optional[Type]
241248
) -> Callable[..., "defer.Deferred[Any]"]:
242-
cache: DeferredCache[CacheKey, Any] = DeferredCache(
243-
name=self.name,
244-
cache_manager=self.cache_manager,
245-
max_entries=self.max_entries,
246-
tree=self.tree,
247-
iterable=self.iterable,
248-
prune_unread_entries=self.prune_unread_entries,
249-
)
250-
251-
get_cache_key = self.cache_key_builder
249+
logger.info("asdf %s %s", self.orig.__name__, owner)
252250

253251
@functools.wraps(self.orig)
254-
def _wrapped(*args: Any, **kwargs: Any) -> "defer.Deferred[Any]":
252+
def _wrapped(
253+
wrapped_self: HasCacheManager, *args: Any, **kwargs: Any
254+
) -> "defer.Deferred[Any]":
255+
# cache: DeferredCache[CacheKey, Any] = DeferredCache(
256+
# name=self.name,
257+
# cache_manager=wrapped_self.cache_manager,
258+
# max_entries=self.max_entries,
259+
# tree=self.tree,
260+
# iterable=self.iterable,
261+
# prune_unread_entries=self.prune_unread_entries,
262+
# )
263+
cache = wrapped_self.cache_manager.get_deferred_cache(
264+
self.name,
265+
cache_manager=wrapped_self.cache_manager,
266+
max_entries=self.max_entries,
267+
tree=self.tree,
268+
iterable=self.iterable,
269+
prune_unread_entries=self.prune_unread_entries,
270+
)
271+
272+
get_cache_key = self.cache_key_builder
273+
255274
# If we're passed a cache_context then we'll want to call its invalidate()
256275
# whenever we are invalidated
257276
invalidate_callback = kwargs.pop("on_invalidate", None)
@@ -496,12 +515,10 @@ class _CachedFunctionDescriptor:
496515
iterable: bool
497516
prune_unread_entries: bool
498517
name: Optional[str]
499-
cache_manager: CacheManager
500518

501519
def __call__(self, orig: F) -> CachedFunction[F]:
502520
d = DeferredCacheDescriptor(
503521
orig,
504-
cache_manager=self.cache_manager,
505522
max_entries=self.max_entries,
506523
num_args=self.num_args,
507524
uncached_args=self.uncached_args,
@@ -514,15 +531,6 @@ def __call__(self, orig: F) -> CachedFunction[F]:
514531
return cast(CachedFunction[F], d)
515532

516533

517-
P = ParamSpec("P")
518-
R = TypeVar("R")
519-
520-
521-
class HasCacheManager(Protocol):
522-
# Used to handle registering the caches
523-
cache_manager: CacheManager
524-
525-
526534
def cached(
527535
*,
528536
max_entries: int = 1000,
@@ -533,55 +541,21 @@ def cached(
533541
iterable: bool = False,
534542
prune_unread_entries: bool = True,
535543
name: Optional[str] = None,
536-
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]:
537-
"""Decorate an async method with a `Measure` context manager.
538-
539-
The Measure is created using `self.cache_manager`; it should only be used to decorate
540-
methods in classes defining an instance-level `clock` attribute.
541-
542-
Usage:
543-
544-
@measure_func()
545-
async def foo(...):
546-
...
547-
548-
Which is analogous to:
549-
550-
async def foo(...):
551-
with Measure(...):
552-
...
553-
544+
) -> _CachedFunctionDescriptor:
554545
"""
555-
556-
def wrapper(
557-
func: Callable[Concatenate[HasCacheManager, P], Awaitable[R]],
558-
) -> Callable[P, Awaitable[R]]:
559-
# block_name = func.__name__ if name is None else name
560-
561-
@functools.wraps(func)
562-
async def cached_func(
563-
self: HasCacheManager, *args: P.args, **kwargs: P.kwargs
564-
) -> R:
565-
return _CachedFunctionDescriptor(
566-
max_entries=max_entries,
567-
num_args=num_args,
568-
uncached_args=uncached_args,
569-
tree=tree,
570-
cache_context=cache_context,
571-
iterable=iterable,
572-
prune_unread_entries=prune_unread_entries,
573-
name=name,
574-
# Grab this attribute from the instance
575-
cache_manager=self.cache_manager,
576-
)
577-
578-
# There are some shenanigans here, because we're decorating a method but
579-
# explicitly making use of the `self` parameter. The key thing here is that the
580-
# return type within the return type for `measure_func` itself describes how the
581-
# decorated function will be called.
582-
return cached_func # type: ignore[return-value]
583-
584-
return wrapper # type: ignore[return-value]
546+
The cache is created using `self.cache_manager`; it should only be used to decorate
547+
methods in classes defining an instance-level `cache_manager` attribute.
548+
"""
549+
return _CachedFunctionDescriptor(
550+
max_entries=max_entries,
551+
num_args=num_args,
552+
uncached_args=uncached_args,
553+
tree=tree,
554+
cache_context=cache_context,
555+
iterable=iterable,
556+
prune_unread_entries=prune_unread_entries,
557+
name=name,
558+
)
585559

586560

587561
@attr.s(auto_attribs=True, slots=True, frozen=True)

0 commit comments

Comments
 (0)