Skip to content

feat(orchestrator): re-plan the queue from the Speculator each run - #506

Open
behinddwalls wants to merge 1 commit into
preetam/speculation-cancel-msgidfrom
preetam/speculation-run
Open

feat(orchestrator): re-plan the queue from the Speculator each run#506
behinddwalls wants to merge 1 commit into
preetam/speculation-cancel-msgidfrom
preetam/speculation-run

Conversation

@behinddwalls

@behinddwalls behinddwalls commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Why?

The speculate controller had no speculation in it: it advanced one batch at a time along a single hard-coded chain — every dependency assumed to pass — and nothing ever called the Speculator, the Generator, or the Allocator that had been built for it. This wires that machinery into the pipeline: a queue's paths are chosen by a swappable extension within a build budget, and each is built against only the dependencies it assumes will succeed.

What?

Every message is a dirty signal naming a batch; the controller re-plans that batch's whole queue from a single read: read the state, cancel paths whose assumptions a finished dependency has proven wrong, ask the Speculator, filter its proposals, dispatch what survives. Nothing carries over between runs, so duplicated or reordered signals are harmless and a later run repairs whatever an earlier one left half-done. doc.go explains the model in plain terms — no vocabulary section, every term is defined where it is used.

The path set keeps exactly one writer — this run. The build stages record what CI did on per-build records; the run folds those into the set and alone decides each path's status. Pending paths are re-dispatched every run until their build is seen running, dispatches partition by batch so heads proceed in parallel, and cancelling paths need no dispatch at all: the poll loop reads the stop off the set and enacts it.

Speculation lands inert. The wiring layer passes a placeholder Speculator that proposes nothing, so the run executes end to end but funds no paths; composing real per-queue speculators and turning the feature on is the wiring change at the top of this stack. Batch outcomes still come from the legacy per-batch finalizer, which waits on every dependency — strictly stricter than path-aware finalization, so the system stays correct until the next commit replaces it.

Test Plan

bazel test //submitqueue/orchestrator/controller/speculate/... — assumption checks and proposal filtering are table-driven; run tests cover funding a first path, re-dispatching pending paths, broken-path cancellation, build results recorded onto paths, lost CAS races skipped rather than failed, and Speculator errors abandoning the run.

make fmt, make gazelle

Stack

  1. feat(speculation): generator contract and bestfirst impl #446
  2. feat(speculation): allocator contract and sticky impl #450
  3. feat(speculation): standard composed speculator #451
  4. feat(storage): speculation path set store #501
  5. feat(storage): path-build link store for per-path builds #502
  6. feat(orchestrator): dispatch builds per speculation path #503
  7. feat(orchestrator): poll builds and stop the ones nothing wants #504
  8. fix(orchestrator): mint distinct message IDs for cancel re-publishes #505
  9. @ feat(orchestrator): re-plan the queue from the Speculator each run #506
  10. feat(orchestrator)!: finalize batches from their speculation paths #507
  11. feat(orchestrator): compose per-queue speculators and turn speculation on #508

## Summary

### Why?

The speculate controller had no speculation in it: it advanced one batch at a time along a single hard-coded chain — every dependency assumed to pass — and nothing ever called the Speculator, the Generator, or the Allocator that had been built for it. This wires that machinery into the pipeline: a queue's paths are chosen by a swappable extension within a build budget, and each is built against only the dependencies it assumes will succeed.

### What?

Every message is a dirty signal naming a batch; the controller re-plans that batch's whole queue from a single read: read the state, cancel paths whose assumptions a finished dependency has proven wrong, ask the Speculator, filter its proposals, dispatch what survives. Nothing carries over between runs, so duplicated or reordered signals are harmless and a later run repairs whatever an earlier one left half-done. `doc.go` explains the model in plain terms — no vocabulary section, every term is defined where it is used.

The path set keeps exactly one writer — this run. The build stages record what CI did on per-build records; the run folds those into the set and alone decides each path's status. Pending paths are re-dispatched every run until their build is seen running, dispatches partition by batch so heads proceed in parallel, and cancelling paths need no dispatch at all: the poll loop reads the stop off the set and enacts it.

Speculation lands inert. The wiring layer passes a placeholder Speculator that proposes nothing, so the run executes end to end but funds no paths; composing real per-queue speculators and turning the feature on is the wiring change at the top of this stack. Batch outcomes still come from the legacy per-batch finalizer, which waits on every dependency — strictly stricter than path-aware finalization, so the system stays correct until the next commit replaces it.

## Test Plan

✅ `bazel test //submitqueue/orchestrator/controller/speculate/...` — assumption checks and proposal filtering are table-driven; run tests cover funding a first path, re-dispatching pending paths, broken-path cancellation, build results recorded onto paths, lost CAS races skipped rather than failed, and Speculator errors abandoning the run.

✅ `make fmt`, `make gazelle`
@behinddwalls
behinddwalls force-pushed the preetam/speculation-run branch from f9ddb16 to 55134c7 Compare August 4, 2026 05:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant