123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- "Stampede barrier implementation."
-
- import functools as ft
- import math
- import random
- import tempfile
- import time
-
- from .core import Cache, ENOVAL
-
-
- class StampedeBarrier(object):
- """Stampede barrier mitigates cache stampedes.
-
- Cache stampedes are also known as dog-piling, cache miss storm, cache
- choking, or the thundering herd problem.
-
- Based on research by Vattani, A.; Chierichetti, F.; Lowenstein, K. (2015),
- Optimal Probabilistic Cache Stampede Prevention,
- VLDB, pp. 886?897, ISSN 2150-8097
-
- Example:
-
- ```python
- stampede_barrier = StampedeBarrier('/tmp/user_data', expire=3)
-
- @stampede_barrier
- def load_user_info(user_id):
- return database.lookup_user_info_by_id(user_id)
- ```
-
- """
- # pylint: disable=too-few-public-methods
- def __init__(self, cache=None, expire=None):
- if isinstance(cache, Cache):
- pass
- elif cache is None:
- cache = Cache(tempfile.mkdtemp())
- else:
- cache = Cache(cache)
-
- self._cache = cache
- self._expire = expire
-
- def __call__(self, func):
- cache = self._cache
- expire = self._expire
-
- @ft.wraps(func)
- def wrapper(*args, **kwargs):
- "Wrapper function to cache function result."
- key = (args, kwargs)
-
- try:
- result, expire_time, delta = cache.get(
- key, default=ENOVAL, expire_time=True, tag=True
- )
-
- if result is ENOVAL:
- raise KeyError
-
- now = time.time()
- ttl = expire_time - now
-
- if (-delta * math.log(random.random())) < ttl:
- return result
-
- except KeyError:
- pass
-
- now = time.time()
- result = func(*args, **kwargs)
- delta = time.time() - now
- cache.set(key, result, expire=expire, tag=delta)
-
- return result
-
- return wrapper
|