cache.storage.storage_memory
On this page
MemoryStorage
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
MemoryStorage() -> None
No parameters. Initializes an empty dict store, an empty expiry-time dict, and a
threading.RLock.
Attributes
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/_expiryby 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 byis_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
SQLiteStorageorRedisStoragefor durability. - Passing
ttl_s=0expecting immediate expiry — see the warning above; use a very small positive value instead if a near-immediate TTL is truly needed.
Related APIs
BaseStorage— the interface implementedbuild_storage()— constructsMemoryStorage()whenconfig.storage_backend == StorageBackend.MEMORYIntelligentCacheEntry— the stored value type