Skip to content

Commit 401c10b

Browse files
committed
Cache Feature completely documented
1 parent 628b9eb commit 401c10b

File tree

9 files changed

+521
-25
lines changed

9 files changed

+521
-25
lines changed

docs/caching.md

Lines changed: 462 additions & 0 deletions
Large diffs are not rendered by default.

ellar/cache/backends/aio_cache.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from ..model import BaseCacheBackend
1818

1919

20-
class AioMemCacheBackendSync(IBaseCacheBackendAsync, ABC):
20+
class _AioMemCacheBackendSync(IBaseCacheBackendAsync, ABC):
2121
def _async_executor(self, func: t.Awaitable) -> t.Any:
2222
return get_or_create_eventloop().run_until_complete(func)
2323

@@ -49,7 +49,7 @@ def touch(
4949
return bool(res)
5050

5151

52-
class AioMemCacheBackend(AioMemCacheBackendSync, BaseCacheBackend):
52+
class AioMemCacheBackend(_AioMemCacheBackendSync, BaseCacheBackend):
5353
"""Memcached-based cache backend."""
5454

5555
pickle_protocol = pickle.HIGHEST_PROTOCOL

ellar/cache/backends/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ..model import BaseCacheBackend
88

99

10-
class BasePylibMemcachedCacheSync(BaseCacheBackend, ABC):
10+
class _BasePylibMemcachedCacheSync(BaseCacheBackend, ABC):
1111
_cache_client: t.Any
1212

1313
@make_key_decorator
@@ -52,7 +52,7 @@ def clear(self) -> None:
5252
self._cache_client.flush_all()
5353

5454

55-
class BasePylibMemcachedCache(BasePylibMemcachedCacheSync):
55+
class BasePylibMemcachedCache(_BasePylibMemcachedCacheSync):
5656
MEMCACHE_CLIENT: t.Type
5757

5858
def __init__(self, servers: t.List[str], options: t.Dict = None, **kwargs: t.Any):

ellar/cache/backends/local_cache.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from ..model import BaseCacheBackend
1414

1515

16-
class LocalMemCacheBackendSync(IBaseCacheBackendAsync, ABC):
16+
class _LocalMemCacheBackendSync(IBaseCacheBackendAsync, ABC):
1717
def _async_executor(self, func: t.Awaitable) -> t.Any:
1818
return get_or_create_eventloop().run_until_complete(func)
1919

@@ -45,7 +45,7 @@ def touch(
4545
return bool(res)
4646

4747

48-
class LocalMemCacheBackend(LocalMemCacheBackendSync, BaseCacheBackend):
48+
class LocalMemCacheBackend(_LocalMemCacheBackendSync, BaseCacheBackend):
4949
pickle_protocol = pickle.HIGHEST_PROTOCOL
5050

5151
def __init__(self, **kwargs: t.Any) -> None:

ellar/cache/backends/redis/backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .serializer import IRedisSerializer, RedisSerializer
1818

1919

20-
class RedisCacheBackendSync(IBaseCacheBackendAsync, ABC):
20+
class _RedisCacheBackendSync(IBaseCacheBackendAsync, ABC):
2121
def _async_executor(self, func: t.Awaitable) -> t.Any:
2222
return get_or_create_eventloop().run_until_complete(func)
2323

@@ -49,7 +49,7 @@ def touch(
4949
return bool(res)
5050

5151

52-
class RedisCacheBackend(RedisCacheBackendSync, BaseCacheBackend):
52+
class RedisCacheBackend(_RedisCacheBackendSync, BaseCacheBackend):
5353
MEMCACHE_CLIENT: t.Any = Redis
5454
"""Redis-based cache backend.
5555

ellar/cache/interface.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import typing as t
22
from abc import ABC, abstractmethod
33

4+
if t.TYPE_CHECKING:
5+
from ellar.cache.model import BaseCacheBackend
6+
47

58
class IBaseCacheBackendAsync(ABC):
69
@abstractmethod
@@ -238,3 +241,10 @@ async def has_key_async(
238241

239242
class ICacheService(ICacheServiceSync, ICacheServiceAsync, ABC):
240243
"""Cache Service Interface"""
244+
245+
def get_backend(self, backend: str = None) -> "BaseCacheBackend":
246+
"""
247+
Return a given Cache Backend configured by configuration backend name.
248+
:param backend: Cache Backend configuration name. If not set, 'default' backend will be returned
249+
:return: BaseCacheBackend
250+
"""

ellar/cache/model.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ def __init__(
2323
self._version = version or 1
2424
self._default_timeout = int(timeout) if timeout else 300
2525

26+
@property
27+
def key_prefix(self) -> str:
28+
return self._key_prefix
29+
2630
async def has_key_async(self, key: str, version: str = None) -> bool:
2731
"""
2832
Return True if the key is in the cache and has not expired.

ellar/cache/service.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
from .model import BaseCacheBackend
88

99

10-
class CacheServiceSync(ICacheServiceSync):
10+
class InvalidCacheBackendKeyException(Exception):
11+
pass
12+
13+
14+
class _CacheServiceSync(ICacheServiceSync):
1115
_get_backend: t.Callable[..., BaseCacheBackend]
1216

1317
def get(self, key: str, version: str = None, backend: str = None) -> t.Any:
@@ -45,7 +49,11 @@ def has_key(self, key: str, version: str = None, backend: str = None) -> bool:
4549

4650

4751
@injectable # type: ignore
48-
class CacheService(CacheServiceSync, ICacheService):
52+
class CacheService(_CacheServiceSync, ICacheService):
53+
"""
54+
A Cache Backend Service that wraps Ellar cache backends
55+
"""
56+
4957
def __init__(self, backends: t.Dict[str, BaseCacheBackend] = None) -> None:
5058
if backends:
5159
assert backends.get(
@@ -55,20 +63,25 @@ def __init__(self, backends: t.Dict[str, BaseCacheBackend] = None) -> None:
5563
"default": LocalMemCacheBackend(key_prefix="ellar", version=1, timeout=300)
5664
}
5765

58-
def _get_backend(self, backend: str = None) -> BaseCacheBackend:
66+
def get_backend(self, backend: str = None) -> BaseCacheBackend:
5967
_backend = backend or "default"
60-
return self._backends[_backend]
68+
try:
69+
return self._backends[_backend]
70+
except KeyError:
71+
raise InvalidCacheBackendKeyException(
72+
f"There is no backend configured with the name: '{_backend}'"
73+
)
6174

6275
async def get_async(
6376
self, key: str, version: str = None, backend: str = None
6477
) -> t.Any:
65-
_backend = self._get_backend(backend)
78+
_backend = self.get_backend(backend)
6679
return await _backend.get_async(key, version=version)
6780

6881
async def delete_async(
6982
self, key: str, version: str = None, backend: str = None
7083
) -> bool:
71-
_backend = self._get_backend(backend)
84+
_backend = self.get_backend(backend)
7285
return bool(await _backend.delete_async(key, version=version))
7386

7487
async def set_async(
@@ -79,7 +92,7 @@ async def set_async(
7992
version: str = None,
8093
backend: str = None,
8194
) -> bool:
82-
_backend = self._get_backend(backend)
95+
_backend = self.get_backend(backend)
8396
return await _backend.set_async(key, value, timeout=timeout, version=version)
8497

8598
async def touch_async(
@@ -89,11 +102,11 @@ async def touch_async(
89102
version: str = None,
90103
backend: str = None,
91104
) -> bool:
92-
_backend = self._get_backend(backend)
105+
_backend = self.get_backend(backend)
93106
return await _backend.touch_async(key, timeout=timeout, version=version)
94107

95108
async def has_key_async(
96109
self, key: str, version: str = None, backend: str = None
97110
) -> bool:
98-
_backend = self._get_backend(backend)
111+
_backend = self.get_backend(backend)
99112
return await _backend.has_key_async(key, version=version)

ellar/common/cache.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from functools import wraps
44

55
from ellar.cache.interface import ICacheService
6-
from ellar.core import IExecutionContext
6+
from ellar.core import ExecutionContext, IExecutionContext
77
from ellar.core.params import ExtraEndpointArg
88
from ellar.helper import is_async_callable
99

@@ -59,9 +59,7 @@ def get_decorator_wrapper(self) -> t.Callable:
5959
return self.get_async_cache_wrapper()
6060
return self.get_cache_wrapper()
6161

62-
def route_cache_make_key(
63-
self, context: IExecutionContext, key_prefix: str = ""
64-
) -> str:
62+
def route_cache_make_key(self, context: IExecutionContext, key_prefix: str) -> str:
6563
"""Defaults key generator for caching view"""
6664
connection = context.switch_to_http_connection()
6765
return f"{connection.get_client().url}:{key_prefix or 'view'}"
@@ -73,7 +71,11 @@ def get_async_cache_wrapper(self) -> t.Callable:
7371
async def _async_wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
7472
cache_service: ICacheService = self._cache_service_arg.resolve(kwargs)
7573
context: IExecutionContext = self._context_arg.resolve(kwargs)
76-
key = self._make_key_callback(context, self._key_prefix or "")
74+
75+
backend = cache_service.get_backend(backend=self._backend)
76+
key = self._make_key_callback(
77+
context, self._key_prefix or backend.key_prefix
78+
)
7779

7880
cached_value = await cache_service.get_async(
7981
key, self._version, backend=self._backend
@@ -99,9 +101,12 @@ def get_cache_wrapper(self) -> t.Callable:
99101
@wraps(self._func)
100102
def _wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
101103
cache_service: ICacheService = self._cache_service_arg.resolve(kwargs)
102-
103104
context: IExecutionContext = self._context_arg.resolve(kwargs)
104-
key = self._make_key_callback(context, self._key_prefix or "")
105+
106+
backend = cache_service.get_backend(backend=self._backend)
107+
key = self._make_key_callback(
108+
context, self._key_prefix or backend.key_prefix
109+
)
105110

106111
cached_value = cache_service.get(key, self._version, backend=self._backend)
107112
if cached_value:
@@ -126,7 +131,9 @@ def cache(
126131
key_prefix: str = "",
127132
version: str = None,
128133
backend: str = "default",
129-
make_key_callback: t.Callable[[IExecutionContext, str], str] = None,
134+
make_key_callback: t.Callable[
135+
[t.Union[ExecutionContext, IExecutionContext], str], str
136+
] = None,
130137
) -> t.Callable:
131138
def _wraps(func: t.Callable) -> t.Callable:
132139
cache_decorator = CacheDecorator(

0 commit comments

Comments
 (0)