fxa/cache.py (42 lines of code) (raw):

import time import threading import collections DEFAULT_CACHE_EXPIRY = 300 class MemoryCache: """Simple Memory cache.""" def __init__(self, ttl=DEFAULT_CACHE_EXPIRY): self.ttl = ttl self.cache = {} self.expiry_queue = collections.deque() self.lock = threading.Lock() def get(self, key, now=None): with self.lock: if now is None: now = time.time() self._purge_expired_items(now) value, expires_at = self.cache.get(key, (None, 0)) # There's a small chance that an expired item has # not been removed yet, due to queue ordering weirdness. if expires_at < now: return None return value def set(self, key, value, now=None): with self.lock: if now is None: now = time.time() expires_at = now + self.ttl self.cache[key] = (value, expires_at) self.expiry_queue.append((expires_at, key)) def delete(self, key): if key in self.cache: del self.cache[key] def _purge_expired_items(self, now): while self.expiry_queue: (expires_at, key) = self.expiry_queue[0] if expires_at >= now: break # The item is expired, remove it. # Careful though, it may have been replaced # with a newer value after its expiry was enqueued. self.expiry_queue.popleft() try: item = self.cache.pop(key) except KeyError: pass else: if item[1] > now: # It's been replaced, put it back. self.cache[key] = item