You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Stage the release builder: bound the dense reconcile working set (~0.5TB measured) via cache spill + checkpointed passes #463
Apply the #406 staging discipline to the release builder: bound tools/build_us_fiscal_refresh_release.py's live working set so dense builds fit a ~256GB box again, instead of renting RAM to cover ~half a terabyte of accreted state.
Evidence (Build M dense reruns, 2026-07-19/20, n2-highmem-32 → n2-highmem-80)
#461 (merged) eliminated the reference-lifetime pathology — post-fix runs show RSS returning between families (43GB → 10GB early; slope 0.4GB/min vs 2.5 pre-fix) and per-family sweeps visibly reclaiming swap. What remains is legitimate live state, measured on the full dense pool (RID …20260720T000722Z):
First crest ≈ 429GB total (RAM 251 + swap 178) at the eligibility/full-pool high-water mark — survived, then receded to ~400GB through the JCT reform families.
Second crest ≈ 536GB (251 + 285) in the SSI take-up reconcile passes, oscillating 240–310GB of swap as passes rematerialize and sweep.
Consequence: once the active arrays exceeded physical RAM, throughput collapsed — 6 hours with zero release-log writes, ~0.93 effective cores on 32 vCPU, continuous two-way paging (4–12MB/s si+so). The run was correct but pacing toward days; resized to n2-highmem-80 (640GB) to finish ($5.2/hr vs ~$2.1).
Shape of the fix (the #406 pattern, already proven on the base builder)
Spill materialized families through the existing target_materialization_cache instead of holding the full target frame + all family outputs resident: materialize → persist → drop frames; the compile/solve step streams from cache.
Prior art: codex/jct-materialization-memory-20260617 (June branch, same instinct pre-#461); measurement trail on #456. Slots into the Build N epic (#449) as the dense-lane front item alongside the CD surface.
Spill materialized reform families through the existing target_materialization_cache (materialize → persist → drop; compile/solve streams from cache), preserving cache identity semantics — any layout change is explicitly versioned, not silent.
Diff stays off the target-registry data surfaces (fiscal_targets.py, release_input_coverage_manifest.json) per the #451 Phase B lane coordination. Working in a fresh worktree off current origin/main.
Third full-scale datapoint, and it reclassifies the problem: the n2-highmem-80 rerun (RID …20260720T102929Z, 629GB RAM + 419GB swap) was oom-killed after 13h11m at anon-rss 623GB / total-vm 1.10TB (dmesg 23:39:26Z; pressure log shows MemAvailable at 0 for the final minutes).
The decisive evidence is the phase behavior, not the peak: the release log shows 73 eligibility/ACA materialization cycles, still looping at death — i.e. the SSI take-up reconcile ran ~a dozen-plus passes without exiting, and the footprint grew across passes (~536GB peak on the 32-vCPU run at fewer passes → 1TB+ here at more passes). Two coupled defects, both dense-arm-specific:
So #463's scope confirms and extends: the staged/checkpointed-pass design must make pass N's memory die with it (fix 1 structurally), and the reconcile-pass audit should treat dense convergence as its own acceptance criterion (pass-count bound or explicit divergence failure, not an OOM). Evidence preserved on the stopped VM (buildm-dense-runner, disk retained): release/pressure/chain logs for all three runs, dmesg records, warm caches. The M-dense artifact is blocked on this work — no box size rescues a non-converging loop.
Scope update absorbed into the implementation contract:
"Pass N's memory dies with pass N" is now a HARD acceptance criterion for the checkpoint-bounded passes, verified two ways: the stage profiler asserts a flat parent RSS floor across pass boundaries on a synthetic multi-pass fixture (no staircase), and the PR body will carry a targeted audit of both named suspects — where pass-variant cache identities are created (disk accretion acceptable, parent-resident handles not) and what the checkpoint layer pins between passes.
Divergence bound added as its own intervention: a trajectory-reading contraction requirement evaluated at each pass boundary, orthogonal to max_passes, reading the Carry the SSI reconcile per-pass trajectory through raise and record (#447) #448 per-pass swap-delta trajectory already in the reconcile payload. Plateau, oscillation, and growth all fail with the full per-pass numbers in the gate error — minutes, not 13 hours. Unit-tested with teeth (contracting passes; plateau/oscillation/growth each fail naming the trajectory).
Please ship the raw logs to this issue (or a gist linked here) — most useful, in order: the pressure log with pass-boundary timestamps (for the staircase step sizes), the release log section covering the reconcile passes (per-pass swap-delta/count-error trajectory to calibrate the contraction factor against real divergence), and the dmesg OOM record. Implementation order: profiler → divergence bound → subprocess passes → family spill-through; equivalence harness built alongside.
Design reframe (supersedes the checkpointed-pass framing above; from Max's review of the OOM evidence): the reconcile loop should be removed, not staged. The calibration solve is already a static matrix descent; the only reason the engine re-enters is that SSI take-up participation depends on weights. That coupling is expressible without re-simulation:
Materialize the take-up-sensitive columns once, conditional on participation state — for each candidate unit, the participant-state and nonparticipant-state contributions to every affected target family (SSI amounts, categorical Medicaid, ACA MAGI class — the dependency set is narrow and enumerable, not "everything").
A reconcile pass is then: flip participation indicators to hit the count target under current weights → recompose the affected matrix rows from the precomputed conditional columns → re-solve. No engine, no rematerialization, milliseconds per pass instead of hours.
The engine runs exactly once per input-frame state, at the start, to build the conditional matrix. Memory = the matrix (full dense pool ≈ tens of GB) plus two variants of the affected column subset — a single-box problem with no pass staircase by construction.
Acceptance for #463 accordingly: (1) dense full-pool build completes on n2-highmem-32 with engine materialization occurring exactly once (assert via a materialization-count telemetry, not just RSS); (2) reconcile trajectory identical-or-better vs the sparse-certified semantics (same fresh-pair exit, caps per #452/#455) — proven on the sparse arm by bit-comparison of the certified M reconcile outcome recomputed through the matrix path; (3) the pass-count bound + loud divergence failure stands as the backstop.
The staged/spill machinery (#406 pattern) remains useful for the one-shot materialization's peak, but it is the secondary lever now.
Plan-of-record simplification (Max, on review of the reconcile's rationale): SSI loses its special treatment entirely. The loop's own docstring gives three motivations; each dissolves:
Dependent surfaces (Medicaid categorical / ACA / other-health) — needs ordering, not iteration: assign take-up flags once from the prior, materialize dependent columns once under those frozen flags, calibrate. Inputs stay self-consistent with zero passes.
Two official measures ~115k apart — already solved by rescaling onto the registry's single ssi_recipients total; that is prior work, not loop work.
Revised #463 scope, in order: (a) delete _reconcile_ssi_take_up_and_refit from the release path — take-up assignment happens once, pre-materialization; (b) engine-once materialization with the #406-pattern spill for the one-shot peak; (c) the materialization-count telemetry and the divergence backstop become trivial (no loop to bound); (d) the conditional-columns design from the previous comment is demoted to a documented fallback, to be built only if the accepted SSI count miss is later adjudicated unacceptable after the #374 priors land. Acceptance adds: N's scorecard reports the SSI recipient-count relative error alongside the other count targets, same contract, no special casing.
This also retires the #452/#455 swap-cap machinery and the #448 pass telemetry from the live path once the deletion lands (they stay in history as the loop's honest instrumentation). Sparse arm equivalence: N is a fresh certification — the M-vs-N scorecard comparison surfaces the count-miss delta transparently rather than hiding it behind reconciliation.
Attempt 310842b: target compilation took 9,951 s at 1.04 CPU-s per wall second across about 2,124 independent batches. The last release rerun spent 22 h at about one core.
The batches are independent, so sharding them across processes is the largest available speedup. About 8 processes would have saved roughly 8,500 s on that attempt (derived from the receipts, not measured).
Each process holds its own batch working set, so the shard count must fit the memory bound this issue sets.
The next Route A attempt runs with staging on (plus Record process telemetry for US release builds #1083 once merged). The Build progress tab will then show per-stage cores and memory and batch progress, which is the baseline for a sharding change.
Activity
MaxGhenis commentedon Jul 20, 2026
Claiming this. Plan of record, per the spec:
codex/jct-materialization-memory-20260617branch for prior art, then map the two crests against the builder's actual stage sequence (eligibility/full-pool high-water; SSI reconcile passes).target_materialization_cache(materialize → persist → drop; compile/solve streams from cache), preserving cache identity semantics — any layout change is explicitly versioned, not silent.stage_profile.jsontwin with per-stage RSS/wall high-water, asserted in tests as a budget number.Diff stays off the target-registry data surfaces (
fiscal_targets.py,release_input_coverage_manifest.json) per the #451 Phase B lane coordination. Working in a fresh worktree off current origin/main.MaxGhenis commentedon Jul 21, 2026
Third full-scale datapoint, and it reclassifies the problem: the n2-highmem-80 rerun (RID
…20260720T102929Z, 629GB RAM + 419GB swap) was oom-killed after 13h11m at anon-rss 623GB / total-vm 1.10TB (dmesg 23:39:26Z; pressure log shows MemAvailable at 0 for the final minutes).The decisive evidence is the phase behavior, not the peak: the release log shows 73 eligibility/ACA materialization cycles, still looping at death — i.e. the SSI take-up reconcile ran ~a dozen-plus passes without exiting, and the footprint grew across passes (~536GB peak on the 32-vCPU run at fewer passes → 1TB+ here at more passes). Two coupled defects, both dense-arm-specific:
target_materialization_cache, or pass-scoped frames pinned by the checkpoint layer). Bound release-builder memory: one engine system per target family, explicit simulation release #461's fix is visibly working within passes (RSS returns between families) — the staircase is across passes.So #463's scope confirms and extends: the staged/checkpointed-pass design must make pass N's memory die with it (fix 1 structurally), and the reconcile-pass audit should treat dense convergence as its own acceptance criterion (pass-count bound or explicit divergence failure, not an OOM). Evidence preserved on the stopped VM (
buildm-dense-runner, disk retained): release/pressure/chain logs for all three runs, dmesg records, warm caches. The M-dense artifact is blocked on this work — no box size rescues a non-converging loop.MaxGhenis commentedon Jul 21, 2026
Scope update absorbed into the implementation contract:
"Pass N's memory dies with pass N" is now a HARD acceptance criterion for the checkpoint-bounded passes, verified two ways: the stage profiler asserts a flat parent RSS floor across pass boundaries on a synthetic multi-pass fixture (no staircase), and the PR body will carry a targeted audit of both named suspects — where pass-variant cache identities are created (disk accretion acceptable, parent-resident handles not) and what the checkpoint layer pins between passes.
Divergence bound added as its own intervention: a trajectory-reading contraction requirement evaluated at each pass boundary, orthogonal to max_passes, reading the Carry the SSI reconcile per-pass trajectory through raise and record (#447) #448 per-pass swap-delta trajectory already in the reconcile payload. Plateau, oscillation, and growth all fail with the full per-pass numbers in the gate error — minutes, not 13 hours. Unit-tested with teeth (contracting passes; plateau/oscillation/growth each fail naming the trajectory).
Please ship the raw logs to this issue (or a gist linked here) — most useful, in order: the pressure log with pass-boundary timestamps (for the staircase step sizes), the release log section covering the reconcile passes (per-pass swap-delta/count-error trajectory to calibrate the contraction factor against real divergence), and the dmesg OOM record. Implementation order: profiler → divergence bound → subprocess passes → family spill-through; equivalence harness built alongside.
MaxGhenis commentedon Jul 21, 2026
Design reframe (supersedes the checkpointed-pass framing above; from Max's review of the OOM evidence): the reconcile loop should be removed, not staged. The calibration solve is already a static matrix descent; the only reason the engine re-enters is that SSI take-up participation depends on weights. That coupling is expressible without re-simulation:
Acceptance for #463 accordingly: (1) dense full-pool build completes on n2-highmem-32 with engine materialization occurring exactly once (assert via a materialization-count telemetry, not just RSS); (2) reconcile trajectory identical-or-better vs the sparse-certified semantics (same fresh-pair exit, caps per #452/#455) — proven on the sparse arm by bit-comparison of the certified M reconcile outcome recomputed through the matrix path; (3) the pass-count bound + loud divergence failure stands as the backstop.
The staged/spill machinery (#406 pattern) remains useful for the one-shot materialization's peak, but it is the secondary lever now.
MaxGhenis commentedon Jul 21, 2026
Plan-of-record simplification (Max, on review of the reconcile's rationale): SSI loses its special treatment entirely. The loop's own docstring gives three motivations; each dissolves:
ssi_recipientstotal; that is prior work, not loop work.ssi_recipientsis an ordinary indicator-column count target (it already exists at fiscal_targets.py:2434); the solve hits it as well as it hits any other count, and the residual miss is recorded in diagnostics like every other program's. M's own trajectory bounds what we are accepting: the loop was closing ~0.46–0.71M on ~7.5M recipients (6–10%), typical of the misses SNAP/Medicaid/EITC live with today. The structural fix for that miss is the Build N SIPP donor stage (SIPP+SCF blend + SSI-aware refit: land the asset magnitudes for the Build J re-certification #374/Child SSI structurally absent: ages 0–14 have no disability signal (baseline child SSI 0.12M vs SSA 0.98M); seed qualifying disability per the #430 pattern #453) — a better prior, not a runtime loop.Revised #463 scope, in order: (a) delete
_reconcile_ssi_take_up_and_refitfrom the release path — take-up assignment happens once, pre-materialization; (b) engine-once materialization with the #406-pattern spill for the one-shot peak; (c) the materialization-count telemetry and the divergence backstop become trivial (no loop to bound); (d) the conditional-columns design from the previous comment is demoted to a documented fallback, to be built only if the accepted SSI count miss is later adjudicated unacceptable after the #374 priors land. Acceptance adds: N's scorecard reports the SSI recipient-count relative error alongside the other count targets, same contract, no special casing.This also retires the #452/#455 swap-cap machinery and the #448 pass telemetry from the live path once the deletion lands (they stay in history as the loop's honest instrumentation). Sparse arm equivalence: N is a fresh certification — the M-vs-N scorecard comparison surfaces the count-miss delta transparently rather than hiding it behind reconciliation.
SSI take-up: delivered-weight prior basis + hard band delivery gate (#…
PavelMakarchuk commentedon Oct 5, 2026
Throughput: the batch loop runs on about one core