Skip to content

Commit 087a366

Browse files
dvora-hvladvildanov
authored andcommitted
CSC Review Fixes - expose delete functions, rename attribute names, add AbstractCache class (#3110)
* CSC review fixes * cahnge cache_max_size default value * use ABC and add docstring
1 parent e1a7fec commit 087a366

File tree

7 files changed

+196
-60
lines changed

7 files changed

+196
-60
lines changed

redis/_cache.py

+39-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import copy
22
import random
33
import time
4+
from abc import ABC, abstractmethod
45
from collections import OrderedDict, defaultdict
56
from enum import Enum
67
from typing import List
@@ -160,7 +161,38 @@ class EvictionPolicy(Enum):
160161
RANDOM = "random"
161162

162163

163-
class _LocalCache:
164+
class AbstractCache(ABC):
165+
"""
166+
An abstract base class for client caching implementations.
167+
If you want to implement your own cache you must support these methods.
168+
"""
169+
170+
@abstractmethod
171+
def set(self, command: str, response: ResponseT, keys_in_command: List[KeyT]):
172+
pass
173+
174+
@abstractmethod
175+
def get(self, command: str) -> ResponseT:
176+
pass
177+
178+
@abstractmethod
179+
def delete_command(self, command: str):
180+
pass
181+
182+
@abstractmethod
183+
def delete_many(self, commands):
184+
pass
185+
186+
@abstractmethod
187+
def flush(self):
188+
pass
189+
190+
@abstractmethod
191+
def invalidate_key(self, key: KeyT):
192+
pass
193+
194+
195+
class _LocalCache(AbstractCache):
164196
"""
165197
A caching mechanism for storing redis commands and their responses.
166198
@@ -180,7 +212,7 @@ class _LocalCache:
180212

181213
def __init__(
182214
self,
183-
max_size: int = 100,
215+
max_size: int = 10000,
184216
ttl: int = 0,
185217
eviction_policy: EvictionPolicy = DEFAULT_EVICTION_POLICY,
186218
**kwargs,
@@ -224,12 +256,12 @@ def get(self, command: str) -> ResponseT:
224256
"""
225257
if command in self.cache:
226258
if self._is_expired(command):
227-
self.delete(command)
259+
self.delete_command(command)
228260
return
229261
self._update_access(command)
230262
return copy.deepcopy(self.cache[command]["response"])
231263

232-
def delete(self, command: str):
264+
def delete_command(self, command: str):
233265
"""
234266
Delete a redis command and its metadata from the cache.
235267
@@ -285,7 +317,7 @@ def _update_access(self, command: str):
285317
def _evict(self):
286318
"""Evict a redis command from the cache based on the eviction policy."""
287319
if self._is_expired(self.commands_ttl_list[0]):
288-
self.delete(self.commands_ttl_list[0])
320+
self.delete_command(self.commands_ttl_list[0])
289321
elif self.eviction_policy == EvictionPolicy.LRU.value:
290322
self.cache.popitem(last=False)
291323
elif self.eviction_policy == EvictionPolicy.LFU.value:
@@ -319,7 +351,7 @@ def _del_key_commands_map(self, keys: List[KeyT], command: str):
319351
for key in keys:
320352
self.key_commands_map[key].remove(command)
321353

322-
def invalidate(self, key: KeyT):
354+
def invalidate_key(self, key: KeyT):
323355
"""
324356
Invalidate (delete) all redis commands associated with a specific key.
325357
@@ -330,4 +362,4 @@ def invalidate(self, key: KeyT):
330362
return
331363
commands = list(self.key_commands_map[key])
332364
for command in commands:
333-
self.delete(command)
365+
self.delete_command(command)

redis/asyncio/client.py

+33-6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
DEFAULT_BLACKLIST,
2828
DEFAULT_EVICTION_POLICY,
2929
DEFAULT_WHITELIST,
30-
_LocalCache,
30+
AbstractCache,
3131
)
3232
from redis._parsers.helpers import (
3333
_RedisCallbacks,
@@ -237,11 +237,11 @@ def __init__(
237237
redis_connect_func=None,
238238
credential_provider: Optional[CredentialProvider] = None,
239239
protocol: Optional[int] = 2,
240-
cache_enable: bool = False,
241-
client_cache: Optional[_LocalCache] = None,
240+
cache_enabled: bool = False,
241+
client_cache: Optional[AbstractCache] = None,
242242
cache_max_size: int = 100,
243243
cache_ttl: int = 0,
244-
cache_eviction_policy: str = DEFAULT_EVICTION_POLICY,
244+
cache_policy: str = DEFAULT_EVICTION_POLICY,
245245
cache_blacklist: List[str] = DEFAULT_BLACKLIST,
246246
cache_whitelist: List[str] = DEFAULT_WHITELIST,
247247
):
@@ -293,11 +293,11 @@ def __init__(
293293
"lib_version": lib_version,
294294
"redis_connect_func": redis_connect_func,
295295
"protocol": protocol,
296-
"cache_enable": cache_enable,
296+
"cache_enabled": cache_enabled,
297297
"client_cache": client_cache,
298298
"cache_max_size": cache_max_size,
299299
"cache_ttl": cache_ttl,
300-
"cache_eviction_policy": cache_eviction_policy,
300+
"cache_policy": cache_policy,
301301
"cache_blacklist": cache_blacklist,
302302
"cache_whitelist": cache_whitelist,
303303
}
@@ -667,6 +667,33 @@ async def parse_response(
667667
return await retval if inspect.isawaitable(retval) else retval
668668
return response
669669

670+
def flush_cache(self):
671+
try:
672+
if self.connection:
673+
self.connection.client_cache.flush()
674+
else:
675+
self.connection_pool.flush_cache()
676+
except AttributeError:
677+
pass
678+
679+
def delete_command_from_cache(self, command):
680+
try:
681+
if self.connection:
682+
self.connection.client_cache.delete_command(command)
683+
else:
684+
self.connection_pool.delete_command_from_cache(command)
685+
except AttributeError:
686+
pass
687+
688+
def invalidate_key_from_cache(self, key):
689+
try:
690+
if self.connection:
691+
self.connection.client_cache.invalidate_key(key)
692+
else:
693+
self.connection_pool.invalidate_key_from_cache(key)
694+
except AttributeError:
695+
pass
696+
670697

671698
StrictRedis = Redis
672699

redis/asyncio/cluster.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
DEFAULT_BLACKLIST,
2323
DEFAULT_EVICTION_POLICY,
2424
DEFAULT_WHITELIST,
25-
_LocalCache,
25+
AbstractCache,
2626
)
2727
from redis._parsers import AsyncCommandsParser, Encoder
2828
from redis._parsers.helpers import (
@@ -273,11 +273,11 @@ def __init__(
273273
ssl_keyfile: Optional[str] = None,
274274
protocol: Optional[int] = 2,
275275
address_remap: Optional[Callable[[str, int], Tuple[str, int]]] = None,
276-
cache_enable: bool = False,
277-
client_cache: Optional[_LocalCache] = None,
276+
cache_enabled: bool = False,
277+
client_cache: Optional[AbstractCache] = None,
278278
cache_max_size: int = 100,
279279
cache_ttl: int = 0,
280-
cache_eviction_policy: str = DEFAULT_EVICTION_POLICY,
280+
cache_policy: str = DEFAULT_EVICTION_POLICY,
281281
cache_blacklist: List[str] = DEFAULT_BLACKLIST,
282282
cache_whitelist: List[str] = DEFAULT_WHITELIST,
283283
) -> None:
@@ -324,11 +324,11 @@ def __init__(
324324
"retry": retry,
325325
"protocol": protocol,
326326
# Client cache related kwargs
327-
"cache_enable": cache_enable,
327+
"cache_enabled": cache_enabled,
328328
"client_cache": client_cache,
329329
"cache_max_size": cache_max_size,
330330
"cache_ttl": cache_ttl,
331-
"cache_eviction_policy": cache_eviction_policy,
331+
"cache_policy": cache_policy,
332332
"cache_blacklist": cache_blacklist,
333333
"cache_whitelist": cache_whitelist,
334334
}

redis/asyncio/connection.py

+38-16
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
DEFAULT_BLACKLIST,
5454
DEFAULT_EVICTION_POLICY,
5555
DEFAULT_WHITELIST,
56+
AbstractCache,
5657
_LocalCache,
5758
)
5859
from .._parsers import (
@@ -156,11 +157,11 @@ def __init__(
156157
encoder_class: Type[Encoder] = Encoder,
157158
credential_provider: Optional[CredentialProvider] = None,
158159
protocol: Optional[int] = 2,
159-
cache_enable: bool = False,
160-
client_cache: Optional[_LocalCache] = None,
161-
cache_max_size: int = 100,
160+
cache_enabled: bool = False,
161+
client_cache: Optional[AbstractCache] = None,
162+
cache_max_size: int = 10000,
162163
cache_ttl: int = 0,
163-
cache_eviction_policy: str = DEFAULT_EVICTION_POLICY,
164+
cache_policy: str = DEFAULT_EVICTION_POLICY,
164165
cache_blacklist: List[str] = DEFAULT_BLACKLIST,
165166
cache_whitelist: List[str] = DEFAULT_WHITELIST,
166167
):
@@ -220,8 +221,8 @@ def __init__(
220221
if p < 2 or p > 3:
221222
raise ConnectionError("protocol must be either 2 or 3")
222223
self.protocol = protocol
223-
if cache_enable:
224-
_cache = _LocalCache(cache_max_size, cache_ttl, cache_eviction_policy)
224+
if cache_enabled:
225+
_cache = _LocalCache(cache_max_size, cache_ttl, cache_policy)
225226
else:
226227
_cache = None
227228
self.client_cache = client_cache if client_cache is not None else _cache
@@ -698,7 +699,7 @@ def _cache_invalidation_process(
698699
self.client_cache.flush()
699700
else:
700701
for key in data[1]:
701-
self.client_cache.invalidate(str_if_bytes(key))
702+
self.client_cache.invalidate_key(str_if_bytes(key))
702703

703704
async def _get_from_local_cache(self, command: str):
704705
"""
@@ -728,15 +729,6 @@ def _add_to_local_cache(
728729
):
729730
self.client_cache.set(command, response, keys)
730731

731-
def delete_from_local_cache(self, command: str):
732-
"""
733-
Delete the command from the local cache
734-
"""
735-
try:
736-
self.client_cache.delete(command)
737-
except AttributeError:
738-
pass
739-
740732

741733
class Connection(AbstractConnection):
742734
"Manages TCP communication to and from a Redis server"
@@ -1240,6 +1232,36 @@ def set_retry(self, retry: "Retry") -> None:
12401232
for conn in self._in_use_connections:
12411233
conn.retry = retry
12421234

1235+
def flush_cache(self):
1236+
connections = chain(self._available_connections, self._in_use_connections)
1237+
1238+
for connection in connections:
1239+
try:
1240+
connection.client_cache.flush()
1241+
except AttributeError:
1242+
# cache is not enabled
1243+
pass
1244+
1245+
def delete_command_from_cache(self, command: str):
1246+
connections = chain(self._available_connections, self._in_use_connections)
1247+
1248+
for connection in connections:
1249+
try:
1250+
connection.client_cache.delete_command(command)
1251+
except AttributeError:
1252+
# cache is not enabled
1253+
pass
1254+
1255+
def invalidate_key_from_cache(self, key: str):
1256+
connections = chain(self._available_connections, self._in_use_connections)
1257+
1258+
for connection in connections:
1259+
try:
1260+
connection.client_cache.invalidate_key(key)
1261+
except AttributeError:
1262+
# cache is not enabled
1263+
pass
1264+
12431265

12441266
class BlockingConnectionPool(ConnectionPool):
12451267
"""

redis/client.py

+34-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
DEFAULT_BLACKLIST,
1111
DEFAULT_EVICTION_POLICY,
1212
DEFAULT_WHITELIST,
13-
_LocalCache,
13+
AbstractCache,
1414
)
1515
from redis._parsers.encoders import Encoder
1616
from redis._parsers.helpers import (
@@ -209,11 +209,11 @@ def __init__(
209209
redis_connect_func=None,
210210
credential_provider: Optional[CredentialProvider] = None,
211211
protocol: Optional[int] = 2,
212-
cache_enable: bool = False,
213-
client_cache: Optional[_LocalCache] = None,
214-
cache_max_size: int = 100,
212+
cache_enabled: bool = False,
213+
client_cache: Optional[AbstractCache] = None,
214+
cache_max_size: int = 10000,
215215
cache_ttl: int = 0,
216-
cache_eviction_policy: str = DEFAULT_EVICTION_POLICY,
216+
cache_policy: str = DEFAULT_EVICTION_POLICY,
217217
cache_blacklist: List[str] = DEFAULT_BLACKLIST,
218218
cache_whitelist: List[str] = DEFAULT_WHITELIST,
219219
) -> None:
@@ -267,11 +267,11 @@ def __init__(
267267
"redis_connect_func": redis_connect_func,
268268
"credential_provider": credential_provider,
269269
"protocol": protocol,
270-
"cache_enable": cache_enable,
270+
"cache_enabled": cache_enabled,
271271
"client_cache": client_cache,
272272
"cache_max_size": cache_max_size,
273273
"cache_ttl": cache_ttl,
274-
"cache_eviction_policy": cache_eviction_policy,
274+
"cache_policy": cache_policy,
275275
"cache_blacklist": cache_blacklist,
276276
"cache_whitelist": cache_whitelist,
277277
}
@@ -589,6 +589,33 @@ def parse_response(self, connection, command_name, **options):
589589
return self.response_callbacks[command_name](response, **options)
590590
return response
591591

592+
def flush_cache(self):
593+
try:
594+
if self.connection:
595+
self.connection.client_cache.flush()
596+
else:
597+
self.connection_pool.flush_cache()
598+
except AttributeError:
599+
pass
600+
601+
def delete_command_from_cache(self, command):
602+
try:
603+
if self.connection:
604+
self.connection.client_cache.delete_command(command)
605+
else:
606+
self.connection_pool.delete_command_from_cache(command)
607+
except AttributeError:
608+
pass
609+
610+
def invalidate_key_from_cache(self, key):
611+
try:
612+
if self.connection:
613+
self.connection.client_cache.invalidate_key(key)
614+
else:
615+
self.connection_pool.invalidate_key_from_cache(key)
616+
except AttributeError:
617+
pass
618+
592619

593620
StrictRedis = Redis
594621

redis/cluster.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ def parse_cluster_myshardid(resp, **options):
167167
"ssl_password",
168168
"unix_socket_path",
169169
"username",
170-
"cache_enable",
170+
"cache_enabled",
171171
"client_cache",
172172
"cache_max_size",
173173
"cache_ttl",
174-
"cache_eviction_policy",
174+
"cache_policy",
175175
"cache_blacklist",
176176
"cache_whitelist",
177177
)

0 commit comments

Comments
 (0)