feat(vision-manager): memory-pressure yield gate

Add a fourth gate to the vision autoscaler poll loop: when host memory
gets tight, proactively SIGTERM-stop the mangalord-vision container so a
transient spike elsewhere (a crawl, CI, a backend burst) can finish
instead of the kernel OOM-killer shooting something stateful.

- read_mem_used_pct: host-wide used% from /proc/meminfo MemAvailable
  (not MemFree), ERR sentinel when MemTotal or MemAvailable is missing.
- mem_check_and_stop: active stop over MEM_HIGH_WATERMARK_PCT + arm a
  restart cooldown; only inspects docker once already over the mark.
- mem_yield_active: start inhibit while cooldown active or used% >=
  MEM_LOW_WATERMARK_PCT (two watermarks = hysteresis).
- Inhibit reuses the existing idle path via pending=0; flip-only logging;
  stop_vision gains a reason arg so idle/MAX_UPTIME/memory-yield stops are
  distinctly logged.
- Faster MEM_POLL_INTERVAL sub-poll between backlog ticks to catch spikes.

Backend verifications V1-V4 (see VISION-MEMORY-YIELD.md) all pass against
the current queue, so no backend changes are needed. RESPECT_CRAWL_MUTEX
is kept for now. New VISION_MEM_* knobs wired through docker-compose.yml
and documented in .env.example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-16 14:33:34 +02:00
parent 790549636f
commit 0a9e727530
4 changed files with 342 additions and 9 deletions

View File

@@ -168,6 +168,14 @@ services:
START_HEALTH_TIMEOUT: ${VISION_START_HEALTH_TIMEOUT:-300}
RESPECT_CRAWL_MUTEX: ${VISION_RESPECT_CRAWL_MUTEX:-1}
MAX_UPTIME: ${VISION_MAX_UPTIME:-0}
# Memory-pressure yield — stop vision when the HOST is short on RAM so a
# spike elsewhere can finish without the kernel OOM-killer. See
# VISION-MEMORY-YIELD.md.
MEM_YIELD_ENABLED: ${VISION_MEM_YIELD_ENABLED:-1}
MEM_HIGH_WATERMARK_PCT: ${VISION_MEM_HIGH_WATERMARK_PCT:-92}
MEM_LOW_WATERMARK_PCT: ${VISION_MEM_LOW_WATERMARK_PCT:-80}
MEM_YIELD_COOLDOWN: ${VISION_MEM_YIELD_COOLDOWN:-300}
MEM_POLL_INTERVAL: ${VISION_MEM_POLL_INTERVAL:-5}
networks:
- default # reach postgres + the vision container by name
- vision-internal # reach the socket-proxy