From c7d5e4f0065a59e3dffab4a98523b863e8e304e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 16:22:21 +0100 Subject: [PATCH 01/12] Suspected bug in informer cache access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../source/informer/ManagedInformerEventSource.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 978deda333..0811e57290 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -193,6 +193,11 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) { public Optional get(ResourceID resourceID) { Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); var res = cache.get(resourceID); + if (log.isDebugEnabled()) { + log.debug( + "Latest sync version: {}", + manager().lastSyncResourceVersion(resourceID.getNamespace().orElse(null))); + } if (comparableResourceVersions && resource.isPresent() && ReconcilerUtilsInternal.compareResourceVersions( @@ -203,8 +208,9 @@ public Optional get(ResourceID resourceID) { return resource; } log.debug( - "Resource not found, or older, in temporary cache. Found in informer cache {}, for" - + " Resource ID: {}", + "Resource found in temp cache: {}, or older, in temporary cache. Found in informer cache" + + " {}, for Resource ID: {}", + resource.isPresent(), res.isPresent(), resourceID); return res; From b852f0065ee2d901856ecd6f25aa1feca4b485cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 16:36:56 +0100 Subject: [PATCH 02/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../informer/ManagedInformerEventSource.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 0811e57290..f761a2061e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -191,6 +191,7 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) { @Override public Optional get(ResourceID resourceID) { + // order of getting those resource from cache matters Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); var res = cache.get(resourceID); if (log.isDebugEnabled()) { @@ -200,10 +201,16 @@ public Optional get(ResourceID resourceID) { } if (comparableResourceVersions && resource.isPresent() - && ReconcilerUtilsInternal.compareResourceVersions( - resource.get().getMetadata().getResourceVersion(), - manager().lastSyncResourceVersion(resource.get().getMetadata().getNamespace())) - > 0) { + // it can happen here that we receive an event after we read the resource from the informer + // cache + // that bumps the lastSync version, but we read the resource before. In that case we want to + // return + // the resource from temp cache. + && (res.isEmpty() + || ReconcilerUtilsInternal.compareResourceVersions( + resource.get().getMetadata().getResourceVersion(), + manager().lastSyncResourceVersion(resource.get().getMetadata().getNamespace())) + > 0)) { log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID); return resource; } From 4bac02c4cdc09075faeeeebeafe3cb5ed46b7931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 16:37:48 +0100 Subject: [PATCH 03/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../event/source/informer/ManagedInformerEventSource.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index f761a2061e..a139b1da8a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -202,10 +202,8 @@ public Optional get(ResourceID resourceID) { if (comparableResourceVersions && resource.isPresent() // it can happen here that we receive an event after we read the resource from the informer - // cache - // that bumps the lastSync version, but we read the resource before. In that case we want to - // return - // the resource from temp cache. + // cache that bumps the lastSync version, but we read the resource before. In that case we + // want to return the resource from temp cache. && (res.isEmpty() || ReconcilerUtilsInternal.compareResourceVersions( resource.get().getMetadata().getResourceVersion(), From 2c763e5534461e605c21133b6442567e160cef2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 16:59:32 +0100 Subject: [PATCH 04/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../event/source/informer/ManagedInformerEventSource.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index a139b1da8a..7bdeb2289a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -194,11 +194,6 @@ public Optional get(ResourceID resourceID) { // order of getting those resource from cache matters Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); var res = cache.get(resourceID); - if (log.isDebugEnabled()) { - log.debug( - "Latest sync version: {}", - manager().lastSyncResourceVersion(resourceID.getNamespace().orElse(null))); - } if (comparableResourceVersions && resource.isPresent() // it can happen here that we receive an event after we read the resource from the informer @@ -207,7 +202,7 @@ public Optional get(ResourceID resourceID) { && (res.isEmpty() || ReconcilerUtilsInternal.compareResourceVersions( resource.get().getMetadata().getResourceVersion(), - manager().lastSyncResourceVersion(resource.get().getMetadata().getNamespace())) + res.get().getMetadata().getResourceVersion()) > 0)) { log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID); return resource; From 686b69f3279e17c5df507e48c2fce3ed4aeaef11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 17:03:45 +0100 Subject: [PATCH 05/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../event/source/informer/ManagedInformerEventSource.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 7bdeb2289a..1828aa5533 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -196,9 +196,6 @@ public Optional get(ResourceID resourceID) { var res = cache.get(resourceID); if (comparableResourceVersions && resource.isPresent() - // it can happen here that we receive an event after we read the resource from the informer - // cache that bumps the lastSync version, but we read the resource before. In that case we - // want to return the resource from temp cache. && (res.isEmpty() || ReconcilerUtilsInternal.compareResourceVersions( resource.get().getMetadata().getResourceVersion(), From 6d5e15cca8ef47739c77d50b10873bd9d4146a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 17:09:52 +0100 Subject: [PATCH 06/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../event/source/informer/ManagedInformerEventSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 1828aa5533..964af20fe9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -191,7 +191,7 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) { @Override public Optional get(ResourceID resourceID) { - // order of getting those resource from cache matters + // The order of reading from these caches matters Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); var res = cache.get(resourceID); if (comparableResourceVersions From c0c95dff4c066218d2e261a56f2482e4d4539a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 20:40:38 +0100 Subject: [PATCH 07/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../CachingFilteringUpdateReconciler.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java index 1bd60eb2c9..9958bf8ad9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java @@ -43,19 +43,30 @@ public UpdateControl reconcile( CachingFilteringUpdateCustomResource resource, Context context) { - context.resourceOperations().serverSideApply(prepareCM(resource)); + context.resourceOperations().serverSideApply(prepareCM(resource, 1)); var cachedCM = context.getSecondaryResource(ConfigMap.class); if (cachedCM.isEmpty()) { issueFound.set(true); throw new IllegalStateException("Error for resource: " + ResourceID.fromResource(resource)); } + var updated = context.resourceOperations().serverSideApply(prepareCM(resource, 2)); + cachedCM = context.getSecondaryResource(ConfigMap.class); + if (!cachedCM + .orElseThrow() + .getMetadata() + .getResourceVersion() + .equals(updated.getMetadata().getResourceVersion())) { + issueFound.set(true); + throw new IllegalStateException("Error for resource: " + ResourceID.fromResource(resource)); + } + ensureStatusExists(resource); resource.getStatus().setUpdated(true); return UpdateControl.patchStatus(resource); } - private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p) { + private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p, int num) { var cm = new ConfigMapBuilder() .withMetadata( @@ -63,7 +74,7 @@ private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p) { .withName(p.getMetadata().getName()) .withNamespace(p.getMetadata().getNamespace()) .build()) - .withData(Map.of("name", p.getMetadata().getName())) + .withData(Map.of("name", p.getMetadata().getName(), "num", "" + num)) .build(); cm.addOwnerReference(p); return cm; From 4e69fd3b9f976ce97936855f023caa4fc774c630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Tue, 17 Mar 2026 20:41:10 +0100 Subject: [PATCH 08/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../CachingFilteringUpdateReconciler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java index 9958bf8ad9..0e9a953129 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java @@ -58,7 +58,8 @@ public UpdateControl reconcile( .getResourceVersion() .equals(updated.getMetadata().getResourceVersion())) { issueFound.set(true); - throw new IllegalStateException("Error for resource: " + ResourceID.fromResource(resource)); + throw new IllegalStateException( + "Update error for resource: " + ResourceID.fromResource(resource)); } ensureStatusExists(resource); From 78a7a13306854acd5779c294fc5d08881e63f04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 18 Mar 2026 08:45:35 +0100 Subject: [PATCH 09/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../informer/ManagedInformerEventSource.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 964af20fe9..ea8dd03c92 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -193,24 +193,24 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) { public Optional get(ResourceID resourceID) { // The order of reading from these caches matters Optional resource = temporaryResourceCache.getResourceFromCache(resourceID); - var res = cache.get(resourceID); if (comparableResourceVersions && resource.isPresent() - && (res.isEmpty() - || ReconcilerUtilsInternal.compareResourceVersions( - resource.get().getMetadata().getResourceVersion(), - res.get().getMetadata().getResourceVersion()) - > 0)) { + && ReconcilerUtilsInternal.compareResourceVersions( + resource.get().getMetadata().getResourceVersion(), + manager().lastSyncResourceVersion(resource.get().getMetadata().getNamespace())) + > 0) { log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID); return resource; + } else { + var resFromInformer = cache.get(resourceID); + log.debug( + "Resource found in temp cache: {}, or older, in temporary cache. Found in informer cache" + + " {}, for Resource ID: {}", + resource.isPresent(), + resFromInformer.isPresent(), + resourceID); + return resFromInformer; } - log.debug( - "Resource found in temp cache: {}, or older, in temporary cache. Found in informer cache" - + " {}, for Resource ID: {}", - resource.isPresent(), - res.isPresent(), - resourceID); - return res; } /** From 844e8877de06f83d93956a1faba29a81f17e0c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 18 Mar 2026 08:49:19 +0100 Subject: [PATCH 10/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../event/source/informer/ManagedInformerEventSource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index ea8dd03c92..ca4e022bc8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -202,6 +202,7 @@ public Optional get(ResourceID resourceID) { log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID); return resource; } else { + // this needs to happen after comparison to ensure correctness var resFromInformer = cache.get(resourceID); log.debug( "Resource found in temp cache: {}, or older, in temporary cache. Found in informer cache" From 0ce8f28e48fcf7424a11cf5dd387d8fda87cb1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 18 Mar 2026 12:22:33 +0100 Subject: [PATCH 11/12] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java Co-authored-by: Steven Hawkins --- .../event/source/informer/ManagedInformerEventSource.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index ca4e022bc8..4003247805 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -205,10 +205,10 @@ public Optional get(ResourceID resourceID) { // this needs to happen after comparison to ensure correctness var resFromInformer = cache.get(resourceID); log.debug( - "Resource found in temp cache: {}, or older, in temporary cache. Found in informer cache" - + " {}, for Resource ID: {}", - resource.isPresent(), - resFromInformer.isPresent(), + "Resource {} in temp cache; {}found in informer cache" + + ", for Resource ID: {}", + resource.isPresent() ? "obsolete" : "not found", + resFromInformer.isPresent() ? "", : "not ", resourceID); return resFromInformer; } From bd4f9372b24545f708f389ef137aa5323b38db83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 18 Mar 2026 12:23:54 +0100 Subject: [PATCH 12/12] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../event/source/informer/ManagedInformerEventSource.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 4003247805..26543e8322 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -205,10 +205,9 @@ public Optional get(ResourceID resourceID) { // this needs to happen after comparison to ensure correctness var resFromInformer = cache.get(resourceID); log.debug( - "Resource {} in temp cache; {}found in informer cache" - + ", for Resource ID: {}", + "Resource {} in temp cache; {}found in informer cache" + ", for Resource ID: {}", resource.isPresent() ? "obsolete" : "not found", - resFromInformer.isPresent() ? "", : "not ", + resFromInformer.isPresent() ? "" : "not ", resourceID); return resFromInformer; }