Summary
After a full reindex the engine settles at ~2.5 GB RSS and stays there indefinitely, with no work in flight. sched: returned idle heap to OS fires and RSS does not move.
On a 16 GB machine shared with the user's real work — the stated driver for #5954 — this permanent floor matters more than the transient peak the epic has been optimising.
Measured
Full from-scratch reindex of the corpus group (427,301 entities / 1,852,625 relationships), engine at bbf22392f. Clean run: swap flat at 582 MB throughout, available memory never below 6359 MB, so these are not pressure artifacts.
Engine RSS sampled every 2 s:
t= 0s engine= 693MB <- freshly restarted
t= 121s engine= 1806MB
t= 241s engine= 2190MB
t= 601s engine= 2494MB
t= 720s engine= 2495MB
...
t=1921s engine= 2495MB <- flat for 22 minutes
sched: returned idle heap to OS idle_for=34s freeosmemory_took=96ms fired at t=914s. RSS after it: unchanged at 2495 MB. Confirmed again 20 minutes after the run ended — 2496 MB, 0.5% CPU, in_flight=0.
For contrast, the same engine before this reindex was 365 MB, and 693 MB immediately after a restart. So the ~2.5 GB is acquired by the reindex and then never released.
Where the time and memory actually go
Same run, phase breakdown:
| phase |
duration |
peak RSS |
| extraction (index children) |
111 s |
3830 MB |
| engine in-process (resolve refs / links / analytics) |
~760 s |
2495 MB |
| community detection (group-algo) |
6 s |
1697 MB |
| peak summed across all processes |
|
4767 MB |
Two things worth noting for anyone reasoning about this:
- Extraction and community detection together are 117 s of a ~880 s pipeline. The dominant wall-time cost is now engine in-process work, which emits almost no log lines — the 14-minute window after group-algo contains exactly one message.
- The 4767 MB peak is transient and serialised (~2 min, during extraction). The 2.5 GB floor is permanent. For the concurrent-worktree-agent workload, the floor is the binding constraint.
Probable cause, unverified
FreeOSMemory on darwin releases pages via MADV_FREE, which does not reduce RSS until the kernel actually reclaims under pressure. If so, the memory is available to the system and the RSS number overstates the harm — but it would still show up as pressure on a machine that is already tight, and it means the log line is claiming something the system does not reflect.
The alternative is that the engine is genuinely retaining live heap after a rebuild (index structures, the prior graph, or analytics scratch never dropped). These have very different fixes, so the first task is to distinguish them — compare runtime.MemStats live heap (next_gc / (1 + GOGC/100)) against RSS at idle, rather than trusting either number alone.
Note heap_inuse is not live heap; that mistake has been made in this epic before.
Why this is filed separately from the peak work
#5954's slices so far (#6009, #6011, #6017, #6025) all target transient peak during the pipeline, and they are measured wins. None of them touch what the engine holds at rest. This is a different axis and needs its own measurement before any fix is designed.
Refs #5954
Summary
After a full reindex the engine settles at ~2.5 GB RSS and stays there indefinitely, with no work in flight.
sched: returned idle heap to OSfires and RSS does not move.On a 16 GB machine shared with the user's real work — the stated driver for #5954 — this permanent floor matters more than the transient peak the epic has been optimising.
Measured
Full from-scratch reindex of the corpus group (427,301 entities / 1,852,625 relationships), engine at
bbf22392f. Clean run: swap flat at 582 MB throughout, available memory never below 6359 MB, so these are not pressure artifacts.Engine RSS sampled every 2 s:
sched: returned idle heap to OS idle_for=34s freeosmemory_took=96msfired at t=914s. RSS after it: unchanged at 2495 MB. Confirmed again 20 minutes after the run ended — 2496 MB, 0.5% CPU,in_flight=0.For contrast, the same engine before this reindex was 365 MB, and 693 MB immediately after a restart. So the ~2.5 GB is acquired by the reindex and then never released.
Where the time and memory actually go
Same run, phase breakdown:
Two things worth noting for anyone reasoning about this:
Probable cause, unverified
FreeOSMemoryon darwin releases pages viaMADV_FREE, which does not reduce RSS until the kernel actually reclaims under pressure. If so, the memory is available to the system and the RSS number overstates the harm — but it would still show up as pressure on a machine that is already tight, and it means the log line is claiming something the system does not reflect.The alternative is that the engine is genuinely retaining live heap after a rebuild (index structures, the prior graph, or analytics scratch never dropped). These have very different fixes, so the first task is to distinguish them — compare
runtime.MemStatslive heap (next_gc / (1 + GOGC/100)) against RSS at idle, rather than trusting either number alone.Note
heap_inuseis not live heap; that mistake has been made in this epic before.Why this is filed separately from the peak work
#5954's slices so far (#6009, #6011, #6017, #6025) all target transient peak during the pipeline, and they are measured wins. None of them touch what the engine holds at rest. This is a different axis and needs its own measurement before any fix is designed.
Refs #5954