Skip to Content
DocsProductionBounded in-process state

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_users is exceeded, the least-recently-touched user’s bucket is dropped. For StandardBudget that means the user’s running totals reset; for InMemoryMemory their working blocks are deleted.
  • TTL. A user idle longer than user_idle_ttl_seconds has 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