Skip to content

fix: retry SSA field manager migration on conflict#567

Open
perdasilva wants to merge 1 commit into
package-operator:mainfrom
perdasilva:fix/retry-ssa-field-manager-migration-on-conflict
Open

fix: retry SSA field manager migration on conflict#567
perdasilva wants to merge 1 commit into
package-operator:mainfrom
perdasilva:fix/retry-ssa-field-manager-migration-on-conflict

Conversation

@perdasilva

@perdasilva perdasilva commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • After creating an object, migrateFieldManagersToSSA patches the field managers to be SSA-compatible using csaupgrade. If another controller (e.g. the apiextensions controller updating CRD status) modifies the object between the create response and that patch, the API server rejects the patch with a 409 Conflict due to a stale resourceVersion.
  • Wraps the patch attempt in retry.RetryOnConflict(retry.DefaultBackoff, ...). DefaultBackoff uses exponential backoff (factor 5×), which is the correct choice for conflicts caused by an unrelated controller — as opposed to DefaultRetry (linear), which is for multiple clients racing on the same resource. On conflict, the object is re-fetched (via unfilteredReader if available, otherwise cache) to get the latest resourceVersion before recomputing and re-applying the patch.
  • Extracts a small refreshObject helper used by the retry closure, called unconditionally at the top of each iteration.

Motivation

Users were seeing transient errors of the form:

reconciling object: reconciling apiextensions.k8s.io/v1, Kind=CustomResourceDefinition /applications.argoproj.io: migrating to SSA after create: update field managers: Operation cannot be fulfilled on customresourcedefinitions.apiextensions.k8s.io "applications.argoproj.io": the object has been modified; please apply your changes to the latest version and try again

This is a TOCTOU race that is especially common with CRDs because the apiextensions controller patches status immediately after creation. The retry absorbs the transient conflict without any user-visible error.

Test plan

  • Existing unit tests pass (./do dev:unit)
  • Manual verification: reconcile a CRD-backed object and confirm the error no longer surfaces

🤖 Generated with Claude Code

@perdasilva perdasilva requested a review from a team as a code owner July 13, 2026 07:43
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

migrateFieldManagersToSSA now refreshes objects and retries managed-fields JSON patches on conflicts. Refreshes use the configured cache or unfiltered reader, and tests cover the added reads and empty-patch behavior.

Changes

Managed-fields migration

Layer / File(s) Summary
Refresh and retry migration flow
machinery/objects.go
Adds conflict retries with object refreshes through the configured cache or unfiltered reader, recomputes patches per attempt, and preserves empty-patch early returns.
Migration read and patch tests
machinery/objects_test.go
Updates cache expectations for refresh reads and verifies that empty managed-fields patches do not call Patch.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: retrying SSA field-manager migration on conflict.
Description check ✅ Passed It includes the required summary, motivation, and test plan, though the change type and checklist sections are missing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.70588% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.95%. Comparing base (8f7a028) to head (aabcb11).

Files with missing lines Patch % Lines
machinery/objects.go 64.70% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #567      +/-   ##
==========================================
- Coverage   82.98%   82.95%   -0.03%     
==========================================
  Files          32       32              
  Lines        2744     2751       +7     
==========================================
+ Hits         2277     2282       +5     
- Misses        340      341       +1     
- Partials      127      128       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
machinery/objects.go (1)

679-706: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression test for conflict refresh and patch recomputation.

Mock the first patch as a 409, update the reader’s object/resourceVersion, then assert that the second patch is computed from the refreshed object and succeeds. The existing no-patch test does not exercise the new retry path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@machinery/objects.go` around lines 679 - 706, Add a regression test for the
retry flow surrounding UpgradeManagedFieldsPatch: make the first writer.Patch
call return a conflict, update the reader’s object and resourceVersion during
that conflict, then verify the second attempt recomputes the patch from the
refreshed object and succeeds. Keep the existing no-patch test unchanged, and
assert both the refreshed input and successful retry outcome.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@machinery/objects.go`:
- Line 679: Update the RetryOnConflict call in the visible retry callback to use
retry.DefaultBackoff instead of retry.DefaultRetry, preserving the existing
callback and conflict-handling behavior.
- Around line 708-714: Update ObjectEngine.refreshObject to always use an
uncached reader during conflict recovery, rather than falling back to e.cache.
Ensure the unfilteredReader is available for migration retries and use it for
the ObjectKeyFromObject lookup and refresh Get operation, preserving the
existing error propagation.

---

Nitpick comments:
In `@machinery/objects.go`:
- Around line 679-706: Add a regression test for the retry flow surrounding
UpgradeManagedFieldsPatch: make the first writer.Patch call return a conflict,
update the reader’s object and resourceVersion during that conflict, then verify
the second attempt recomputes the patch from the refreshed object and succeeds.
Keep the existing no-patch test unchanged, and assert both the refreshed input
and successful retry outcome.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 943095d3-3edb-457d-bfc0-b00450058418

📥 Commits

Reviewing files that changed from the base of the PR and between 8f7a028 and 649c8d3.

📒 Files selected for processing (1)
  • machinery/objects.go

Comment thread machinery/objects.go Outdated
Comment thread machinery/objects.go
@perdasilva perdasilva force-pushed the fix/retry-ssa-field-manager-migration-on-conflict branch from b12610e to 9712d8f Compare July 13, 2026 07:51
When an object is created and immediately mutated by another controller
(e.g. the CRD controller adding status), the field-manager migration
patch carries a stale resourceVersion and gets rejected with a 409
Conflict. Use retry.RetryOnConflict with DefaultBackoff (exponential,
for unrelated controller conflicts) to re-fetch the object and retry
the patch, avoiding spurious reconcile errors bubbling up to the user.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@perdasilva perdasilva force-pushed the fix/retry-ssa-field-manager-migration-on-conflict branch from 9e6f776 to aabcb11 Compare July 13, 2026 07:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@machinery/objects_test.go`:
- Around line 239-245: Add cache.AssertExpectations(t) to
TestObjectEngine_MigrateFieldManagersToSSA_NoPatch after the migration call
completes, ensuring the configured Get expectations are verified while
preserving the existing mock setup and assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 6c012ef3-6cf1-4acd-9d9e-43803cb5426f

📥 Commits

Reviewing files that changed from the base of the PR and between 649c8d3 and 9712d8f.

📒 Files selected for processing (2)
  • machinery/objects.go
  • machinery/objects_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • machinery/objects.go

Comment thread machinery/objects_test.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant