Estimate when a try push will finish - #7
Open
rcurranmoz wants to merge 2 commits into
Open
Conversation
Adds an ETA to the push list and the push detail view. Two numbers, not one, because a push does not finish smoothly: on a sampled 907-job push, 90% of the jobs were done at 47 minutes and the last one at 144, and the tail was queueing (95-117 min) rather than slow work (12-25 min). The headline is when 90% of the jobs are in; the full finish sits beside it as an approximation. Backtested against 369 try pushes / 73,864 jobs by replaying 77 completed pushes at nine points each. Most-results: 2 min median error, within +/-25% 70% of the time, overruns by >30 min in 1%. All-done: 13 min, 39%, 32%. Naive percent-complete extrapolation, for contrast, has a 84-165 min median error and a -50 to -105 min bias, because the completion curve is nowhere near linear. Run time comes from a bundled 137 KB table of per-job-type medians -- duration has a median coefficient of variation of 5%, so this predicts to within 5%. Queue wait, the hard part, is read live per worker pool from the push itself. Building the whole thing from the push's own jobs instead lands within +/-25% only 12% of the time, which is why the table ships. Below 60% pool coverage the estimate is wrong by ~113 min, so it is not shown; a flat "rough" band was measured and dropped (within +/-50% only half the time, and an honest range has to span 6x). Instead that case shows the precise thing it is actually waiting for: the build. Gecko's shippable pipeline is three stages deep, so the chain is walked rather than just the running stage. Predicted build finishes land within 5 min 71% of the time. Also fixes three things this work surfaced: - TreeHerder emits an `unscheduled` job state that JobState did not model, so the parser's unknown-state fallback filed those jobs as `.completed`. They were invisible to isRunning and could fire "Try push passed" with jobs not yet started -- a live 691-job push had 24 of them. Unknown states now default to `.pending`, since guessing "done" is the dangerous direction. - Absent timestamps arrive as 0, not null, and decoded to a valid 1970 date, so every queued job looked already-started. - A still-running push with failures showed only the failure badge, hiding the ETA on exactly the pushes being watched most closely.
Feature bump rather than a build-only bump: the push list and detail view both gain a new element. Release build verified carrying JobDurations.json -- without the bundled table the estimator silently falls back to a single 20.8 minute global median for every job.
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Adds an ETA to the push list and the push detail view, and ships it as 1.2 (10).
The reason a try push is annoying isn't that it fails, it's not knowing when it lands. This
answers that.
Two numbers, not one
A push doesn't finish smoothly. On a sampled 907-job push, 90% of the jobs were done at 47
minutes and the last one at 144 — and the tail wasn't slow work, it was waiting: those
final jobs ran for 12–25 minutes after queueing for 95–117. A single "done at" time is
therefore dominated by whichever worker pool happens to be backed up.
So the headline is when 90% of your jobs are in, which is both the reliable number and
the one that answers "when will I know if this is green". The full finish sits beside it,
deliberately rendered as an approximation.
Backtested against 369 try pushes / 73,864 jobs, by replaying 77 completed pushes at nine
points each:
For contrast, naive percent-complete extrapolation — the obvious implementation — has a
84–165 minute median error and a −50 to −105 minute bias, because the completion curve is
nowhere near linear.
How
Run time is the predictable part: a job's duration has a median coefficient of variation of
5%, so a bundled table of per-job-type medians predicts it to within 5%. Queue wait is the
hard part, and it's read live per worker pool from the push itself — jobs in a pool that have
already started tell you what the ones that haven't will wait. On one push, 76 jobs in
macosx1500-aarch64-shippablehad a p25, median and p90 queue wait of 140.7, 140.7 and144.6 minutes, because they're all gated on the same build and released together.
The table is why this works and why it ships (137 KB,
BuildWatch/Resources/JobDurations.json,regenerate with
tools/generate-duration-table.py). Building the same estimator from onlythe push's own completed jobs lands within ±25% just 12% of the time, against 70% with it.
No new data source and no extra requests — it runs off job rows BuildWatch already fetches.
Three states, because only one supports a countdown
unscheduledbehind a build; one live Talos push had 32 of its 37 unresolved jobs inexactly that state with no pool data to work from. Rather than a vague push ETA, the card
shows the precise thing: which stage is running and when tests get released. Gecko's
shippable pipeline is three stages deep (
instrumented-build-→generate-profile-→build-), so the chain is walked, not just the running stage. Predicted build finishesland within 5 minutes 71% of the time.
the estimate is wrong by ~113 minutes.
A flat "rough estimate" band between those was measured and dropped: within ±50% only half
the time, and a range honest enough to cover 77% has to span 6× ("somewhere between one hour
and six"). 83% of pushes clear the firm bar, at a median of 30 minutes in, and then stay
clear for a median 72% of what's left.
Three bugs this surfaced, fixed here
unscheduledwas an unmodelled job state. TreeHerder emits it alongside the otherthree and it isn't rare — a live 691-job push had 24. The parser's unknown-state fallback
filed them as
.completed, so they were invisible toisRunningand could fire "Try pushpassed" with jobs not yet started. Since the watch is one-shot, no correction followed.
Unknown states now default to
.pending; guessing "done" is the dangerous direction.0, notnull, and decoded to a valid 1 January 1970, soevery queued job looked like one that started 56 years ago.
being watched most closely. Both now show.
Verification
JobDurations.json(withoutit the estimator silently degrades to one 20.8-minute global median for every job).
read
1h 2m, matching an independent hand calculation of the algorithm to the minute(~6 min of
generate-profile+ ~59 min ofbuild-macosx64-aarch64-shippable).sees the pushes it's scored on.
Caveats
±several points. The effects driving the design are large enough that the conclusions hold.
and they account for the worst errors in the whole backtest. The ETA just recomputes.
Confidence's thresholds and the 1.25× tail calibration are tuned on three days of trytraffic. If pool behaviour shifts a lot, they'd want re-measuring.
Not in this PR
NotificationDelegateposts.openPushbut nothing observes it, so tapping a completionnotification doesn't deep-link anywhere despite the README saying it does. Pre-existing and
left alone.
🤖 Generated with Claude Code