Switch the scale set off instead of rebuilding it when runners are outdated - #4649
Merged
nikola-jokic merged 0 commit intoSep 10, 2026
Conversation
nikola-jokic
requested review from
a team,
Steve-Glass,
mumoshu,
rentziass and
toast-gear
as code owners
September 10, 2026 16:49
nikola-jokic
force-pushed
the
nikola-jokic/ars-outdated-phase
branch
from
September 10, 2026 20:56
5318de3 to
7078da7
Compare
nikola-jokic
merged commit Sep 10, 2026
7078da7
into
nikola-jokic/guard-job-started-phase-write
5 checks passed
nikola-jokic
force-pushed
the
nikola-jokic/guard-job-started-phase-write
branch
from
September 10, 2026 20:56
3aabeeb to
f4c21f8
Compare
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.
Stacked on #4648 (which is stacked on #4647).
The problem
AutoscalingRunnerSetPhaseOutdatedwas declared and read, but nothing in the codebase ever assigned it.So when the runners rejected the runner spec (pod exits 7 →
EphemeralRunnergoesOutdated→EphemeralRunnerSetgoesOutdated), theAutoscalingRunnerSetremoved the listener and then calledcleanupEphemeralRunnerSet, which deletes the set — while leaving its own phase onRunning.Nothing held the scale set switched off. The next reconcile saw a missing
EphemeralRunnerSet, created it, created a listener for it, and the fresh runners rejected the same spec again. The scale set churned through create/teardown cycles against the Actions service instead of resting, directly contradicting the comment in that branch saying it "should stay in outdated state until the spec is updated".The
outdatedbranch at the top ofReconcilealready did the right thing — remove the listener, keep the set, pin it to zero replicas — but was dead code, because the phase it keys on was never set.The intended lifecycle
EphemeralRunnerphaseOutdated.EphemeralRunnerSetsees it, goesOutdated, and releases every runner that is not executing a job (cleanUpEphemeralRunnersalready skipsHasJob()runners).AutoscalingRunnerSetgoesOutdatedand switches the listener off, so no more jobs are acquired. ← this step was missingThe change
The
ephemeralRunnerSetOutdatedForAppliedRevision && phase == Runningcase now recordsAutoscalingRunnerSetPhaseOutdatedand hands over to the same handler the top-of-reconcileoutdatedbranch uses, which is extracted intoreconcileOutdated. TheEphemeralRunnerSetis kept and pinned toReplicas = 0, PatchID = 0rather than deleted.ObservedGenerationis carried over unchanged, so a later spec edit still registers as new work and moves the phase back toPendingthrough the existing generation check.The
EphemeralRunnerSetspec patch now also fires when the set is still outdated for its applied revision, which is the recovery path. Reaching that branch means theAutoscalingRunnerSetis not outdated and the case above already claimed every running set that is, so the spec must have been updated since the runners rejected it.The revision has to advance even when the runner spec itself did not change. The revision is what tells the
EphemeralRunnerSetto stop judging itself by the runners that failed (patchAppliedActionableRevisionStatusre-buckets them as stale and clears the outdated phase). Without it, a scale set could only ever be recovered by editing the pod template, and an edit to anything else —maxRunners, the runner group, the config secret — would switch the listener back on against a set permanently parked at zero.Tests
The outdated path had no coverage at all in
autoscalingrunnerset_controller_test.go, which is why deleting the runner set went unnoticed. AddedTest AutoscalingRunnerSet outdated lifecyclecovering:Outdated, the listener is deleted, and theEphemeralRunnerSetsurvives at zero replicas (asserted withConsistently, since the bug was that it got deleted);All three fail on the parent commit.