Follow-up finding from #753 part 2 (observed incident: projection load reported loading for 16 minutes past load_deadline_ms: 30000, RSS climbing 11.4 GB -> 18.5 GB until a kernel OOM).
The state-truth defects around that incident are fixed on the PR #707 lane:
- the load deadline now fires during a cold
open_session (typed LoadDeadlineExceeded, loader thread keeps the slot/reservation until the runtime returns, then releases), and
- lifecycle
Indexing{completed_units,total_units} now surfaces as runtime Indexing progress instead of being flattened to Loading, so long "loading" phases that are actually indexing are no longer mislabeled.
What remains and is worth its own slice: the semantic load path's memory ceiling is enforced only against estimates, never against actual usage while work is in flight.
Attribution (code pointers, crates/tracedecay-semantic):
SessionPool reserves runtime.resident_bytes_reservation(authority) (the manifest resident_bytes_estimate) before open and re-checks session.resident_bytes_estimate() after open (session_pool.rs, cold-open path). Nothing observes actual RSS between those two points.
FastEmbedEmbeddingRuntime::open_session (fastembed_adapter.rs:1179) buffers the entire model + tokenizer members in memory (fastembed_model, fastembed_adapter.rs:1295) and then builds the ORT session via TextEmbedding::try_new_from_user_defined. ORT's graph parse/optimization/arena allocation during that constructor is unbounded by any TraceDecay ceiling; peak transient memory is a multiple of model bytes and invisible to the pool's accounting.
- After the (new) deadline abandonment, the loader thread cannot cancel ORT mid-build; the memory stays genuinely held (and truthfully accounted as reserved) until ORT returns. A hung or thrashing ORT build therefore still holds its memory unboundedly in time, just no longer blocks the acquisition.
The 7.1 GB growth in the incident is far larger than one ~600 MB model graph build, which is consistent with the mislabeled-indexing mechanism (real projection/indexing work accumulating while the surface said loading) rather than a single ORT session build; with the progress mapping fixed, the next occurrence will be attributable directly from status output.
Suggested direction (not started): enforce an actual resident bound during load/indexing — either sample the loader's RSS against resident_byte_ceiling and fail typed, or isolate the ORT session build so it can be cancelled/killed at the deadline instead of merely abandoned. Per repo rules the ceilings themselves are symptoms guards and stay unchanged.
Follow-up finding from #753 part 2 (observed incident: projection load reported
loadingfor 16 minutes pastload_deadline_ms: 30000, RSS climbing 11.4 GB -> 18.5 GB until a kernel OOM).The state-truth defects around that incident are fixed on the PR #707 lane:
open_session(typedLoadDeadlineExceeded, loader thread keeps the slot/reservation until the runtime returns, then releases), andIndexing{completed_units,total_units}now surfaces as runtimeIndexingprogress instead of being flattened toLoading, so long "loading" phases that are actually indexing are no longer mislabeled.What remains and is worth its own slice: the semantic load path's memory ceiling is enforced only against estimates, never against actual usage while work is in flight.
Attribution (code pointers,
crates/tracedecay-semantic):SessionPoolreservesruntime.resident_bytes_reservation(authority)(the manifestresident_bytes_estimate) before open and re-checkssession.resident_bytes_estimate()after open (session_pool.rs, cold-open path). Nothing observes actual RSS between those two points.FastEmbedEmbeddingRuntime::open_session(fastembed_adapter.rs:1179) buffers the entire model + tokenizer members in memory (fastembed_model,fastembed_adapter.rs:1295) and then builds the ORT session viaTextEmbedding::try_new_from_user_defined. ORT's graph parse/optimization/arena allocation during that constructor is unbounded by any TraceDecay ceiling; peak transient memory is a multiple of model bytes and invisible to the pool's accounting.The 7.1 GB growth in the incident is far larger than one ~600 MB model graph build, which is consistent with the mislabeled-indexing mechanism (real projection/indexing work accumulating while the surface said
loading) rather than a single ORT session build; with the progress mapping fixed, the next occurrence will be attributable directly from status output.Suggested direction (not started): enforce an actual resident bound during load/indexing — either sample the loader's RSS against
resident_byte_ceilingand fail typed, or isolate the ORT session build so it can be cancelled/killed at the deadline instead of merely abandoned. Per repo rules the ceilings themselves are symptoms guards and stay unchanged.