4
4
from abc import ABC , abstractmethod
5
5
from collections import OrderedDict , defaultdict
6
6
from enum import Enum
7
- from typing import List
7
+ from typing import List , Sequence , Union
8
8
9
9
from redis .typing import KeyT , ResponseT
10
10
11
- DEFAULT_EVICTION_POLICY = "lru"
12
11
12
+ class EvictionPolicy (Enum ):
13
+ LRU = "lru"
14
+ LFU = "lfu"
15
+ RANDOM = "random"
13
16
14
- DEFAULT_BLACKLIST = [
17
+
18
+ DEFAULT_EVICTION_POLICY = EvictionPolicy .LRU
19
+
20
+ DEFAULT_DENY_LIST = [
15
21
"BF.CARD" ,
16
22
"BF.DEBUG" ,
17
23
"BF.EXISTS" ,
71
77
"TTL" ,
72
78
]
73
79
74
-
75
- DEFAULT_WHITELIST = [
80
+ DEFAULT_ALLOW_LIST = [
76
81
"BITCOUNT" ,
77
82
"BITFIELD_RO" ,
78
83
"BITPOS" ,
155
160
_ACCESS_COUNT = "access_count"
156
161
157
162
158
- class EvictionPolicy (Enum ):
159
- LRU = "lru"
160
- LFU = "lfu"
161
- RANDOM = "random"
162
-
163
-
164
163
class AbstractCache (ABC ):
165
164
"""
166
165
An abstract base class for client caching implementations.
167
166
If you want to implement your own cache you must support these methods.
168
167
"""
169
168
170
169
@abstractmethod
171
- def set (self , command : str , response : ResponseT , keys_in_command : List [KeyT ]):
170
+ def set (
171
+ self ,
172
+ command : Union [str , Sequence [str ]],
173
+ response : ResponseT ,
174
+ keys_in_command : List [KeyT ],
175
+ ):
172
176
pass
173
177
174
178
@abstractmethod
175
- def get (self , command : str ) -> ResponseT :
179
+ def get (self , command : Union [ str , Sequence [ str ]] ) -> ResponseT :
176
180
pass
177
181
178
182
@abstractmethod
179
- def delete_command (self , command : str ):
183
+ def delete_command (self , command : Union [ str , Sequence [ str ]] ):
180
184
pass
181
185
182
186
@abstractmethod
183
- def delete_many (self , commands ):
187
+ def delete_commands (self , commands : List [ Union [ str , Sequence [ str ]]] ):
184
188
pass
185
189
186
190
@abstractmethod
@@ -215,7 +219,6 @@ def __init__(
215
219
max_size : int = 10000 ,
216
220
ttl : int = 0 ,
217
221
eviction_policy : EvictionPolicy = DEFAULT_EVICTION_POLICY ,
218
- ** kwargs ,
219
222
):
220
223
self .max_size = max_size
221
224
self .ttl = ttl
@@ -224,12 +227,17 @@ def __init__(
224
227
self .key_commands_map = defaultdict (set )
225
228
self .commands_ttl_list = []
226
229
227
- def set (self , command : str , response : ResponseT , keys_in_command : List [KeyT ]):
230
+ def set (
231
+ self ,
232
+ command : Union [str , Sequence [str ]],
233
+ response : ResponseT ,
234
+ keys_in_command : List [KeyT ],
235
+ ):
228
236
"""
229
237
Set a redis command and its response in the cache.
230
238
231
239
Args:
232
- command (str): The redis command.
240
+ command (Union[ str, Sequence[str]] ): The redis command.
233
241
response (ResponseT): The response associated with the command.
234
242
keys_in_command (List[KeyT]): The list of keys used in the command.
235
243
"""
@@ -244,12 +252,12 @@ def set(self, command: str, response: ResponseT, keys_in_command: List[KeyT]):
244
252
self ._update_key_commands_map (keys_in_command , command )
245
253
self .commands_ttl_list .append (command )
246
254
247
- def get (self , command : str ) -> ResponseT :
255
+ def get (self , command : Union [ str , Sequence [ str ]] ) -> ResponseT :
248
256
"""
249
257
Get the response for a redis command from the cache.
250
258
251
259
Args:
252
- command (str): The redis command.
260
+ command (Union[ str, Sequence[str]] ): The redis command.
253
261
254
262
Returns:
255
263
ResponseT: The response associated with the command, or None if the command is not in the cache. # noqa
@@ -261,34 +269,42 @@ def get(self, command: str) -> ResponseT:
261
269
self ._update_access (command )
262
270
return copy .deepcopy (self .cache [command ]["response" ])
263
271
264
- def delete_command (self , command : str ):
272
+ def delete_command (self , command : Union [ str , Sequence [ str ]] ):
265
273
"""
266
274
Delete a redis command and its metadata from the cache.
267
275
268
276
Args:
269
- command (str): The redis command to be deleted.
277
+ command (Union[ str, Sequence[str]] ): The redis command to be deleted.
270
278
"""
271
279
if command in self .cache :
272
280
keys_in_command = self .cache [command ].get ("keys" )
273
281
self ._del_key_commands_map (keys_in_command , command )
274
282
self .commands_ttl_list .remove (command )
275
283
del self .cache [command ]
276
284
277
- def delete_many (self , commands ):
278
- pass
285
+ def delete_commands (self , commands : List [Union [str , Sequence [str ]]]):
286
+ """
287
+ Delete multiple commands and their metadata from the cache.
288
+
289
+ Args:
290
+ commands (List[Union[str, Sequence[str]]]): The list of commands to be
291
+ deleted.
292
+ """
293
+ for command in commands :
294
+ self .delete_command (command )
279
295
280
296
def flush (self ):
281
297
"""Clear the entire cache, removing all redis commands and metadata."""
282
298
self .cache .clear ()
283
299
self .key_commands_map .clear ()
284
300
self .commands_ttl_list = []
285
301
286
- def _is_expired (self , command : str ) -> bool :
302
+ def _is_expired (self , command : Union [ str , Sequence [ str ]] ) -> bool :
287
303
"""
288
304
Check if a redis command has expired based on its time-to-live.
289
305
290
306
Args:
291
- command (str): The redis command.
307
+ command (Union[ str, Sequence[str]] ): The redis command.
292
308
293
309
Returns:
294
310
bool: True if the command has expired, False otherwise.
@@ -297,56 +313,60 @@ def _is_expired(self, command: str) -> bool:
297
313
return False
298
314
return time .monotonic () - self .cache [command ]["ctime" ] > self .ttl
299
315
300
- def _update_access (self , command : str ):
316
+ def _update_access (self , command : Union [ str , Sequence [ str ]] ):
301
317
"""
302
318
Update the access information for a redis command based on the eviction policy.
303
319
304
320
Args:
305
- command (str): The redis command.
321
+ command (Union[ str, Sequence[str]] ): The redis command.
306
322
"""
307
- if self .eviction_policy == EvictionPolicy .LRU . value :
323
+ if self .eviction_policy == EvictionPolicy .LRU :
308
324
self .cache .move_to_end (command )
309
- elif self .eviction_policy == EvictionPolicy .LFU . value :
325
+ elif self .eviction_policy == EvictionPolicy .LFU :
310
326
self .cache [command ]["access_count" ] = (
311
327
self .cache .get (command , {}).get ("access_count" , 0 ) + 1
312
328
)
313
329
self .cache .move_to_end (command )
314
- elif self .eviction_policy == EvictionPolicy .RANDOM . value :
330
+ elif self .eviction_policy == EvictionPolicy .RANDOM :
315
331
pass # Random eviction doesn't require updates
316
332
317
333
def _evict (self ):
318
334
"""Evict a redis command from the cache based on the eviction policy."""
319
335
if self ._is_expired (self .commands_ttl_list [0 ]):
320
336
self .delete_command (self .commands_ttl_list [0 ])
321
- elif self .eviction_policy == EvictionPolicy .LRU . value :
337
+ elif self .eviction_policy == EvictionPolicy .LRU :
322
338
self .cache .popitem (last = False )
323
- elif self .eviction_policy == EvictionPolicy .LFU . value :
339
+ elif self .eviction_policy == EvictionPolicy .LFU :
324
340
min_access_command = min (
325
341
self .cache , key = lambda k : self .cache [k ].get ("access_count" , 0 )
326
342
)
327
343
self .cache .pop (min_access_command )
328
- elif self .eviction_policy == EvictionPolicy .RANDOM . value :
344
+ elif self .eviction_policy == EvictionPolicy .RANDOM :
329
345
random_command = random .choice (list (self .cache .keys ()))
330
346
self .cache .pop (random_command )
331
347
332
- def _update_key_commands_map (self , keys : List [KeyT ], command : str ):
348
+ def _update_key_commands_map (
349
+ self , keys : List [KeyT ], command : Union [str , Sequence [str ]]
350
+ ):
333
351
"""
334
352
Update the key_commands_map with command that uses the keys.
335
353
336
354
Args:
337
355
keys (List[KeyT]): The list of keys used in the command.
338
- command (str): The redis command.
356
+ command (Union[ str, Sequence[str]] ): The redis command.
339
357
"""
340
358
for key in keys :
341
359
self .key_commands_map [key ].add (command )
342
360
343
- def _del_key_commands_map (self , keys : List [KeyT ], command : str ):
361
+ def _del_key_commands_map (
362
+ self , keys : List [KeyT ], command : Union [str , Sequence [str ]]
363
+ ):
344
364
"""
345
365
Remove a redis command from the key_commands_map.
346
366
347
367
Args:
348
368
keys (List[KeyT]): The list of keys used in the redis command.
349
- command (str): The redis command.
369
+ command (Union[ str, Sequence[str]] ): The redis command.
350
370
"""
351
371
for key in keys :
352
372
self .key_commands_map [key ].remove (command )
0 commit comments