Skip the Installation finalizer write when nothing changed - #5219
Skip the Installation finalizer write when nothing changed#5219eastagiletracker wants to merge 1 commit into
Conversation
MaintainInstallationFinalizer patches the Installation on every call, including the steady-state case where the controller's finalizer is already present and there is nothing to write. The patch carries an optimistic lock, so it is not a harmless no-op: any concurrent writer turns it into a conflict, and the caller reports that as a degraded status with "Error setting finalizer on Installation". Return early when the finalizer is already present, mirroring the check the removal path already makes when it is already absent.
|
|
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an optimization to MaintainInstallationFinalizer to avoid unnecessary patches (and resulting optimistic-lock conflicts) when the desired finalizer state is already satisfied, and introduces targeted tests to validate patch behavior under concurrent writes.
Changes:
- Skip patching the Installation when the finalizer is already present (main resource exists), avoiding empty-patch conflict errors.
- Add new Ginkgo specs using controller-runtime client interceptors to assert when
Patchis (and isn’t) called.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| pkg/controller/utils/utils.go | Adds an early-return optimization when the finalizer is already present to avoid needless optimistic-lock patch conflicts. |
| pkg/controller/utils/utils_test.go | Adds tests for MaintainInstallationFinalizer, including verifying no patch occurs when nothing changes and simulating another writer updating the Installation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Add a finalizer indicating that the mainResource is still available. We can skip this if the | ||
| // finalizer is already present: the optimistic lock above makes an otherwise empty patch fail | ||
| // whenever another writer has touched the Installation, degrading the controller over a write | ||
| // that had nothing to change. |
There was a problem hiding this comment.
Let's revert this comment change - I don't think it's adding anything that isn't obvious from the code.
|
@eastagiletracker You'll need to sign the CLA if we want to merge this. Not sure if you're capable of doing that, since you're a bot. I'll give you a bit and then probably close this. |
This PR proposes skipping the
Installationfinalizer write when the finalizer is already present, so a steady-state reconcile stops issuing an optimistic-lock patch that has nothing to change and can only fail (Fixes #4999). We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/402. You can sign in with your GitHub ID to claim ownership of the project.Description
This is a bug fix in
pkg/controller/utils, affecting every controller that maintains a finalizer on theInstallation: whisker, goldmane, apiserver, gatewayapi and clusterconnection.MaintainInstallationFinalizerreads theInstallation, captures a patch base withclient.MergeFromWithOptimisticLock{}, and then patches unconditionally — including the ordinary case where the controller's finalizer is already in the list and nothing needs writing. The optimistic lock means that patch is never an empty no-op:mergeFromPatch.Dataalways writesmetadata.resourceVersioninto the body, so the request goes to the apiserver every time. That happens on every reconcile of each of those controllers — at minimum once perPeriodicReconcileTime(5 minutes) each, and again on every watch event.Because each of those writes carries a resourceVersion precondition, any other writer touching the
Installationbetween the read and the write turns a request that had nothing to change into a409. The callers do not treat that as benign: it becomesSetDegraded(ResourceReadError, "Error setting finalizer on Installation", ...), which is the error reported in #4999 for a cluster where Argo CD also writes the object.The removal path already guards against precisely this — it returns early when the finalizer is not present, rather than writing to discover there was nothing to do. This change adds the mirror-image guard on the add path, so the write only happens when the finalizer list actually changes.
Reproducing on master
The five specs added here run against
pkg/controller/utils. Onmasterat 5615c1a, two of them fail:The second failure reproduces the reported symptom exactly, with the apiserver's own wording:
The three specs that pass on both trees are the controls: the finalizer is still added when it is missing, finalizers owned by other controllers are still preserved alongside it, and the finalizer is still removed once the main resource is gone. They are there so the guard cannot pass by simply doing less work.
Testing
Automated only; I have no cluster reproduction of the Argo CD setup in #4999, so the concurrent writer is modelled with an interceptor that updates the
Installationbetween the read and the write.With the change applied, all five specs pass. The wider suite was run before and after the change with no new failures —
go test ./pkg/...is green across 98 packages, using envtest assets fromsetup-envtest use 1.34.xto match theuttarget (without them theInstallationCEL validation specs fail inBeforeEachon a missing control plane, before and after alike).One thing worth being explicit about: this does not make finalizer writes conflict-proof. A genuine first-time add can still lose the optimistic lock if another writer lands at the same instant, and that path is deliberately unchanged — it has something real to write, and it retries on the next reconcile. What goes away is the steady-state write that recurs for the life of the cluster, which is the one behind a recurring error.
Release Note
For PR author
make gen-files— not changed.make gen-versions— not changed.How this was managed
This work was tracked on a board imported from this repository's own issues and pull requests — 5,202 stories, with release milestones as epics. The story for this fix is
Error setting finalizer on Installationerrors in operator logs, on the board at https://eastagiletracker.com/projects/402.If you'd rather not receive contributions like this, reply
no-more-prson this pull request and we won't open any further ones on your repositories.Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com