|
@@ -2,6 +2,8 @@
|
|
|
import time
|
|
|
import warnings
|
|
|
|
|
|
+from asgiref.sync import sync_to_async
|
|
|
+
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
|
from django.utils.module_loading import import_string
|
|
|
|
|
@@ -130,6 +132,9 @@ class BaseCache:
|
|
|
"""
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide an add() method')
|
|
|
|
|
|
+ async def aadd(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
+ return await sync_to_async(self.add, thread_sensitive=True)(key, value, timeout, version)
|
|
|
+
|
|
|
def get(self, key, default=None, version=None):
|
|
|
"""
|
|
|
Fetch a given key from the cache. If the key does not exist, return
|
|
@@ -137,6 +142,9 @@ class BaseCache:
|
|
|
"""
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a get() method')
|
|
|
|
|
|
+ async def aget(self, key, default=None, version=None):
|
|
|
+ return await sync_to_async(self.get, thread_sensitive=True)(key, default, version)
|
|
|
+
|
|
|
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
"""
|
|
|
Set a value in the cache. If timeout is given, use that timeout for the
|
|
@@ -144,6 +152,9 @@ class BaseCache:
|
|
|
"""
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a set() method')
|
|
|
|
|
|
+ async def aset(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
+ return await sync_to_async(self.set, thread_sensitive=True)(key, value, timeout, version)
|
|
|
+
|
|
|
def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
"""
|
|
|
Update the key's expiry time using timeout. Return True if successful
|
|
@@ -151,6 +162,9 @@ class BaseCache:
|
|
|
"""
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a touch() method')
|
|
|
|
|
|
+ async def atouch(self, key, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
+ return await sync_to_async(self.touch, thread_sensitive=True)(key, timeout, version)
|
|
|
+
|
|
|
def delete(self, key, version=None):
|
|
|
"""
|
|
|
Delete a key from the cache and return whether it succeeded, failing
|
|
@@ -158,6 +172,9 @@ class BaseCache:
|
|
|
"""
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a delete() method')
|
|
|
|
|
|
+ async def adelete(self, key, version=None):
|
|
|
+ return await sync_to_async(self.delete, thread_sensitive=True)(key, version)
|
|
|
+
|
|
|
def get_many(self, keys, version=None):
|
|
|
"""
|
|
|
Fetch a bunch of keys from the cache. For certain backends (memcached,
|
|
@@ -173,6 +190,15 @@ class BaseCache:
|
|
|
d[k] = val
|
|
|
return d
|
|
|
|
|
|
+ async def aget_many(self, keys, version=None):
|
|
|
+ """See get_many()."""
|
|
|
+ d = {}
|
|
|
+ for k in keys:
|
|
|
+ val = await self.aget(k, self._missing_key, version=version)
|
|
|
+ if val is not self._missing_key:
|
|
|
+ d[k] = val
|
|
|
+ return d
|
|
|
+
|
|
|
def get_or_set(self, key, default, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
"""
|
|
|
Fetch a given key from the cache. If the key does not exist,
|
|
@@ -192,12 +218,30 @@ class BaseCache:
|
|
|
return self.get(key, default, version=version)
|
|
|
return val
|
|
|
|
|
|
+ async def aget_or_set(self, key, default, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
+ """See get_or_set()."""
|
|
|
+ val = await self.aget(key, self._missing_key, version=version)
|
|
|
+ if val is self._missing_key:
|
|
|
+ if callable(default):
|
|
|
+ default = default()
|
|
|
+ await self.aadd(key, default, timeout=timeout, version=version)
|
|
|
+ # Fetch the value again to avoid a race condition if another caller
|
|
|
+ # added a value between the first aget() and the aadd() above.
|
|
|
+ return await self.aget(key, default, version=version)
|
|
|
+ return val
|
|
|
+
|
|
|
def has_key(self, key, version=None):
|
|
|
"""
|
|
|
Return True if the key is in the cache and has not expired.
|
|
|
"""
|
|
|
return self.get(key, self._missing_key, version=version) is not self._missing_key
|
|
|
|
|
|
+ async def ahas_key(self, key, version=None):
|
|
|
+ return (
|
|
|
+ await self.aget(key, self._missing_key, version=version)
|
|
|
+ is not self._missing_key
|
|
|
+ )
|
|
|
+
|
|
|
def incr(self, key, delta=1, version=None):
|
|
|
"""
|
|
|
Add delta to value in the cache. If the key does not exist, raise a
|
|
@@ -210,6 +254,15 @@ class BaseCache:
|
|
|
self.set(key, new_value, version=version)
|
|
|
return new_value
|
|
|
|
|
|
+ async def aincr(self, key, delta=1, version=None):
|
|
|
+ """See incr()."""
|
|
|
+ value = await self.aget(key, self._missing_key, version=version)
|
|
|
+ if value is self._missing_key:
|
|
|
+ raise ValueError("Key '%s' not found" % key)
|
|
|
+ new_value = value + delta
|
|
|
+ await self.aset(key, new_value, version=version)
|
|
|
+ return new_value
|
|
|
+
|
|
|
def decr(self, key, delta=1, version=None):
|
|
|
"""
|
|
|
Subtract delta from value in the cache. If the key does not exist, raise
|
|
@@ -217,6 +270,9 @@ class BaseCache:
|
|
|
"""
|
|
|
return self.incr(key, -delta, version=version)
|
|
|
|
|
|
+ async def adecr(self, key, delta=1, version=None):
|
|
|
+ return await self.aincr(key, -delta, version=version)
|
|
|
+
|
|
|
def __contains__(self, key):
|
|
|
"""
|
|
|
Return True if the key is in the cache and has not expired.
|
|
@@ -242,6 +298,11 @@ class BaseCache:
|
|
|
self.set(key, value, timeout=timeout, version=version)
|
|
|
return []
|
|
|
|
|
|
+ async def aset_many(self, data, timeout=DEFAULT_TIMEOUT, version=None):
|
|
|
+ for key, value in data.items():
|
|
|
+ await self.aset(key, value, timeout=timeout, version=version)
|
|
|
+ return []
|
|
|
+
|
|
|
def delete_many(self, keys, version=None):
|
|
|
"""
|
|
|
Delete a bunch of values in the cache at once. For certain backends
|
|
@@ -251,10 +312,17 @@ class BaseCache:
|
|
|
for key in keys:
|
|
|
self.delete(key, version=version)
|
|
|
|
|
|
+ async def adelete_many(self, keys, version=None):
|
|
|
+ for key in keys:
|
|
|
+ await self.adelete(key, version=version)
|
|
|
+
|
|
|
def clear(self):
|
|
|
"""Remove *all* values from the cache at once."""
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a clear() method')
|
|
|
|
|
|
+ async def aclear(self):
|
|
|
+ return await sync_to_async(self.clear, thread_sensitive=True)()
|
|
|
+
|
|
|
def incr_version(self, key, delta=1, version=None):
|
|
|
"""
|
|
|
Add delta to the cache version for the supplied key. Return the new
|
|
@@ -271,6 +339,19 @@ class BaseCache:
|
|
|
self.delete(key, version=version)
|
|
|
return version + delta
|
|
|
|
|
|
+ async def aincr_version(self, key, delta=1, version=None):
|
|
|
+ """See incr_version()."""
|
|
|
+ if version is None:
|
|
|
+ version = self.version
|
|
|
+
|
|
|
+ value = await self.aget(key, self._missing_key, version=version)
|
|
|
+ if value is self._missing_key:
|
|
|
+ raise ValueError("Key '%s' not found" % key)
|
|
|
+
|
|
|
+ await self.aset(key, value, version=version + delta)
|
|
|
+ await self.adelete(key, version=version)
|
|
|
+ return version + delta
|
|
|
+
|
|
|
def decr_version(self, key, delta=1, version=None):
|
|
|
"""
|
|
|
Subtract delta from the cache version for the supplied key. Return the
|
|
@@ -278,10 +359,16 @@ class BaseCache:
|
|
|
"""
|
|
|
return self.incr_version(key, -delta, version)
|
|
|
|
|
|
+ async def adecr_version(self, key, delta=1, version=None):
|
|
|
+ return await self.aincr_version(key, -delta, version)
|
|
|
+
|
|
|
def close(self, **kwargs):
|
|
|
"""Close the cache connection"""
|
|
|
pass
|
|
|
|
|
|
+ async def aclose(self, **kwargs):
|
|
|
+ pass
|
|
|
+
|
|
|
|
|
|
def memcache_key_warnings(key):
|
|
|
if len(key) > MEMCACHE_MAX_KEY_LENGTH:
|