Bounded in-process state
StandardBudget (per-user usage totals) and InMemoryMemory
(per-user working blocks) keep per-user_id state in process. Without
bounds, a runaway tenant or a flood of one-off user_ids (for
example, someone sending one request per random UUID) grows that
state until the process runs out of memory. Both are bounded by
default with LRU and idle-TTL eviction; tune the bounds when you know
your tenant count, or turn them off for a fixed set of users.
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