Bounded in-process state
StandardBudget._by_user and InMemoryMemory._blocks hold per-user_id
state in process. Without bounds, a runaway tenant or one-shot
user_id explosion (e.g. someone sending one request per random UUID)
grows the dict until the process OOMs. Both primitives now default to
bounded state with LRU + idle-TTL eviction.
from loomflow import InMemoryMemory
from loomflow.governance.budget import BudgetConfig, StandardBudget
# Defaults: 100k users, 24h idle TTL.
budget = StandardBudget(BudgetConfig()) # implicit bounds
memory = InMemoryMemory() # implicit bounds
# Tune for your workload:
budget = StandardBudget(
BudgetConfig(),
max_users=10_000, # smaller cap for known tenant size
user_idle_ttl_seconds=3_600, # drop idle users after 1h
)
memory = InMemoryMemory(
max_users=10_000,
user_idle_ttl_seconds=3_600,
)
# Or disable bounding entirely (single-tenant or fixed N tenants):
budget = StandardBudget(BudgetConfig(), max_users=None, user_idle_ttl_seconds=None)What eviction does
- LRU. When
max_usersis exceeded, the least-recently-touched user’s bucket is dropped. ForStandardBudgetthat means the user’s running totals reset; forInMemoryMemorytheir working blocks are deleted. - TTL. A user idle longer than
user_idle_ttl_secondshas their bucket dropped on the next access. Lazy: no background thread; the sweep runs on each touch.
Eviction is destructive in process. The bucket’s data is gone,
not flushed elsewhere. Callers needing durable spill-to-disk should
use SqliteMemory or PostgresMemory (which persist working blocks
across restarts) instead of relying on the in-memory bound.
Last updated on