fix(mem): 5s force floor so delegate-batch closes don't stack trims

Efficiency-pass follow-up on the #66355 salvage: force=True bypassed the
cooldown entirely, and AIAgent.close() fires a forced trim for EVERY
in-process child subagent close (delegate_tool child.close(), parent
close step 5). A delegate batch of N children closing back-to-back in
the gateway process stacked N+1 uncooled full gc.collect()+malloc_trim
passes (50-500ms each with a large live heap). Forced trims now honor a
5s floor — bursts coalesce, the parent's final close-trim still fires.
Guard test mutation-checked (floor zeroed -> test fails).
This commit is contained in:
kshitij
2026-08-02 22:41:02 +05:30
parent 3fad8fdc45
commit c575351d9a
2 changed files with 49 additions and 1 deletions

View File

@@ -200,6 +200,18 @@ def trim_memory(
)
if not force and _last_trim_monotonic and now - _last_trim_monotonic < cooldown:
return False
# Even forced trims honor a short floor: AIAgent.close() forces a trim,
# and delegate batches close N child subagents back-to-back in the SAME
# process — without a floor that stacks N+1 uncooled full gc.collect()
# passes (50-500ms each in a large gateway process). 5s coalesces the
# burst while keeping the parent's final close-trim effective.
_FORCE_FLOOR_SECONDS = 5.0
if (
force
and _last_trim_monotonic
and now - _last_trim_monotonic < _FORCE_FLOOR_SECONDS
):
return False
# Record the attempt before calling into libc so repeated failures do not
# turn every turn boundary into an expensive full collection.
_last_trim_monotonic = now

View File

@@ -108,7 +108,10 @@ def test_force_logs_even_when_periodic_log_sampling_skips(monkeypatch, caplog):
monkeypatch.setattr(mem_trim.gc, "collect", lambda: None)
monkeypatch.setattr(mem_trim, "_malloc_trim", lambda _pad: 1)
monkeypatch.setattr(mem_trim, "_config_settings", lambda: (True, 0.0, 99, 1.0))
monkeypatch.setattr(mem_trim.time, "monotonic", lambda: 100.0)
# Two ticks: the forced call comes after the 5s force floor so it runs
# (the floor exists to coalesce burst closes, not to mute logging).
_ticks = iter([100.0, 110.0])
monkeypatch.setattr(mem_trim.time, "monotonic", lambda: next(_ticks, 110.0))
monkeypatch.setattr(
mem_trim,
"collect_memory_snapshot",
@@ -178,3 +181,36 @@ def test_libc_failure_is_fail_open_and_rate_limited(monkeypatch):
assert mem_trim._last_trim_monotonic == 100.0
assert mem_trim.trim_memory(cooldown_seconds=60) is False
assert trim.call_count == 1
def test_force_floor_coalesces_burst_closes(monkeypatch):
"""A delegate batch closes N child agents back-to-back, each forcing a
trim — the short force floor must coalesce the burst instead of stacking
N uncooled full gc.collect() passes in the same process."""
collect = Mock()
trim = Mock(return_value=1)
monkeypatch.setattr(mem_trim.gc, "collect", collect)
monkeypatch.setattr(mem_trim, "_malloc_trim", trim)
monkeypatch.setattr(mem_trim, "_config_settings", lambda: (True, 0.0, 1, 0.0))
monkeypatch.setattr(
mem_trim,
"collect_memory_snapshot",
lambda: {"rss_kib": 4096, "rss_anon_kib": 3072, "thread_count": 3},
)
monkeypatch.setattr(mem_trim, "_last_trim_monotonic", 0.0)
# t=100: first forced close runs.
monkeypatch.setattr(mem_trim.time, "monotonic", lambda: 100.0)
assert mem_trim.trim_memory(force=True, reason="agent close") is True
assert trim.call_count == 1
# t=101..103: three more child closes inside the floor — all coalesced.
for t in (101.0, 102.0, 103.0):
monkeypatch.setattr(mem_trim.time, "monotonic", lambda t=t: t)
assert mem_trim.trim_memory(force=True, reason="agent close") is False
assert trim.call_count == 1, "burst closes must not stack forced trims"
# t=106: past the floor — the parent's final close-trim still fires.
monkeypatch.setattr(mem_trim.time, "monotonic", lambda: 106.0)
assert mem_trim.trim_memory(force=True, reason="agent close") is True
assert trim.call_count == 2