Skip to content

feat: resource operation support multiple update modes#3491

Merged
csviri merged 46 commits into
operator-framework:mainfrom
csviri:kroxybug-extended-matchers
Jul 15, 2026
Merged

feat: resource operation support multiple update modes#3491
csviri merged 46 commits into
operator-framework:mainfrom
csviri:kroxybug-extended-matchers

Conversation

@csviri

@csviri csviri commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

This reworks ResourceOperations into the primary, idiomatic API for create/update/patch operations that keep the informer cache read-after-write consistent and control how the resulting own event is handled. Every operation comes in a
default flavor and an Options-taking flavor, so users can tune caching vs. own-event filtering per call.

Own-event filtering is only correct when the framework can distinguish an own write from a concurrent third-party write. This PR makes that requirement explicit through a small strategy model (OptionsMode) and a pluggable Matcher
framework with sensible per-operation defaults.

Options / Mode

  • matchAndFilter(matcher) / matchAndFilterWithDefaultMatcher(updateType) — compare desired vs. actual (cached) state; skip the write entirely if they already match, otherwise write and filter the own event. This is the default for the
    update/patch methods and the most efficient option (filters and avoids a needless API call).
  • filterWithOptimisticLocking() — filter the own event; the write must use optimistic locking (a resourceVersion set), otherwise an IllegalArgumentException is thrown. Guarantees a concurrent change is rejected server-side rather than
    silently filtered out.
  • cacheOnly() — only cache the response (read-after-write consistency), no own-event filtering.
  • forceFilterEvents() — always filter (mostly internal; safe only when correctness is otherwise guaranteed).

Matcher framework

  • New Matcher interface plus default matchers for every UpdateType (UPDATE, SSA, JSON_PATCH, JSON_MERGE_PATCH, and their *_STATUS variants), backed by MatcherUtils.
  • GenericKubernetesResourceMatcher.matchStatus(...) added to match only the /status subtree (tolerating server-added fields by default).

Framework integration

  • Finalizer handling (ReconciliationDispatcher): the finalizer is now added with cacheOnly (its own event is no longer filtered), replacing the previous filter + INSTANT_RESCHEDULE so the finalizer-add event itself drives the follow-up
    reconcile.
  • Primary UpdateControl writes now use cacheOnly, trading one extra self-triggered reconcile for correctness (a concurrent spec change during a status patch can no longer be absorbed by the own-event filter).
  • KubernetesDependentResource create/SSA now use forceFilterEvents().
  • AbstractExternalDependentResource persists explicit state through resourceOperations().create(...).

Tests & docs

  • New ITs covering the concurrency and read-after-write edge cases: ResourceOperationsIT, SecondaryResourceOperationsIT, SpecChangeDuringStatusPatchIT, OwnSsaStatusUpdateIT, plus expanded ResourceOperationsTest, PatchMatchersTest,
    StatusMatchersTest.
  • Adjusted existing tests to the new event cadence (e.g. SubResourceUpdateIT, WorkflowMultipleActivationIT now snapshot reconcile counts only after they stabilize).
  • Docs updated: reconciler.md, v5-5-migration.md, v5-5-release.md, and the read-after-write blog post.

Notes / follow-ups

  • The Options API is marked @Experimental (API may change).
  • Default matchers are heuristics — reconcilers relying on them should be tested against their concrete resources, or supply a custom Matcher.

@csviri csviri added this to the 5.5 milestone Jul 13, 2026
@openshift-ci openshift-ci Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jul 13, 2026
@csviri csviri force-pushed the kroxybug-extended-matchers branch from b978daa to f0d3fcb Compare July 14, 2026 08:57
@csviri csviri marked this pull request as ready for review July 14, 2026 13:12
Copilot AI review requested due to automatic review settings July 14, 2026 13:12
@openshift-ci openshift-ci Bot requested review from metacosm and xstefank July 14, 2026 13:12
@csviri csviri changed the title [WIP] improve: resource operation support multiple update modes improve: resource operation support multiple update modes Jul 14, 2026
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 14, 2026

Copilot AI 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.

Pull request overview

This PR reworks ResourceOperations to support multiple update modes (match-before-write, optimistic-locking-aware filtering, cache-only, force-filter), introduces a new Matcher SPI plus default matchers per update type, and expands test/doc coverage around own-event filtering correctness and read-cache-after-write behavior.

Changes:

  • Adds matcher-aware update/patch/create modes to ResourceOperations (including default matchers per operation type).
  • Introduces new matcher implementations (including status-only matching) and expands unit/integration tests for own-event filtering edge cases.
  • Updates documentation/migration notes for v5.5 and adjusts CI integration-test timeout.

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java Updates assertions to reflect additional reconciliation triggered by status updates.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/SecondaryResourceOperationsReconciler.java New reconciler exercising secondary-resource ResourceOperations overloads and update modes.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/SecondaryResourceOperationsIT.java New IT covering secondary-resource operations convergence without looping.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/SecondaryResourceOperationsCustomResource.java New CR type for secondary resource operations tests.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/ResourceOperationsStatus.java New status model for ResourceOperations test CRs.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/ResourceOperationsSpec.java New spec model for ResourceOperations test CRs.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/ResourceOperationsReconciler.java New reconciler exercising primary update/patch/SSA + status variants via ResourceOperations.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/ResourceOperationsIT.java New IT covering all primary update/patch/SSA strategies and convergence.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/resourceoperations/ResourceOperationsCustomResource.java New CR type for primary resource operations tests.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/specchangeduringstatuspatch/SpecChangeDuringStatusPatchStatus.java New status type for spec-change-during-status-patch reproduction test.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/specchangeduringstatuspatch/SpecChangeDuringStatusPatchSpec.java New spec type for spec-change-during-status-patch reproduction test.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/specchangeduringstatuspatch/SpecChangeDuringStatusPatchReconciler.java New reconciler reproducing concurrent spec change during status patch filtering window.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/specchangeduringstatuspatch/SpecChangeDuringStatusPatchIT.java New repeated IT validating spec change is not swallowed by own status patch filtering.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/specchangeduringstatuspatch/SpecChangeDuringStatusPatchCustomResource.java New CR type for the spec-change-during-status-patch scenario.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/ownssastatusupdate/OwnSsaStatusUpdateStatus.java New status type for own SSA status update scenario.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/ownssastatusupdate/OwnSsaStatusUpdateReconciler.java New reconciler applying status via resourceOperations().serverSideApplyPrimaryStatus(...).
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/ownssastatusupdate/OwnSsaStatusUpdateIT.java New IT asserting own SSA status update doesn’t loop and external updates still reconcile.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/ownssastatusupdate/OwnSsaStatusUpdateCustomResource.java New CR type for own SSA status update test.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/ownsecondaryupdate/OwnSecondaryUpdateReconciler.java Switches SSA calls to explicit force-filter options for secondary own-event filtering.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/readcacheafterwrite/onrelistfilter/OnRelistFilterReconciler.java Updates SSA calls to force-filter options for relist filtering scenarios.
operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestReconciler.java Updates SSA call to force-filter options.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/matcher/StatusMatchersTest.java New unit tests for status-only matchers behavior.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/matcher/PatchMatchersTest.java New unit tests for JSON patch/merge patch matchers (resource + status).
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSourceTest.java Adds test ensuring force-filter opens window even without resourceVersion.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java Adds tests for new matchStatus(...) behavior and equality modes.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/ResourceOperationsTest.java Extends tests for matcher-driven patching, cache-only, and filtering modes.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/UpdateType.java New enum mapping update types to default matchers.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/UpdateStatusMatcher.java New matcher for update-status operations using status-only matching.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/UpdateMatcher.java New matcher for update operations using generic resource matching.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/SSAStatusMatcher.java New matcher for SSA status operations using status-only matching.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/SSAMatcher.java New matcher for SSA operations using SSA-based matcher implementation.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/MatcherUtils.java New shared utilities for JSON patch/merge patch matcher implementations.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/JsonPatchStatusMacher.java New JSON Patch status matcher implementation.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/JsonPatchMacher.java New JSON Patch matcher implementation.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/JsonMergePatchStatusMatcher.java New JSON Merge Patch status matcher implementation.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/JsonMergePatchMatcher.java New JSON Merge Patch matcher implementation.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java Adds update-and-cache helper and annotates update paths as experimental.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java Adjusts primary patch paths to use cache-only ResourceOperations variants.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java Updates dependent resource create/update to use explicit force-filter options.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java Adds matchStatus(...) to compare only /status subtree with configurable equality.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceOperations.java Major API expansion: modes/options, matchers, default matcher selection per operation type.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/matcher/Matcher.java New matcher SPI for plug-in match logic.
docs/content/en/docs/migration/v5-5-migration.md New migration doc describing v5.5 behavioral change around UpdateControl.
docs/content/en/docs/documentation/reconciler.md Documents new ResourceOperations.Options modes and UpdateControl behavior change.
docs/content/en/blog/releases/v5-5-release.md New release post for v5.5 summarizing features and migration notes.
docs/content/en/blog/news/read-after-write-consistency.md Updates blog content to reflect explicit facilities for own-event filtering.
.github/workflows/integration-tests.yml Increases integration test job timeout.

@csviri csviri linked an issue Jul 14, 2026 that may be closed by this pull request
@csviri csviri force-pushed the kroxybug-extended-matchers branch 2 times, most recently from 440b380 to 2d7c490 Compare July 14, 2026 16:20
@csviri csviri requested a review from shawkins July 15, 2026 08:32
@csviri csviri changed the title improve: resource operation support multiple update modes feat: resource operation support multiple update modes Jul 15, 2026
Comment on lines +238 to +239
// UpdateControl.patchStatus would only cache the resource to filter out events too
// you have to use resourceOperations.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this should be better structured, it can be confusing

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

changed

@@ -0,0 +1,120 @@
---
title: Version 5.5 Released!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this be a separate PR? Won't you squash commits on merge?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, but since this is I have only this PR related changes in here, I added as a part of the commit, want to make a separate PR for all other changes after we merged this. But if you insist can have this as a separate PR.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

for history purposes it would be better to split

@csviri csviri Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

changed! created a separate PR: #3497

Thank you!

available for the whole resource and the `status` subresource, and with dedicated `primary`
variants.

Every method comes in two flavors: a default one, and one taking an `Options` argument that controls

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe we should also have a global default change so the user doesn't have to pass the argument to each method call.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not sure what do you mean, could you pls elaborate ? maybe with some code snippet

Comment thread docs/content/en/blog/releases/v5-5-release.md Outdated
Comment thread docs/content/en/docs/documentation/reconciler.md Outdated
public <R extends HasMetadata> R create(R resource) {
return resourcePatch(resource, r -> context.getClient().resource(r).create());
// it is safe to do event filtering for create since check if the resource already exists.
return create(resource, Options.forceFilterEvents());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I understand, however, once something is default, I think it should be default everywhere. If you implement that changing of default as I suggested above, this might be confusing. Maybe then don't provide Options parametrized overload and just keep this as implementation detail.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The default methods are always filtering, we achieve that with the Matchers (to cover the problematic edge case). We however want to give the user possibility to not filter (cache-only). The API might be more restrictive in that regard, but this way it is also forward compatible.

boolean matched = true;
for (int i = 0; i < wholeDiffJsonPatch.size() && matched; i++) {
var node = wholeDiffJsonPatch.get(i);
if (nodeIsChildOf(node, List.of(STATUS))) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should this also support nested values?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

it supports nested values, there is a recursive call there.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ok, didn't check, thanks

@csviri csviri force-pushed the kroxybug-extended-matchers branch from 723c493 to 7fe336d Compare July 15, 2026 12:19
csviri added 8 commits July 15, 2026 14:56
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
csviri and others added 25 commits July 15, 2026 14:56
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
The status assertions could throw NPE on the first Awaitility poll before
the reconciler patched the status. untilAsserted retries AssertionErrors but
rethrows other exceptions immediately, so the NPE aborted the test instead of
retrying. Guard with assertThat(status).isNotNull() to keep polling.

Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Martin Stefanko <xstefank122@gmail.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…e-blog)

Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
@csviri csviri force-pushed the kroxybug-extended-matchers branch from fd96006 to 35d020e Compare July 15, 2026 12:56
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
@csviri csviri merged commit 99aba6f into operator-framework:main Jul 15, 2026
26 of 27 checks passed
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.

Superseding event during retry does not reset retry counter — resource can permanently stall Enhanced mode(s) in ResourceOperation

3 participants