Created
May 3, 2023 08:54
-
-
Save matanper/dc0acd7722f5385bd787801746e82b74 to your computer and use it in GitHub Desktop.
A thread-safe TTL cache function in python, and a thread-safe lock by parameter (hash key)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from cachetools import TTLCache | |
from cachetools.keys import hashkey | |
from parameterized_lock import parameterized_lock | |
def async_threadsafe_ttl_cache(func=None, ttl=60): | |
cache = TTLCache(maxsize=100, ttl=ttl) | |
def decorator(decorated_func): | |
async def wrapper(*args, **kwargs): | |
# Does not use 'session' in the key | |
kwargs_for_key = {i: kwargs[i] for i in kwargs if i != 'session'} | |
key = hashkey(*args, **kwargs_for_key) | |
if key in cache: | |
return cache[key] | |
with parameterized_lock(key): | |
if key in cache: | |
return cache[key] | |
cache[key] = await decorated_func(*args, **kwargs) | |
return cache[key] | |
return wrapper | |
# Allows to call the decorator with or without parenthesis | |
return decorator(func) if callable(func) else decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import threading | |
from contextlib import contextmanager | |
namespace_lock = threading.Lock() | |
namespace = {} | |
counters = {} | |
@contextmanager | |
def parameterized_lock(value, blocking=True, timeout=-1.0): | |
try: | |
with namespace_lock: | |
if value in namespace: | |
counters[value] += 1 | |
else: | |
namespace[value] = threading.Lock() | |
counters[value] = 1 | |
yield namespace[value].acquire(blocking=blocking, timeout=timeout) | |
finally: | |
with namespace_lock: | |
if counters[value] == 1: | |
del counters[value] | |
lock = namespace.pop(value) | |
else: | |
counters[value] -= 1 | |
lock = namespace[value] | |
lock.release() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
not exposing the interface of the function result ;(