Commit b506dd14 authored by Florian Zimmermann's avatar Florian Zimmermann

allow to pass a value to get_or_set

fixes issue #124
parent 9382347b
...@@ -131,16 +131,18 @@ Cache Methods Provided by django-redis-cache ...@@ -131,16 +131,18 @@ Cache Methods Provided by django-redis-cache
:param version: Version of the keys :param version: Version of the keys
.. function:: get_or_set(self, key, func[, timeout=None, lock_timeout=None, stale_cache_timeout=None]): .. function:: get_or_set(self, key, default[, timeout=None, lock_timeout=None, stale_cache_timeout=None]):
Get a value from the cache or call ``func`` to set it and return it. Get a value from the cache or use ``default`` to set it and return it.
If ``default`` is a callable, call it without arguments and store its return value in the cache instead.
This implementation is slightly more advanced that Django's. It provides thundering herd This implementation is slightly more advanced that Django's. It provides thundering herd
protection, which prevents multiple threads/processes from calling the value-generating protection, which prevents multiple threads/processes from calling the value-generating
function at the same time. function at the same time.
:param key: Location of the value :param key: Location of the value
:param func: Callable used to set the value if key does not exist. :param default: Used to set the value if key does not exist.
:param timeout: Time in seconds that value at key is considered fresh. :param timeout: Time in seconds that value at key is considered fresh.
:type timeout: Number of seconds or None :type timeout: Number of seconds or None
:param lock_timeout: Time in seconds that the lock will stay active and prevent other threads from acquiring the lock. :param lock_timeout: Time in seconds that the lock will stay active and prevent other threads from acquiring the lock.
......
...@@ -410,11 +410,13 @@ class BaseRedisCache(BaseCache): ...@@ -410,11 +410,13 @@ class BaseRedisCache(BaseCache):
self, self,
client, client,
key, key,
func, default,
timeout=DEFAULT_TIMEOUT, timeout=DEFAULT_TIMEOUT,
lock_timeout=None, lock_timeout=None,
stale_cache_timeout=None): stale_cache_timeout=None):
"""Get a value from the cache or call ``func`` to set it and return it. """Get a value from the cache or use ``default`` to set it and return it.
If ``default`` is a callable, call it without arguments and store its return value in the cache instead.
This implementation is slightly more advanced that Django's. It provides thundering herd This implementation is slightly more advanced that Django's. It provides thundering herd
protection, which prevents multiple threads/processes from calling the value-generating protection, which prevents multiple threads/processes from calling the value-generating
...@@ -429,9 +431,6 @@ class BaseRedisCache(BaseCache): ...@@ -429,9 +431,6 @@ class BaseRedisCache(BaseCache):
expired. If ``None`` is specified, the stale value will remain indefinitely. expired. If ``None`` is specified, the stale value will remain indefinitely.
""" """
if not callable(func):
raise Exception("Must pass in a callable")
lock_key = "__lock__" + key lock_key = "__lock__" + key
fresh_key = "__fresh__" + key fresh_key = "__fresh__" + key
...@@ -448,7 +447,7 @@ class BaseRedisCache(BaseCache): ...@@ -448,7 +447,7 @@ class BaseRedisCache(BaseCache):
if acquired: if acquired:
try: try:
value = func() value = default() if callable(default) else default
except Exception: except Exception:
raise raise
else: else:
......
...@@ -8,11 +8,8 @@ class RedisDummyCache(DummyCache): ...@@ -8,11 +8,8 @@ class RedisDummyCache(DummyCache):
def delete_pattern(self, pattern, version=None): def delete_pattern(self, pattern, version=None):
return None return None
def get_or_set(self, key, func, timeout=None): def get_or_set(self, key, default, timeout=None):
if not callable(func): return default() if callable(default) else default
raise Exception("Must pass in a callable")
return func()
def reinsert_keys(self): def reinsert_keys(self):
return None return None
......
...@@ -486,7 +486,7 @@ class BaseRedisTestCase(SetupMixin): ...@@ -486,7 +486,7 @@ class BaseRedisTestCase(SetupMixin):
self.assertEqual(self.cache.get('b'), 'b') self.assertEqual(self.cache.get('b'), 'b')
self.assertGreater(self.cache.ttl('a'), 1) self.assertGreater(self.cache.ttl('a'), 1)
def test_get_or_set(self): def test_get_or_set_with_callable(self):
def expensive_function(): def expensive_function():
expensive_function.num_calls += 1 expensive_function.num_calls += 1
...@@ -511,6 +511,15 @@ class BaseRedisTestCase(SetupMixin): ...@@ -511,6 +511,15 @@ class BaseRedisTestCase(SetupMixin):
self.assertEqual(expensive_function.num_calls, 2) self.assertEqual(expensive_function.num_calls, 2)
self.assertEqual(value, 42) self.assertEqual(value, 42)
def test_get_or_set_with_value(self):
self.assertEqual(self.cache.get_or_set('a', 42, 1), 42)
self.assertEqual(self.cache.get_or_set('a', 43, 1), 42)
self.assertEqual(self.cache.get_or_set('a', 44, 1), 42)
time.sleep(2)
self.assertEqual(self.cache.get_or_set('a', 45, 1), 45)
self.assertEqual(self.cache.get_or_set('a', 46, 1), 45)
self.assertEqual(self.cache.get_or_set('a', 47, 1), 45)
def test_get_or_set_serving_from_stale_value(self): def test_get_or_set_serving_from_stale_value(self):
def expensive_function(x): def expensive_function(x):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment