Skip to content

feat(memory): remove the watermark self-recycle - #85

Merged
jghoman merged 3 commits into
mainfrom
jakob/remove-self-recycle
Sep 3, 2026
Merged

feat(memory): remove the watermark self-recycle#85
jghoman merged 3 commits into
mainfrom
jakob/remove-self-recycle

Conversation

@jghoman

@jghoman jghoman commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Why

The dest-connection age sweep (#84) has held prod flat for ~19h: zero watermark events against the prior ~70-90min seppuku cadence. The watermark existed to preempt the Leak-2 OOMKill with a controlled drain; the sweep makes the crossing never happen. The process no longer kills itself on an RSS watermark.

What goes away

  • memory.self_recycle_enabled / self_recycle_rss_fraction / self_recycle_rss_gib / self_recycle_min_uptime_seconds (config + validation + load wiring)
  • resolve_recycle_watermark, _should_self_recycle, _cgroup_memory_limit_gib, _RECYCLE_DRAIN_TIMEOUT_S
  • viaduck_self_recycles_total counter
  • the watermark test section
  • doc references in README / runbook-offset-reset / pyproject / log-consumer-proposal (dated measurement rows restored verbatim with erratum markers)

What stays

  • _read_rss_gib and the per-cycle viaduck_rss_bytes gauge (now the only memory-safety signal — see "deploy notes")
  • SIGTERM graceful drain (60s, unchanged — verified across all three eras)
  • The dest-pool sweep (feat(memory): dest-connection age sweep + leak-2 probes #84), now the sole mitigation
  • Duckling _maybe_recycle (per-pod RSS limit) — single-connection pods have no pool to sweep; the pod is the unit there. Signposted in MemoryConfig.

Deploy notes (from adversarial review)

  • Stale charts: memory.self_recycle_* keys in a deployed chart are now WARN-ignored, never refused (prod's chart still sets them; refusal would CrashLoop). A charts-repo cleanup PR should follow.
  • Alerting: with viaduck_self_recycles_total gone and no in-repo RSS panel, the leading indicator of a new leak class is viaduck_rss_bytes slope / OOMKill — confirm an infra-level alert exists before rollout.
  • Premise honesty: the falsifier run showed the sweep does not zero the local slope; the residual's structure is still unnamed. 19h < the historical ~41h failure horizon. The gamble is acceptable because the terminal failure mode is recoverable (cursor re-read) and the revert is two commits (1440dc3, f34627e + this one).
  • Drain niche: the 300s drain only ever fired on watermark events; SIGTERM's 60s predates and outlives it. If the band breaks in a hot/catch-up regime, the process runs to OOMKill with no drain — the cursor-group scatter the watermark was built to avoid returns as the failure posture.

Validation

1052 tests pass; ruff clean. Both adversarial reviews (lead QE + principal SWE) — convergent P1 (silent stale-key acceptance) fixed and covered by a new test; the P2/P3 docstring/history/metrics-table findings are in the follow-up commit.

The dest-connection age sweep (memory.dest_conn_max_age_seconds, #84)
has held prod flat for ~19h — zero watermark events against a prior
~70-90min seppuku cadence. The leak the watermark papered over is
bounded by the sweep, so the preemptive drain-and-exit goes away: the
process no longer kills itself on an RSS watermark, and the only drain
path left is the ordinary SIGTERM one (60s, unchanged).

Removes: memory.self_recycle_* config, resolve_recycle_watermark,
_should_self_recycle, _cgroup_memory_limit_gib, the
viaduck_self_recycles_total counter, and the watermark test section.
_read_rss_gib stays — the per-cycle viaduck_rss_bytes gauge still uses
it.
README (config sample, failure-mode table, metrics table, Deployment
section), runbook-offset-reset startup check, pyproject comment, and
the log-consumer proposal's point-in-time references. The section now
documents the dest-connection age sweep (the replacement mitigation).

Duckling._maybe_recycle (the per-destination-process RSS-limit exit)
is intentionally untouched: it is a different component with no pool
to sweep — its pod IS the unit of recycling.
Adversarial review (QE + SWE) of the removal:
- config.load: retired memory.self_recycle_* keys now WARN-ignored
  (delivery-section pattern), never refused — prod's chart still carries
  them; silence would let an operator believe the deleted bound exists.
  memory section is None-tolerant ('memory:' with all comments parses
  to None).
- log-consumer-proposal.md: dated 2026-08-15 measurement row restored
  verbatim + erratum (dated tables are immutable); same for the §reliable
  design-assumption line.
- _read_rss_gib docstring now cites the gauge, not the deleted watermark.
- MemoryConfig signpost: removal record + revert pointer + the duckling
  asymmetry. source-conn probe comment records its FALSIFIED verdict.
- README metrics table gains viaduck_rss_bytes + viaduck_dest_conn_sweeps_total.
- test: stale-chart warn-not-refuse coverage.
@jghoman
jghoman merged commit 9a2e0b5 into main Sep 3, 2026
16 checks passed
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.

1 participant