Filter owned-resource events in the workqueue - #4647
Open
nikola-jokic wants to merge 1 commit into
Open
Conversation
nikola-jokic
requested review from
a team,
Steve-Glass,
mumoshu,
rentziass and
toast-gear
as code owners
September 10, 2026 11:42
nikola-jokic
force-pushed
the
nikola-jokic/workqueue-predicates
branch
from
September 10, 2026 12:07
197555c to
d48fc8d
Compare
nikola-jokic
added this pull request to stack #4645
September 10, 2026 12:07
nikola-jokic
force-pushed
the
nikola-jokic/workqueue-predicates
branch
from
September 10, 2026 12:52
d48fc8d to
6e48a61
Compare
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The critical Status.JobID projection issue must be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
controllers/actions.github.com/predicates.go — The EphemeralRunnerSet reconciler uses HasJob() (which is based on Status.JobID) to skip busy… |
|
controllers/actions.github.com/predicates_test.go — The ARS predicate adds Status.Phase and Status.AppliedActionableRevision as wake-up fields, but… |
What changed in this PR
Optimizes controller workqueues by filtering irrelevant owned-resource updates while preserving reconciliation behavior.
Changes:
- Adds field-aware predicates for owned resource watches.
- Extracts shared pod readiness logic.
- Adds predicate tests and wires predicates into three controllers.
| File | Summary |
|---|---|
controllers/actions.github.com/predicates.go |
Defines owned-resource predicates. Critical (3 votes): include Status.JobID in the runner projection. |
controllers/actions.github.com/predicates_test.go |
Tests predicate behavior. Nit (3 votes): add positive cases for ARS phase and actionable revision. |
controllers/actions.github.com/ephemeralrunnerset_controller.go |
Applies the runner predicate. |
controllers/actions.github.com/ephemeralrunner_controller.go |
Applies the pod predicate and shared readiness helper. |
controllers/actions.github.com/autoscalingrunnerset_controller.go |
Applies the runner-set predicate. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+79
to
+80
| return oldRunner.Status.Phase != newRunner.Status.Phase || | ||
| oldRunner.Status.RunnerID != newRunner.Status.RunnerID |
Comment on lines
+39
to
+45
| for name, mutate := range map[string]func(*v1alpha1.EphemeralRunnerSet){ | ||
| "replicas": func(s *v1alpha1.EphemeralRunnerSet) { s.Spec.Replicas = 5 }, | ||
| "patch id": func(s *v1alpha1.EphemeralRunnerSet) { s.Spec.PatchID = 9 }, | ||
| "actionable revision": func(s *v1alpha1.EphemeralRunnerSet) { s.Spec.ActionableRevision = 7 }, | ||
| "ephemeral runner spec": func(s *v1alpha1.EphemeralRunnerSet) { | ||
| s.Spec.EphemeralRunnerSpec.GitHubConfigURL = "https://github.com/org" | ||
| }, |
nikola-jokic
force-pushed
the
nikola-jokic/workqueue-predicates
branch
from
September 10, 2026 16:20
6e48a61 to
af8eaa4
Compare
This was referenced Sep 10, 2026
nikola-jokic
force-pushed
the
nikola-jokic/workqueue-predicates
branch
from
September 10, 2026 20:56
af8eaa4 to
790f2bb
Compare
Every controller in this package woke up on every update of the resources it
owns, including the ones that only touch fields it never reads. An
EphemeralRunner status carries readiness, failure bookkeeping and the job
details written by the listener, a pod reports IPs, its node, a start time and
several conditions, and an EphemeralRunnerSet rewrites
Status.FinishedRunnerCleanupPatchID for every listener patch id. None of that is
input to the owner, yet all of it enqueued a reconcile.
Add an update predicate to each owned watch. A predicate is only allowed to be
an optimisation, so each one is written as the projection of the fields its
reconciler actually reads and drops an update only when all of them are equal:
- AutoscalingRunnerSet -> EphemeralRunnerSet: object metadata, the whole spec,
and the Status.Phase and Status.AppliedActionableRevision pair that
ephemeralRunnerSetOutdatedForAppliedRevision consults.
- EphemeralRunnerSet -> EphemeralRunner: object metadata, spec, Status.Phase
and Status.RunnerID.
- EphemeralRunner -> Pod: UID, deletion timestamp, pod phase, reason and
message, the container and init container statuses, and the Ready
condition.
An event of an unexpected type is always delivered, and create, delete and
generic events are untouched. The primary watches keep seeing every update, so
the status patches a reconciler makes to hand work to its own next pass, such
as marking a runner Failed or Outdated before cleaning up its resources, still
re-enqueue.
The Ready condition lookup is extracted into podReady so that the predicate and
updateRunStatusFromPod cannot disagree about what readiness means.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
nikola-jokic
force-pushed
the
nikola-jokic/workqueue-predicates
branch
from
September 11, 2026 09:42
790f2bb to
b3e44a1
Compare
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 #4646, which is stacked on #4580. Second of two PRs that split #4582
apart. This is the optimisation half, and it is meant to be behaviour
preserving.
The problem
Each controller woke up on every update of the resources it owns, including
updates that only touch fields it never reads. An
EphemeralRunnerstatuscarries readiness, failure bookkeeping and the job details the listener writes.
A pod reports IPs, its node, a start time and several conditions. An
EphemeralRunnerSetrewritesStatus.FinishedRunnerCleanupPatchIDfor everylistener patch id. None of that is input to the owner, and all of it enqueued a
reconcile.
The change
An update predicate on each owned watch. A predicate must only ever be an
optimisation, so each one is written as the projection of the fields its
reconciler actually reads, and drops an update only when all of them are equal:
Status.PhaseandStatus.AppliedActionableRevisionStatus.Phase,Status.RunnerIDObject metadata here means generation, deletion timestamp, finalizers, labels,
annotations and owner references. Annotations matter because the runner set
reads
AnnotationKeyPatchIDandAnnotationKeyActionableRevisionoff runners,and because the autoscaling runner set patches runner set labels and
annotations without touching the spec.
The ARS predicate has to keep
Status.PhaseandStatus.AppliedActionableRevisionbecauseephemeralRunnerSetOutdatedForAppliedRevisionreads exactly that pair to decide whether the autoscaling runner set becomes
Outdated.
Status.FinishedRunnerCleanupPatchIDis the only runner set statusfield it never reads, and it is the one that churns.
podReadyis extracted so the pod predicate andupdateRunStatusFromPodcannotdisagree about what readiness means.
Why this does not change behaviour
The version of these predicates in #4582 did change behaviour, in five ways.
This PR does not carry any of them:
EphemeralRunnerstatus on the primary watch, keyed ongeneration.
markAsFailedandmarkAsOutdatedpatch status and return; thepod and secret are cleaned up on the next reconcile via
IsDone(). Thatreconcile would never have fired. Here the primary watches are untouched,
so a reconciler handing work to its own next pass still re-enqueues.
Status.Failurespatch fromdeletePodAsFailed,which drives the
maxFailuresdelete path. Same fix.EphemeralRunnerSetprimary watch on generation, butautoscalingrunnerset_controller.gopatches runner set labels andannotations without touching the spec, so generation does not move. Those
would never have propagated to the runners. Same fix.
EphemeralRunnerpredicate ignored annotations, which the runner setreads for patch id and actionable revision. Annotations are compared here.
Status.Conditions, the source ofStatus.Ready, andStatus.Reason/Message, which are copied into therunner status. All three are compared here.
Two smaller hardenings: an event whose type does not match is now delivered
rather than dropped, and create, delete and generic events are untouched.
Testing
go test ./controllers/... ./cmd/...with envtest, all green, no changes neededto existing tests.
predicates_test.goadds 40 cases that pin the contract fromboth sides: one per field that must wake each reconciler, plus cases asserting
the specific noise fields stay filtered.