Skip to content

feat(gateway): periodic memory trim in idle reaper for long-lived processes - #64591

Closed
aider4ryder wants to merge 2 commits into
NousResearch:mainfrom
aider4ryder:feat/gateway-Idle-memory-trim
Closed

feat(gateway): periodic memory trim in idle reaper for long-lived processes#64591
aider4ryder wants to merge 2 commits into
NousResearch:mainfrom
aider4ryder:feat/gateway-Idle-memory-trim

Conversation

@aider4ryder

Copy link
Copy Markdown

Problem

Long-lived gateway processes (days/weeks without restart) accumulate high RSS even though the memory trim infrastructure from #63708 is deployed. Root cause: trim_memory() is only called at turn completion and agent close, but the idle reaper thread (running every 5 min) never triggers it unless a session is actually reaped.

For active sessions that never hit the 6h idle TTL, gc.collect() + malloc_trim(0) are never invoked, so glibc retains freed heap pages as RSS indefinitely.

Solution

Add trim_memory(reason="idle reaper periodic trim") at the end of _reap_idle_sessions() so every reaper scan (default 300s) releases releasable pages regardless of whether any session was reaped.

This builds on the carried #63708 infrastructure:

  • hermes_cli/mem_trim.py: config-driven malloc_trim(0) with cooldown and kill-switch
  • Post-turn trim in _run_prompt_submit finally block
  • Force trim on AIAgent.close()
  • context.memory_trim config schema (enabled, cooldown_seconds)

Observability

Before (gateway running 28h, PID 115324):

RSS: 1033 MB

Expected after: periodic trim every 5 min should keep RSS bounded. The 60s cooldown in trim_memory prevents redundant trims when turn-completion and reaper fire close together.

Testing

  • 8 mem_trim unit tests (config, cooldown, kill-switch, libc failure)
  • New test_reap_idle_sessions_calls_periodic_trim — verifies trim fires even with zero reaped sessions
  • 324 gateway tests pass, no regressions
PYTHONPATH=. pytest tests/hermes_cli/test_mem_trim.py tests/test_tui_gateway_server.py -x -q
# 10 passed, 324 passed

…cesses

The idle session reaper runs every 5 minutes but only called malloc_trim
when a session was actually reaped. Long-lived gateway processes (days/weeks)
accumulated unreleased RSS because:
1. Python gen2 GC rarely triggers under steady-state allocation
2. glibc retains freed heap pages as RSS until explicitly trimmed
3. Active sessions that never hit the idle TTL never triggered trim

Add trim_memory() at the end of _reap_idle_sessions() so every 5-min
scan releases releasable pages regardless of session reap activity.

Also includes the carried mem_trim infrastructure (PR NousResearch#63708):
- hermes_cli/mem_trim.py: config-driven malloc_trim(0) helper
- tui_gateway/server.py: post-turn trim in _run_prompt_submit finally
- run_agent.py: force trim on agent close
- context.memory_trim config schema (enabled, cooldown_seconds)

Tests: 8 mem_trim unit tests + new periodic trim integration test +
324 gateway tests pass with no regressions.
@alt-glitch alt-glitch added type/perf Performance improvement or optimization comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have labels Jul 14, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for extending the allocator-trim work with the idle-reaper boundary. The current implementation confirms the reaper runs every 300 seconds but has no trim on current main (tui_gateway/server.py:847,875-881,953-966).

Problems

  • The new periodic call reads context.memory_trim on the daemon reaper thread. In remote multi-profile mode, profile selection is a request/turn ContextVar (tui_gateway/server.py:995-1038,8985-8987), while the reaper starts at module initialization (tui_gateway/server.py:953-966). Since the helper calls load_config(), periodic enabled/cooldown values resolve to the launch profile, not a remotely served session profile. The added tests cover turn cleanup but not this periodic configuration path.
  • The PR is conflicting and contains the open allocator-trim implementation plus an unrelated text-verbosity commit. The final memory commit itself is limited to six files (97c5008445fb).

Suggested changes

  • Define the intended process-wide profile/config behavior and add a regression test for the periodic reaper under remote-profile mode.
  • Salvage the focused memory-trim commit only.

Automated hermes-sweeper review.

Comment thread tui_gateway/server.py
# returns releasable pages, preventing unbounded RSS growth over days/weeks.
try:
from hermes_cli.mem_trim import trim_memory

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trim_memory() reads load_config() here, but this daemon reaper has no per-session profile ContextVar. In desktop remote-profile mode, turns bind a profile only in their own thread, so this periodic call always uses the launch profile's context.memory_trim settings. Please define the intended process-wide profile policy and cover this path with a remote-profile test.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 16, 2026
aider4ryder pushed a commit to aider4ryder/hermes-agent that referenced this pull request Jul 19, 2026
…gateway/slash_worker housekeeper + forced-trim logging (PR NousResearch#63708 + NousResearch#64591 enhanced, CSA tier-4 reviewed)
@teknium1 teknium1 added the area/memory Memory subsystem: store, providers, sync, background reviews label Jul 19, 2026
RyderFreeman4Logos added a commit to RyderFreeman4Logos/hermes-agent that referenced this pull request Jul 24, 2026
…gateway/slash_worker housekeeper + forced-trim logging (PR NousResearch#63708 + NousResearch#64591 enhanced, CSA tier-4 reviewed)
RyderFreeman4Logos added a commit to RyderFreeman4Logos/hermes-agent that referenced this pull request Jul 26, 2026
…gateway/slash_worker housekeeper + forced-trim logging (PR NousResearch#63708 + NousResearch#64591 enhanced, CSA tier-4 reviewed)
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Thanks @aider4ryder for kicking off the memory-trim work here! Closing this in favor of your own #66355, which supersedes it with the config-driven version — configurable cooldown, RSS telemetry, and broader lifecycle coverage (gateway housekeeping, slash_worker, agent close) plus unit tests. This branch also picked up an unrelated text-verbosity commit, so consolidating on the clean #66355 keeps review simple. Let's continue the trim discussion over there.

kshitijk4poor pushed a commit that referenced this pull request Aug 2, 2026
…coverage

Add config-driven glibc malloc_trim for long-lived Hermes processes:
- hermes_cli/mem_trim.py: trim_memory() with configurable cooldown,
  RSS snapshot telemetry, and forced-trim INFO logging
- gateway/run.py: periodic trim in gateway housekeeping loop
- tui_gateway/server.py: trim in idle reaper (~every 5 min)
- tui_gateway/slash_worker.py: trim on turn boundary
- run_agent.py: force trim on agent close
- hermes_cli/config.py: context.memory_trim config section
  (enabled, cooldown_seconds, log_every_n, info_log_min_delta_mb)

CSA tier-4 reviewed (4 rounds, 0 HIGH/MEDIUM/CRITICAL remaining).

Supersedes PR #63708 + #64591 with enhanced telemetry and gateway/slash_worker coverage.
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…coverage

Add config-driven glibc malloc_trim for long-lived Hermes processes:
- hermes_cli/mem_trim.py: trim_memory() with configurable cooldown,
  RSS snapshot telemetry, and forced-trim INFO logging
- gateway/run.py: periodic trim in gateway housekeeping loop
- tui_gateway/server.py: trim in idle reaper (~every 5 min)
- tui_gateway/slash_worker.py: trim on turn boundary
- run_agent.py: force trim on agent close
- hermes_cli/config.py: context.memory_trim config section
  (enabled, cooldown_seconds, log_every_n, info_log_min_delta_mb)

CSA tier-4 reviewed (4 rounds, 0 HIGH/MEDIUM/CRITICAL remaining).

Supersedes PR NousResearch#63708 + NousResearch#64591 with enhanced telemetry and gateway/slash_worker coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/memory Memory subsystem: store, providers, sync, background reviews comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants