job_queue: grow the rebuild subgraph from DeployApply (#2664)
The config-PR deploy's apply node still did the whole container rebuild inline, through the last surviving `lifecycle::rebuild_no_meta` call. It now merges, opens the two-phase meta deploy, and returns the ordinary rebuild chain as a subgraph the scheduler grafts into the live DAG under it. A new `FinalizeDeploy` node, gated on that graft, plants the deploy tag and commits the staged lock. Net effect: "did the agent come back up?" is answered by `Reconcile` succeeding, the same way it is for every other rebuild, instead of by a fused inline start — and each deploy phase is its own queue node, so the dashboard shows which one is running. The grafted nodes root on the apply node, so they land inside `DeployWindow`'s subtree and re-enter the meta window and build slot it already holds rather than deadlocking against them. The new happy-path test runs on a one-slot queue specifically to pin that down. `FinalizeDeploy`'s two git writes are fatal, deliberately: they are what tells `DeployTail` a deploy confirmed good, so a node that merely warned on them could report success while leaving the tail looking at the git state of a failure — and the tail would then roll a good deploy back. The trailing `meta::finalize_deploy` stays warn-only, since by then the container already runs the new config. The `failed/<id>` annotated tag moves into the tail, which is now the only place holding a failed deploy. It reads the reason off the DAG via a new `JobQueue::first_error`, and is gated on `main` having actually moved — the rollback ref is parked *before* the merge, so its existence alone does not mean a merge happened, and a pre-merge rejection must not tag the previous, innocent head. Removing the last inline rebuild orphaned a chain of now-dead code: `rebuild_no_meta`, `container_exists`, `Coordinator::set_queue_build_log` and `JobQueue::set_build_log_id_running`, all deleted here.
This commit is contained in:
parent
7b2645078a
commit
3429a8c5a6
10 changed files with 388 additions and 257 deletions
|
|
@ -247,20 +247,20 @@ a plain `nix flake lock` leaves an existing applied override in
|
|||
place (it only re-locks when the declared url itself changes), so
|
||||
the forge-declared / applied-deployed split is stable.
|
||||
|
||||
Per-deploy lock flow (two-phase, owned by
|
||||
`actions::run_deploy_apply` → `deploy_applied_target` →
|
||||
`meta::{prepare,finalize,abort}_deploy`, with the abort half moved out
|
||||
into `actions::run_deploy_tail`):
|
||||
Per-deploy lock flow (two-phase), spread across the deploy subtree's
|
||||
nodes — each phase is its own node, so the queue can show which one is
|
||||
running and a restart resumes at node granularity:
|
||||
|
||||
1. `meta::prepare_deploy(name)` runs
|
||||
1. `DeployApply` → `meta::prepare_deploy(name)` runs
|
||||
`nix flake lock --update-input agent-<n>` without
|
||||
committing. Working tree of meta now points the input at
|
||||
`applied/<n>/main` (which the deploy already fast-forwarded to
|
||||
the reviewed PR head).
|
||||
2. `lifecycle::rebuild_no_meta` runs
|
||||
`nixos-container update <c> --flake meta#<name>`. Nix
|
||||
evaluates against the staged lock.
|
||||
3. On success — `meta::finalize_deploy(name, sha, "deployed/
|
||||
2. The rebuild subgraph `DeployApply` grows into the DAG builds and
|
||||
swaps the container (`Prebuild → StopForUpdate → Swap → PostSwap`,
|
||||
plus `Reconcile`). Nix evaluates against the staged lock.
|
||||
3. On success — `FinalizeDeploy` drops the rollback ref, plants
|
||||
`deployed/<id>`, then `meta::finalize_deploy(name, sha, "deployed/
|
||||
<id>")` stages `flake.lock` and commits with
|
||||
`deploy <n> deployed/<id> <sha12>`. Meta's git log gains
|
||||
one entry per successful deploy.
|
||||
|
|
|
|||
|
|
@ -355,11 +355,17 @@ Sequence for a rebuild DAG (each step is its own queue node):
|
|||
slot, so the next DAG's `Prebuild` overlaps the container boot — the old
|
||||
"deferred start" split, now structural.
|
||||
|
||||
The approval apply-commit pipeline still drives `lifecycle::rebuild_no_meta`
|
||||
(the fused stop/update/start path with an inline start) inside its
|
||||
`DeployApply` node, because it verifies the agent comes back up before
|
||||
finalizing the deploy tag. Breaking that fused path apart into the
|
||||
`Prebuild → Swap → Reconcile` chain above is increment 2b of #2664, not 2a.
|
||||
The approval deploy uses this same chain rather than a rebuild path of its
|
||||
own. Its `DeployApply` node does not build: it merges, opens the two-phase
|
||||
meta deploy, and returns the chain above as a subgraph the scheduler grafts
|
||||
into the live DAG under that node. A `FinalizeDeploy` node gated on the
|
||||
graft's completion then plants the deploy tag — so "did the agent come back
|
||||
up?" is answered by `Reconcile` succeeding, the same way it is for every
|
||||
other rebuild, instead of by a fused inline start.
|
||||
|
||||
The grafted nodes land *inside* `DeployWindow`'s subtree, so they re-enter
|
||||
the meta window and build slot it already holds rather than deadlocking
|
||||
against it.
|
||||
|
||||
### Cold-start fallback
|
||||
|
||||
|
|
@ -371,8 +377,8 @@ half-started at that point.
|
|||
Fallback: `stop` (graceful SIGTERM drain) → `kill` (SIGKILL any lingering processes)
|
||||
→ `start` (clean cold-start, no generation transition, new activation runs cleanly).
|
||||
Both errors are preserved and surfaced if the cold-start also fails. The fallback
|
||||
lives in `lifecycle::start_with_fallback`, shared by the apply-commit deploy's
|
||||
inline start and every `Reconcile` node's start action.
|
||||
lives in `lifecycle::start_with_fallback`, used by every `Reconcile` node's
|
||||
start action.
|
||||
|
||||
### Spawn path (new container)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue