Skip to content

Commit 94ddab1

Browse files
committed
Updated docs, convert getters function to properties, added dataclasses
1 parent 18a4d3d commit 94ddab1

File tree

4 files changed

+55
-55
lines changed

4 files changed

+55
-55
lines changed

docs/resp3_features.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Client-side caching
7272
-------------------
7373

7474
Client-side caching is a technique used to create high performance services.
75-
It exploits the memory available on application servers, servers that are usually distinct computers compared to the database nodes, to store some subset of the database information directly in the application side.
75+
It utilizes the memory on application servers, typically separate from the database nodes, to cache a subset of the data directly on the application side.
7676
For more information please check `official Redis documentation <https://redis.io/docs/latest/develop/use/client-side-caching/>`_.
7777
Please notice that this feature only available with RESP3 protocol enabled in sync client only. Supported in standalone, Cluster and Sentinel clients.
7878

@@ -98,4 +98,4 @@ Enable caching with custom cache implementation:
9898
9999
CacheImpl should implement a `CacheInterface` specified in `redis.cache` package.
100100

101-
More robust documentation soon will be available at `official Redis documentation <https://redis.io/docs/latest/>`_.
101+
More comprehensive documentation soon will be available at `official Redis documentation <https://redis.io/docs/latest/>`_.

redis/cache.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from abc import ABC, abstractmethod
22
from collections import OrderedDict
3+
from dataclasses import dataclass
34
from enum import Enum
45
from typing import Any, List, Optional, Union
56

@@ -14,19 +15,10 @@ class EvictionPolicyType(Enum):
1415
frequency_based = "frequency_based"
1516

1617

18+
@dataclass(frozen=True)
1719
class CacheKey:
18-
def __init__(self, command: str, redis_keys: tuple):
19-
self.command = command
20-
self.redis_keys = redis_keys
21-
22-
def get_redis_keys(self) -> tuple:
23-
return self.redis_keys
24-
25-
def __hash__(self):
26-
return hash((self.command, self.redis_keys))
27-
28-
def __eq__(self, other):
29-
return hash(self) == hash(other)
20+
command: str
21+
redis_keys: tuple
3022

3123

3224
class CacheEntry:
@@ -102,20 +94,24 @@ def is_allowed_to_cache(self, command: str) -> bool:
10294

10395

10496
class CacheInterface(ABC):
97+
@property
10598
@abstractmethod
106-
def get_collection(self) -> OrderedDict:
99+
def collection(self) -> OrderedDict:
107100
pass
108101

102+
@property
109103
@abstractmethod
110-
def get_config(self) -> CacheConfigurationInterface:
104+
def config(self) -> CacheConfigurationInterface:
111105
pass
112106

107+
@property
113108
@abstractmethod
114-
def get_eviction_policy(self) -> EvictionPolicyInterface:
109+
def eviction_policy(self) -> EvictionPolicyInterface:
115110
pass
116111

112+
@property
117113
@abstractmethod
118-
def get_size(self) -> int:
114+
def size(self) -> int:
119115
pass
120116

121117
@abstractmethod
@@ -153,16 +149,20 @@ def __init__(
153149
self._eviction_policy = self._cache_config.get_eviction_policy().value()
154150
self._eviction_policy.cache = self
155151

156-
def get_collection(self) -> OrderedDict:
152+
@property
153+
def collection(self) -> OrderedDict:
157154
return self._cache
158155

159-
def get_config(self) -> CacheConfigurationInterface:
156+
@property
157+
def config(self) -> CacheConfigurationInterface:
160158
return self._cache_config
161159

162-
def get_eviction_policy(self) -> EvictionPolicyInterface:
160+
@property
161+
def eviction_policy(self) -> EvictionPolicyInterface:
163162
return self._eviction_policy
164163

165-
def get_size(self) -> int:
164+
@property
165+
def size(self) -> int:
166166
return len(self._cache)
167167

168168
def set(self, entry: CacheEntry) -> bool:
@@ -206,7 +206,7 @@ def delete_by_redis_keys(self, redis_keys: List[bytes]) -> List[bool]:
206206
if isinstance(redis_key, bytes):
207207
redis_key = redis_key.decode()
208208
for cache_key in self._cache:
209-
if redis_key in cache_key.get_redis_keys():
209+
if redis_key in cache_key.redis_keys:
210210
keys_to_delete.append(cache_key)
211211
response.append(True)
212212

@@ -242,29 +242,29 @@ def type(self) -> EvictionPolicyType:
242242

243243
def evict_next(self) -> CacheKey:
244244
self._assert_cache()
245-
popped_entry = self._cache.get_collection().popitem(last=False)
245+
popped_entry = self._cache.collection.popitem(last=False)
246246
return popped_entry[0]
247247

248248
def evict_many(self, count: int) -> List[CacheKey]:
249249
self._assert_cache()
250-
if count > len(self._cache.get_collection()):
250+
if count > len(self._cache.collection):
251251
raise ValueError("Evictions count is above cache size")
252252

253253
popped_keys = []
254254

255255
for _ in range(count):
256-
popped_entry = self._cache.get_collection().popitem(last=False)
256+
popped_entry = self._cache.collection.popitem(last=False)
257257
popped_keys.append(popped_entry[0])
258258

259259
return popped_keys
260260

261261
def touch(self, cache_key: CacheKey) -> None:
262262
self._assert_cache()
263263

264-
if self._cache.get_collection().get(cache_key) is None:
264+
if self._cache.collection.get(cache_key) is None:
265265
raise ValueError("Given entry does not belong to the cache")
266266

267-
self._cache.get_collection().move_to_end(cache_key)
267+
self._cache.collection.move_to_end(cache_key)
268268

269269
def _assert_cache(self):
270270
if self.cache is None or not isinstance(self.cache, CacheInterface):

tests/test_cache.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def r(request):
4141

4242
@pytest.mark.skipif(HIREDIS_AVAILABLE, reason="PythonParser only")
4343
@pytest.mark.onlynoncluster
44-
@skip_if_resp_version(2)
44+
# @skip_if_resp_version(2)
4545
@skip_if_server_version_lt("7.4.0")
4646
class TestCache:
4747
@pytest.mark.parametrize(
@@ -109,8 +109,8 @@ def test_get_from_given_cache(self, r, r2):
109109
@pytest.mark.onlynoncluster
110110
def test_get_from_default_cache(self, r, r2):
111111
cache = r.get_cache()
112-
assert isinstance(cache.get_eviction_policy(), LRUPolicy)
113-
assert cache.get_config().get_max_size() == 128
112+
assert isinstance(cache.eviction_policy, LRUPolicy)
113+
assert cache.config.get_max_size() == 128
114114

115115
# add key to redis
116116
r.set("foo", "bar")
@@ -161,7 +161,7 @@ def test_cache_clears_on_disconnect(self, r, cache):
161161
# Force disconnection
162162
r.connection_pool.get_connection("_").disconnect()
163163
# Make sure cache is empty
164-
assert cache.get_size() == 0
164+
assert cache.size == 0
165165

166166
@pytest.mark.parametrize(
167167
"r",
@@ -207,7 +207,7 @@ def test_cache_lru_eviction(self, r, cache):
207207
assert r.get("foo4") == b"bar4"
208208
# the first key is not in the local cache anymore
209209
assert cache.get(CacheKey(command="GET", redis_keys=("foo",))) is None
210-
assert cache.get_size() == 3
210+
assert cache.size == 3
211211

212212
@pytest.mark.parametrize(
213213
"r",
@@ -321,7 +321,7 @@ def test_cache_flushed_on_server_flush(self, r):
321321
# Flush server and trying to access cached entry
322322
assert r.flushall()
323323
assert r.get("foo") is None
324-
assert cache.get_size() == 0
324+
assert cache.size == 0
325325

326326

327327
@pytest.mark.skipif(HIREDIS_AVAILABLE, reason="PythonParser only")
@@ -383,8 +383,8 @@ def test_get_from_cache(self, r):
383383
)
384384
def test_get_from_custom_cache(self, r, r2):
385385
cache = r.nodes_manager.get_node_from_slot(12000).redis_connection.get_cache()
386-
assert isinstance(cache.get_eviction_policy(), LRUPolicy)
387-
assert cache.get_config().get_max_size() == 128
386+
assert isinstance(cache.eviction_policy, LRUPolicy)
387+
assert cache.config.get_max_size() == 128
388388

389389
# add key to redis
390390
assert r.set("foo", "bar")
@@ -431,7 +431,7 @@ def test_cache_clears_on_disconnect(self, r, r2):
431431
12000
432432
).redis_connection.connection_pool.get_connection("_").disconnect()
433433
# Make sure cache is empty
434-
assert cache.get_size() == 0
434+
assert cache.size == 0
435435

436436
@pytest.mark.parametrize(
437437
"r",
@@ -564,7 +564,7 @@ def test_cache_flushed_on_server_flush(self, r, cache):
564564
# Flush server and trying to access cached entry
565565
assert r.flushall()
566566
assert r.get("foo{slot}") is None
567-
assert cache.get_size() == 0
567+
assert cache.size == 0
568568

569569

570570
@pytest.mark.skipif(HIREDIS_AVAILABLE, reason="PythonParser only")
@@ -623,7 +623,7 @@ def test_get_from_cache(self, master):
623623
)
624624
def test_get_from_default_cache(self, r, r2):
625625
cache = r.get_cache()
626-
assert isinstance(cache.get_eviction_policy(), LRUPolicy)
626+
assert isinstance(cache.eviction_policy, LRUPolicy)
627627

628628
# add key to redis
629629
r.set("foo", "bar")
@@ -669,7 +669,7 @@ def test_cache_clears_on_disconnect(self, master, cache):
669669
# Force disconnection
670670
master.connection_pool.get_connection("_").disconnect()
671671
# Make sure cache_data is empty
672-
assert cache.get_size() == 0
672+
assert cache.size == 0
673673

674674

675675
@pytest.mark.skipif(HIREDIS_AVAILABLE, reason="PythonParser only")
@@ -734,7 +734,7 @@ def test_get_from_cache(self, r, r2, cache):
734734
)
735735
def test_get_from_custom_cache(self, r, r2):
736736
cache = r.get_cache()
737-
assert isinstance(cache.get_eviction_policy(), LRUPolicy)
737+
assert isinstance(cache.eviction_policy, LRUPolicy)
738738

739739
# add key to redis
740740
r.set("foo", "bar")
@@ -798,15 +798,15 @@ def test_cache_invalidate_all_related_responses(self, r):
798798
class TestUnitDefaultCache:
799799
def test_get_eviction_policy(self):
800800
cache = DefaultCache(CacheConfig(max_size=5))
801-
assert isinstance(cache.get_eviction_policy(), LRUPolicy)
801+
assert isinstance(cache.eviction_policy, LRUPolicy)
802802

803803
def test_get_max_size(self):
804804
cache = DefaultCache(CacheConfig(max_size=5))
805-
assert cache.get_config().get_max_size() == 5
805+
assert cache.config.get_max_size() == 5
806806

807807
def test_get_size(self):
808808
cache = DefaultCache(CacheConfig(max_size=5))
809-
assert cache.get_size() == 0
809+
assert cache.size == 0
810810

811811
@pytest.mark.parametrize(
812812
"cache_key", [{"command": "GET", "redis_keys": ("bar",)}], indirect=True
@@ -988,7 +988,7 @@ def test_delete_by_cache_keys_removes_associated_entries(self, mock_connection):
988988
True,
989989
False,
990990
]
991-
assert len(cache.get_collection()) == 1
991+
assert len(cache.collection) == 1
992992
assert cache.get(cache_key3).cache_value == b"bar2"
993993

994994
def test_delete_by_redis_keys_removes_associated_entries(self, mock_connection):
@@ -1034,7 +1034,7 @@ def test_delete_by_redis_keys_removes_associated_entries(self, mock_connection):
10341034
)
10351035

10361036
assert cache.delete_by_redis_keys([b"foo", b"foo1"]) == [True, True, True]
1037-
assert len(cache.get_collection()) == 1
1037+
assert len(cache.collection) == 1
10381038
assert cache.get(cache_key4).cache_value == b"bar3"
10391039

10401040
def test_flush(self, mock_connection):
@@ -1071,7 +1071,7 @@ def test_flush(self, mock_connection):
10711071
)
10721072

10731073
assert cache.flush() == 3
1074-
assert len(cache.get_collection()) == 0
1074+
assert len(cache.collection) == 0
10751075

10761076

10771077
class TestUnitLRUPolicy:
@@ -1083,7 +1083,7 @@ def test_evict_next(self, mock_connection):
10831083
cache = DefaultCache(
10841084
CacheConfig(max_size=5, eviction_policy=EvictionPolicy.LRU)
10851085
)
1086-
policy = cache.get_eviction_policy()
1086+
policy = cache.eviction_policy
10871087

10881088
cache_key1 = CacheKey(command="GET", redis_keys=("foo",))
10891089
cache_key2 = CacheKey(command="GET", redis_keys=("bar",))
@@ -1112,7 +1112,7 @@ def test_evict_many(self, mock_connection):
11121112
cache = DefaultCache(
11131113
CacheConfig(max_size=5, eviction_policy=EvictionPolicy.LRU)
11141114
)
1115-
policy = cache.get_eviction_policy()
1115+
policy = cache.eviction_policy
11161116
cache_key1 = CacheKey(command="GET", redis_keys=("foo",))
11171117
cache_key2 = CacheKey(command="GET", redis_keys=("bar",))
11181118
cache_key3 = CacheKey(command="GET", redis_keys=("baz",))
@@ -1153,7 +1153,7 @@ def test_touch(self, mock_connection):
11531153
cache = DefaultCache(
11541154
CacheConfig(max_size=5, eviction_policy=EvictionPolicy.LRU)
11551155
)
1156-
policy = cache.get_eviction_policy()
1156+
policy = cache.eviction_policy
11571157

11581158
cache_key1 = CacheKey(command="GET", redis_keys=("foo",))
11591159
cache_key2 = CacheKey(command="GET", redis_keys=("bar",))
@@ -1175,7 +1175,7 @@ def test_touch(self, mock_connection):
11751175
)
11761176
)
11771177

1178-
assert cache.get_collection().popitem(last=True)[0] == cache_key2
1178+
assert cache.collection.popitem(last=True)[0] == cache_key2
11791179
cache.set(
11801180
CacheEntry(
11811181
cache_key=cache_key2,
@@ -1186,7 +1186,7 @@ def test_touch(self, mock_connection):
11861186
)
11871187

11881188
policy.touch(cache_key1)
1189-
assert cache.get_collection().popitem(last=True)[0] == cache_key1
1189+
assert cache.collection.popitem(last=True)[0] == cache_key1
11901190

11911191
def test_throws_error_on_invalid_cache(self):
11921192
policy = LRUPolicy()

tests/test_connection.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,8 @@ def test_creates_cache_with_given_configuration(self, mock_cache):
411411
)
412412

413413
assert isinstance(connection_pool.cache, CacheInterface)
414-
assert connection_pool.cache.get_config().get_max_size() == 100
415-
assert isinstance(connection_pool.cache.get_eviction_policy(), LRUPolicy)
414+
assert connection_pool.cache.config.get_max_size() == 100
415+
assert isinstance(connection_pool.cache.eviction_policy, LRUPolicy)
416416
connection_pool.disconnect()
417417

418418
def test_make_connection_proxy_connection_on_given_cache(self):
@@ -447,7 +447,7 @@ def test_clears_cache_on_disconnect(self, mock_connection, cache_conf):
447447
)
448448
proxy_connection.disconnect()
449449

450-
assert len(cache.get_collection()) == 0
450+
assert len(cache.collection) == 0
451451

452452
@pytest.mark.skipif(
453453
platform.python_implementation() == "PyPy",

0 commit comments

Comments
 (0)