Skip to main content
The eviction planner names the coldest idle warm caches to reclaim under memory pressure, so a busy gateway on a small host survives instead of being OOM-killed.
Each evicted session is transparently rebuilt from the persisted session store on its next turn — cheap and lossless, with no user-visible impact.
MemoryPressurePolicy sheds new turns under pressure; this planner reclaims memory from idle warm caches. See Admission Control · Memory-aware admission for the sibling admission-side control.

Quick Start

1

On by default on cgroup-aware Linux hosts

No knob to set. On a Linux host that can read a cgroup v1/v2 memory limit, the gateway samples RSS and soft-evicts the coldest rebuildable caches under pressure on its periodic tick. On a host that can’t report a cgroup limit (macOS/Windows, unconstrained host), the sweep is a no-op — legacy behaviour is preserved exactly.
2

Add a persistent store to make eviction rebuildable

A cache is only evicted when its transcript is durably persisted, so it can be losslessly rebuilt on the next turn. A BotSessionManager reads/writes that store via its store= argument. Without a store, nothing is flushed, nothing is evicted, and the sweep is a no-op.
For a full BotOS deployment, configure the store through gateway.yaml (session.persist: true) — see Gateway Session Persistence.
3

Trigger the sweep yourself

Running the session manager standalone (no BotOS)? Call the enactor directly with the concrete cgroup probe:

Which primitive?

Two gateway primitives protect memory from opposite sides — this diagram picks the right one. Use both together for full memory protection: admission gates new work; the planner reclaims from idle warm caches.

API Reference

Three symbols export from praisonaiagents.gateway:

WarmSession

A frozen dataclass carrying a pure fact from the running gateway to the planner.

MemoryPressureProtocol

A @runtime_checkable Protocol the gateway implements over its own process to report the container’s memory ceiling and current RSS.

plan_pressure_evictions

Returns the session_ids to soft-evict, coldest (LRU) first, tie-broken by session_id (stable). It never touches the caches — the gateway is the enactor. Returns [] when RSS is within budget, the budget is unknown (None / <= 0), the budget or RSS is non-finite (NaN/inf), or nothing evictable remains.

Using CgroupMemoryPressure

CgroupMemoryPressure is the concrete, stdlib-only MemoryPressureProtocol the gateway uses to read the real cgroup budget and anonymous RSS.
A nested cgroup’s lower limit is preferred: cgroup_limit_mb() resolves the process’s own cgroup path from /proc/self/cgroup first, then falls back to the filesystem root. Every source degrades safely, so on a host with no cgroup limit the sweep is a no-op.

Wrapper-side wiring

BotSessionManager is the runtime enactor — it snapshots warm caches, calls the pure planner, and soft-evicts the losers.

warm_sessions

Snapshots the warm per-session caches as core WarmSession facts (LRU last_activity, in_flight, flushed). A cache is flushed only when a persistent store is configured and its last store write succeeded.

sweep_under_pressure

Reads the real cgroup budget/RSS from memory_pressure, calls plan_pressure_evictions, and soft-evicts the coldest rebuildable caches LRU-first — re-sampling RSS to shed the minimum. Returns the number evicted. A no-op (returns 0) when no cgroup limit is reported.

How It Works

The gateway samples its own memory, asks the planner for the order to evict, then enacts it — re-sampling RSS as it goes. The planner returns the full ordered list, not a prefix — the gateway re-samples RSS as it evicts and stops as soon as RSS is back within headroom_ratio × budget (default 0.9). It is byte-agnostic by design: it never guesses per-cache sizes, so over-shedding is avoided by the enactor and under-shedding is caught on the next pass. Guards — a victim is skipped entirely (never evicted) when: Degraded modes — the planner never crashes the gateway it protects, returning [] when:

Configuration

The single tunable is headroom_ratio — the fraction of the budget the gateway sheds back down to.

User Interaction Flow

A Fly machine with a 512 MiB cgroup limit hosts a Telegram bot that has accumulated 60 warm session caches. A burst pushes RSS to 490 MiB — over 0.9 × 512 ≈ 460 MiB.
  1. CgroupMemoryPressure reads cgroup_limit_mb() = 512, anon_rss_mb() = 490.
  2. plan_pressure_evictions names the 40 evictable sessions LRU-first (in-flight and unflushed ones are skipped).
  3. sweep_under_pressure evicts down the list, re-sampling RSS, and stops at ~460 MiB.
  4. Those users get their cache rebuilt transparently on their next turn.
No user-visible impact. No OOM kill. On each periodic tick the gateway already runs reap_stale; the new sweep sits alongside it and is a no-op when the host has no cgroup limit:

Common Patterns

Combine with MemoryPressurePolicy for full memory protection — admission gates new work, the planner reclaims from idle warm caches:
Custom MemoryPressureProtocol for non-Linux hosts — supply a macOS/Windows probe:
Tune headroom_ratio for hosts with spiky per-turn allocations — drop to 0.8 to leave more headroom:

Best Practices

Call plan_pressure_evictions and enact the returned list. The decision is pure and unit-tested in isolation — a re-implementation drifts from the guards and degraded-mode returns.
It returns the full LRU-ordered list, not a prefix, and never guesses per-cache sizes. Evict down the list and stop as soon as RSS is back within headroom_ratio × budget. Never guess how much each cache will free.
The planner already skips them — an executing turn would be aborted, and an unflushed transcript isn’t rebuildable yet. Do not override the guards in your enactor.
An empty list means “no work to do” — RSS within budget, unknown/<= 0 budget, non-finite inputs, or nothing evictable. It never means the planner is broken; it never crashes the gateway it protects.

Admission Control · Memory-aware

The admission-side sibling — sheds new turns under RSS pressure with MemoryPressurePolicy

Gateway Reliability Presets

One switch that composes the gateway’s protective primitives

Gateway Session Persistence

Why evicted caches can be losslessly rebuilt — an unflushed transcript is never evicted

Gateway Graceful Drain

The other “avoid dropping live sessions” primitive
Core planner introduced in PraisonAI PR #3805 (fixes #3804). Wrapper-side wiring (CgroupMemoryPressure, BotSessionManager.warm_sessions / sweep_under_pressure, per-turn in-flight and flushed guards) added in PR #4296 (fixes #4294).