Summary
SECTION_COST_VS_PROBE in src/core/query/overview.js is calibrated at
1.9x, measured against a 48k-row cache. On a 91k-row cache it measures
~3.4x. The planner therefore believes a window costs roughly half what it
actually costs, and picks one it cannot finish inside OVERVIEW_TIME_BUDGET_MS.
The user-visible symptoms:
hyp init prints Stopped here to keep setup moving - the repos and tools sections did not finish. The wizard's deadline is a documented backstop
("it should only fire when the measured plan was wrong ... If it starts
firing routinely, the fix is the planner's calibration, not a longer
deadline" - first_look.js), and it was firing routinely.
hyp query overview has no deadline, so it does not truncate; it just runs
long. Measured 8.5s against a 5s budget.
Measurement
Cache: 91,098 rows across 55 active days (macOS, warm).
| statement |
wall time (CLI, includes ~400ms node startup) |
| probe |
773 ms |
| models |
1,665 ms |
| daily |
1,633 ms |
| repos |
1,620 ms |
| tools |
1,049 ms |
For the window the planner actually chose (18 days, 71,582 rows):
- predicted section cost, as coded: ~2,210 ms
- actual section cost: ~5,000 ms
The window choice is consistent with an in-process probe of ~700ms: that
yields timeCap ≈ 73,600 rows, and the day walk stops at 18 days / 71,582
rows. So the time cap is the binding constraint here, computed from a cost
model that is ~1.8x optimistic.
Why the ratio drifted
The constant's own comment records the original measurement: "Measured at 48k
rows: ~0.27s against ~0.50s, so a section is ~1.9x the probe." Two things
changed underneath it:
- The probe reads one narrow column (
date), which columnar storage prunes
to almost nothing, while the sections read attributes - large JSON blobs
with json_extract/cast over them. The gap widens with payload size, so
a ratio measured on a smaller/lighter cache under-predicts a bigger one.
- Three of the four sections now also run
count(distinct session_id), which
was not part of the 48k-row measurement.
Second defect: the section count is hardcoded
rowsAffordable charges for OVERVIEW_SECTIONS.length (4) sections
unconditionally:
const sectionCount = OVERVIEW_SECTIONS.length
return Math.floor(budgetMs / (perRowMs * SECTION_COST_VS_PROBE * sectionCount))
but collectOverview accepts a sections subset, and since LLP 0195
#wizard-sections the wizard runs only ['models', 'daily']. The planner now
over-charges the wizard by 2x, which partly cancels the 1.8x under-charge
above - by accident, not by design, and only for that one caller. The cost
model should take the sections it is planning for.
Suggested fix
- Re-measure
SECTION_COST_VS_PROBE across a few cache sizes (not one), and
update both the constant and the comment recording what it was measured
against.
- Pass the requested
sections into rowsAffordable so the estimate matches
the work.
- Consider whether the ratio belongs as a single constant at all, given it
varies with payload size. A per-section weight, or a floor on the estimate,
may be more honest than one number that is right at one cache size.
Not in scope: raising OVERVIEW_TIME_BUDGET_MS or the wizard's deadline.
first_look.js rules that out explicitly, and it would make every install
wait longer to paper over a bad estimate.
Open question
With a ~700ms probe and the 18-day window, the predicted-vs-actual arithmetic
above puts the sections at ~4.7-5.7s, still inside the wizard's 8s deadline -
yet the truncation message fired. Either the sections are slower in the
wizard's process than when timed through hyp query sql, or something else
contributes to the elapsed time the deadline sees. Worth confirming with a
wizard.first_look span from a real run before settling on the fix, since it
may indicate a second cost the model does not account for at all.
References
src/core/query/overview.js: SECTION_COST_VS_PROBE, rowsAffordable,
chooseOverviewWindow, OVERVIEW_TIME_BUDGET_MS
src/core/cli/wizard/first_look.js: FIRST_LOOK_BUDGET_MS and the
backstop-not-mechanism rule
- LLP 0135 #window, #overrun - the planner's design
- LLP 0195 #wizard-sections - the trim that exposed the hardcoded section count
Summary
SECTION_COST_VS_PROBEinsrc/core/query/overview.jsis calibrated at1.9x, measured against a 48k-row cache. On a 91k-row cache it measures
~3.4x. The planner therefore believes a window costs roughly half what it
actually costs, and picks one it cannot finish inside
OVERVIEW_TIME_BUDGET_MS.The user-visible symptoms:
hyp initprintsStopped here to keep setup moving - the repos and tools sections did not finish.The wizard's deadline is a documented backstop("it should only fire when the measured plan was wrong ... If it starts
firing routinely, the fix is the planner's calibration, not a longer
deadline" -
first_look.js), and it was firing routinely.hyp query overviewhas no deadline, so it does not truncate; it just runslong. Measured 8.5s against a 5s budget.
Measurement
Cache: 91,098 rows across 55 active days (macOS, warm).
For the window the planner actually chose (18 days, 71,582 rows):
The window choice is consistent with an in-process probe of ~700ms: that
yields
timeCap≈ 73,600 rows, and the day walk stops at 18 days / 71,582rows. So the time cap is the binding constraint here, computed from a cost
model that is ~1.8x optimistic.
Why the ratio drifted
The constant's own comment records the original measurement: "Measured at 48k
rows: ~0.27s against ~0.50s, so a section is ~1.9x the probe." Two things
changed underneath it:
date), which columnar storage prunesto almost nothing, while the sections read
attributes- large JSON blobswith
json_extract/castover them. The gap widens with payload size, soa ratio measured on a smaller/lighter cache under-predicts a bigger one.
count(distinct session_id), whichwas not part of the 48k-row measurement.
Second defect: the section count is hardcoded
rowsAffordablecharges forOVERVIEW_SECTIONS.length(4) sectionsunconditionally:
but
collectOverviewaccepts asectionssubset, and since LLP 0195#wizard-sections the wizard runs only
['models', 'daily']. The planner nowover-charges the wizard by 2x, which partly cancels the 1.8x under-charge
above - by accident, not by design, and only for that one caller. The cost
model should take the sections it is planning for.
Suggested fix
SECTION_COST_VS_PROBEacross a few cache sizes (not one), andupdate both the constant and the comment recording what it was measured
against.
sectionsintorowsAffordableso the estimate matchesthe work.
varies with payload size. A per-section weight, or a floor on the estimate,
may be more honest than one number that is right at one cache size.
Not in scope: raising
OVERVIEW_TIME_BUDGET_MSor the wizard's deadline.first_look.jsrules that out explicitly, and it would make every installwait longer to paper over a bad estimate.
Open question
With a ~700ms probe and the 18-day window, the predicted-vs-actual arithmetic
above puts the sections at ~4.7-5.7s, still inside the wizard's 8s deadline -
yet the truncation message fired. Either the sections are slower in the
wizard's process than when timed through
hyp query sql, or something elsecontributes to the elapsed time the deadline sees. Worth confirming with a
wizard.first_lookspan from a real run before settling on the fix, since itmay indicate a second cost the model does not account for at all.
References
src/core/query/overview.js:SECTION_COST_VS_PROBE,rowsAffordable,chooseOverviewWindow,OVERVIEW_TIME_BUDGET_MSsrc/core/cli/wizard/first_look.js:FIRST_LOOK_BUDGET_MSand thebackstop-not-mechanism rule