Skip to content

Prod deploy evicted from the merge queue when the autoscaler changes a node mid-transaction #3184

Description

@devantler

🤖 Generated by the Agentic Engineer

Evidence

The prod deploy's node-safety transaction races the cluster autoscaler and refuses to proceed when
an autoscale node changes underneath it. Three merge-group deploys have now died this way in under
two hours, each evicting its PR from the merge queue:

When (UTC) Run Failure
2026-08-16 22:39 #3171 node claimed mid-transaction was replaced — identity changed … refusing image verification
2026-08-16 23:02 #3171 evicted again; orphaned Lease flux-system/ghcr-auth-refresh held past expiry
2026-08-17 00:16 #3171 (run 31980786395) Talos node autoscale-cx43-2834820e83860317 identity changed, cordon ownership changed, or scheduling safety state changed before revision marker; refusing the mutation.

The guard itself is correct — refusing to mutate a node whose safety state moved is the right
call. The defect is that nothing reconciles the transaction with the autoscaler, so an ordinary
scale event is indistinguishable from a genuine safety violation.

Residue outlives the run. autoscale-cx43-2834820e83860317 is still cordoned=true with no
ghcr-auth-drain-owner annotation — a cordon with no owner, left behind after the refusal. A
cordoned node with no owner is exactly the state the next deploy will trip over again.

🩹 Heal Prod also failed in this run, which is the free discriminator for whose fault it is:
a base-state fault, not anything in the PR's diff.

Why it matters

Every platform merge is blocked while this recurs. The queue evicts on the failed merge_group,
so the PR bounces back to OPEN and the next re-queue hits the same race. Re-queuing without a fix is
futile by the contract's own merge-queue rule.

Prod itself was never at risk: all three deploys died before publish_platform_manifest, and prod is
currently 11/11 Kustomizations Ready — so this is a delivery-lane outage, not a production
incident.

Expected behaviour

A deploy is not evicted by a routine autoscaler scale event. Either the transaction tolerates an
autoscale node changing state while it holds no claim on that node, or it excludes autoscaler-managed
nodes from the safety marker, or it retries once against the refreshed node set.

Separately, a refused mutation leaves no cordoned node without a drain owner — the refusal path
should release its own cordon, the way the node fence self-releases elsewhere.

Acceptance criteria

  • A merge-group deploy completes while an autoscale node is added or removed during the run.
  • After a refused mutation, no node is left cordoned with ghcr-auth-drain-owner absent.
  • The failure message distinguishes a routine autoscale event from a genuine safety violation.

Rough size: M.

Related: #3178 (stranded cx33 nodes consuming the autoscaler's budget) — a different defect on the
same autoscaler surface, not a duplicate of this one.

Metadata

Metadata

Assignees

Labels

Type

Projects

Status
📥 Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions