Skip to content

Bound operator memory: GC terminal sessions and drop the in-memory audit mirror - #21

Merged
josephschorr merged 1 commit into
mainfrom
fix-operator-memory-oom
Oct 6, 2026
Merged

josephschorr merged 1 commit into
mainfrom
fix-operator-memory-oom

Conversation

@josephschorr

Copy link
Copy Markdown
Member

Summary

On long-lived clusters the operator steadily climbed to its memory limit, was OOMKilled, and crash-looped. The memory wasn't going to its actual work — it came from two retain-forever-by-design sources, so the limit was reached within days and reset on each restart.

Two causes, two fixes

1. Terminal sessions (and their ToolCalls) were never garbage-collected.
The pod reap and PVC reclaim free pods and scratch storage but intentionally keep the AgentSession record — and with it every ToolCall it owns. So every session and every tool call ever run stayed resident in etcd and in the operator's informer cache, growing without bound.

This adds a session-lifetime GC (--session-gc-after, default 30d): a terminal session past the window is deleted, which cascade-collects its owned ToolCalls, pods and Secrets. The append-only audit log is untouched — it lives in the durable memory backend and session teardown already refuses to delete it — so the record survives; only the CR and its ephemera go. This is the retention the existing reap logic already anticipated.

2. A redundant in-memory copy of the entire audit log.
With a Postgres memory backend, every append-only write was also mirrored into an in-memory backend, while reads were served only from Postgres. That mirror was never read and could never be freed, so it grew with the whole cluster's audit history for the operator's lifetime.

This uses the Postgres backend directly. Reads are byte-identical to what the mirror already served, at none of the RAM.

Testing

  • New regression tests for both changes: the session GC (disabled / non-terminal / within-window / past-window, plus a reconcile-level test that it is wired into the terminal path) and the direct Postgres backend (no in-memory shadow).
  • unit, integration, and e2e suites pass.

Operational notes

  • --session-gc-after defaults to 30 days and is tunable (0 disables). On first rollout to an existing cluster it will collect sessions already older than the window.
  • Deleting a session gives up its on-record truncation anchor (status.auditChainHeads); the audit records and their signing-key witness remain durable, so signature verification is unaffected.

…mirror

The operator OOMKilled on long-lived clusters from two unbounded,
retain-forever-by-design sources, neither of which serves its running work.

1. Terminal AgentSessions and the ToolCalls they own were never deleted. The
   pod reap and PVC reclaim free pods and scratch volumes but keep the CR, so
   every session and every ToolCall ever created stayed resident in etcd and in
   the operator's informer cache (observed: 10k+ ToolCalls, oldest months old).
   Add a session-lifetime GC (--session-gc-after, default 30d): a terminal
   session past the window is deleted, cascade-collecting its ToolCalls, pods
   and Secrets. finalize keeps the append-only audit log (it lives in the
   durable backend; DeleteScope refuses append-only kinds), so the record
   survives — only the CR and its owned ephemera go. This is the retention the
   reap already anticipated ("until retention GCs the AgentSession").

2. Under MEMORY_BACKEND=postgres the ShadowBackend dual-wrote every append-only
   entry into an in-memory backend as well, while serving reads only from
   postgres. That inmem mirror was never read and scope-delete can never free it
   (append-only scopes are undeletable), so it grew with the whole cluster's
   audit history for the operator's lifetime. Use the postgres backend directly;
   reads are byte-identical to what the shadow already served from its secondary.
@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openagentprimitives Ready Ready Preview Oct 5, 2026 10:54pm UTC

Request Review

@josephschorr
josephschorr merged commit 62008d1 into main Oct 6, 2026
8 of 9 checks passed

This branch was successfully deployed

1 active deployment
Preview — 3ea02b2f Deployed Oct 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants