Skip to content

Commit 562152b

Browse files
authored
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 254c8c0 commit 562152b

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
@@ -29,7 +29,7 @@
2929
DEFAULT_BLACKLIST,
3030
DEFAULT_EVICTION_POLICY,
3131
DEFAULT_WHITELIST,
32-
_LocalCache,
32+
AbstractCache,
3333
)
3434
from redis._parsers.helpers import (
3535
_RedisCallbacks,
@@ -238,11 +238,11 @@ def __init__(
238238
redis_connect_func=None,
239239
credential_provider: Optional[CredentialProvider] = None,
240240
protocol: Optional[int] = 2,
241-
cache_enable: bool = False,
242-
client_cache: Optional[_LocalCache] = None,
241+
cache_enabled: bool = False,
242+
client_cache: Optional[AbstractCache] = None,
243243
cache_max_size: int = 100,
244244
cache_ttl: int = 0,
245-
cache_eviction_policy: str = DEFAULT_EVICTION_POLICY,
245+
cache_policy: str = DEFAULT_EVICTION_POLICY,
246246
cache_blacklist: List[str] = DEFAULT_BLACKLIST,
247247
cache_whitelist: List[str] = DEFAULT_WHITELIST,
248248
):
@@ -294,11 +294,11 @@ def __init__(
294294
"lib_version": lib_version,
295295
"redis_connect_func": redis_connect_func,
296296
"protocol": protocol,
297-
"cache_enable": cache_enable,
297+
"cache_enabled": cache_enabled,
298298
"client_cache": client_cache,
299299
"cache_max_size": cache_max_size,
300300
"cache_ttl": cache_ttl,
301-
"cache_eviction_policy": cache_eviction_policy,
301+
"cache_policy": cache_policy,
302302
"cache_blacklist": cache_blacklist,
303303
"cache_whitelist": cache_whitelist,
304304
}
@@ -671,6 +671,33 @@ async def parse_response(
671671
return await retval if inspect.isawaitable(retval) else retval
672672
return response
673673

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

675702
StrictRedis = Redis
676703

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
@@ -54,6 +54,7 @@
5454
DEFAULT_BLACKLIST,
5555
DEFAULT_EVICTION_POLICY,
5656
DEFAULT_WHITELIST,
57+
AbstractCache,
5758
_LocalCache,
5859
)
5960
from .._parsers import (
@@ -157,11 +158,11 @@ def __init__(
157158
encoder_class: Type[Encoder] = Encoder,
158159
credential_provider: Optional[CredentialProvider] = None,
159160
protocol: Optional[int] = 2,
160-
cache_enable: bool = False,
161-
client_cache: Optional[_LocalCache] = None,
162-
cache_max_size: int = 100,
161+
cache_enabled: bool = False,
162+
client_cache: Optional[AbstractCache] = None,
163+
cache_max_size: int = 10000,
163164
cache_ttl: int = 0,
164-
cache_eviction_policy: str = DEFAULT_EVICTION_POLICY,
165+
cache_policy: str = DEFAULT_EVICTION_POLICY,
165166
cache_blacklist: List[str] = DEFAULT_BLACKLIST,
166167
cache_whitelist: List[str] = DEFAULT_WHITELIST,
167168
):
@@ -221,8 +222,8 @@ def __init__(
221222
if p < 2 or p > 3:
222223
raise ConnectionError("protocol must be either 2 or 3")
223224
self.protocol = protocol
224-
if cache_enable:
225-
_cache = _LocalCache(cache_max_size, cache_ttl, cache_eviction_policy)
225+
if cache_enabled:
226+
_cache = _LocalCache(cache_max_size, cache_ttl, cache_policy)
226227
else:
227228
_cache = None
228229
self.client_cache = client_cache if client_cache is not None else _cache
@@ -699,7 +700,7 @@ def _cache_invalidation_process(
699700
self.client_cache.flush()
700701
else:
701702
for key in data[1]:
702-
self.client_cache.invalidate(str_if_bytes(key))
703+
self.client_cache.invalidate_key(str_if_bytes(key))
703704

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

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

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

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

12451267
class BlockingConnectionPool(ConnectionPool):
12461268
"""

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
}
@@ -592,6 +592,33 @@ def parse_response(self, connection, command_name, **options):
592592
return self.response_callbacks[command_name](response, **options)
593593
return response
594594

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

596623
StrictRedis = Redis
597624

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)