Skip to content

Instantly share code, notes, and snippets.

@matanper
Created May 3, 2023 08:54
Show Gist options
  • Save matanper/dc0acd7722f5385bd787801746e82b74 to your computer and use it in GitHub Desktop.
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)
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
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()
@Morriz
Copy link

Morriz commented Jan 27, 2024

not exposing the interface of the function result ;(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment