cache.storage.storage_memory

On this page

MemoryStorage

Python
class MemoryStorage(BaseStorage)

Overview

A pure in-process, dict-backed implementation of BaseStorage. It exists for tests, ephemeral workloads, and any deployment where durability across process restarts is not required (StorageBackend.MEMORY). It performs no serialization — entries are stored by reference — so it is the fastest backend but the least safe against process crashes.

Constructor

Python
MemoryStorage() -> None

No parameters. Initializes an empty dict store, an empty expiry-time dict, and a threading.RLock.

Attributes

AttributeTypeMeaning
_storeDict[str, IntelligentCacheEntry]The entries themselves
_expiryDict[str, float]Absolute expiry Unix timestamps, keyed the same as _store
_lockthreading.RLockGuards all access

Methods

get(key: str) -> Optional[IntelligentCacheEntry]

Returns the stored entry, or None if the key is absent or its TTL has elapsed. On expiry, the entry is lazily removed from both _store and _expiry as a side effect of the get() call (there is no background sweep in this class — expiration is checked only on access).

  • Thread safety: thread-safe (RLock).
  • Side effects: may mutate _store/_expiry by deleting an expired key.
  • Complexity: O(1) time; O(1) space.

set(key: str, entry: IntelligentCacheEntry, ttl_s: Optional[float] = None) -> None

Stores entry under key. If ttl_s is truthy (i.e. not None and not 0), records an absolute expiry time of time.time() + ttl_s; otherwise the entry never expires via this mechanism.

!!! warning "A ttl_s of 0 is treated as "no TTL"" Because the check is if ttl_s: rather than if ttl_s is not None:, calling set(key, entry, ttl_s=0) stores the entry with no expiry rather than an immediate one. This is a real, source-verified edge case, not a hypothetical.

delete(key: str) -> bool

Removes key from both _store and _expiry. Returns whether the key was present in _store beforehand.

exists(key: str) -> bool

Implemented as self.get(key) is not None — this means calling exists() on an expired key also triggers its lazy removal, identical to get().

keys_by_tenant(tenant_id: str) -> List[str]

Linear scan of _store.items(), filtering by entry.tenant_id == tenant_id. Does not check expiry — an expired-but-not-yet-accessed entry's key is still returned.

  • Complexity: O(n) in the number of stored entries.

all_keys() -> List[str]

Returns list(self._store.keys()) — again, without filtering expired entries.

total_size_bytes() -> int

Returns sum(e.size_bytes for e in self._store.values()), i.e. the sum of each entry's IntelligentCacheEntry.size_bytes field (computed at entry-construction time, not re-measured here).

flush_tenant(tenant_id: str) -> int

Calls keys_by_tenant() then delete() on each result. Returns the count removed.

  • Complexity: O(n) — one full scan plus one delete per matching key.

close() -> None

No-op (pass). There are no resources to release for an in-memory store.

Thread Safety

Every method that touches _store/_expiry acquires self._lock (an RLock, so re-entrant calls from the same thread — e.g. exists() calling get() — do not deadlock). Not safe across separate OS processes (it is plain in-memory Python state).

Async Support

None natively — MemoryStorage has no async methods. Callers needing async access go through CacheManager, which awaits its own async pipeline and calls storage methods synchronously from within.

Best Practices

  • Use for unit tests, local development, or caches that are acceptable to lose on restart.
  • Do not rely on all_keys() / keys_by_tenant() to reflect only live entries — filter by is_expired() yourself if that distinction matters for your use case, since this backend does not filter expired keys out of listing operations.

Common Mistakes

  • Assuming data survives a process restart — it does not; use SQLiteStorage or RedisStorage for durability.
  • Passing ttl_s=0 expecting immediate expiry — see the warning above; use a very small positive value instead if a near-immediate TTL is truly needed.

Related pages