Skip to content

Commit cd2a5ff

Browse files
csviriCopilotshawkins
authored
fix: race condition on get when add received after informer cache read (#3237)
Signed-off-by: Attila Mészáros <a_meszaros@apple.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Steven Hawkins <shawkins@redhat.com>
1 parent b240aef commit cd2a5ff

2 files changed

Lines changed: 25 additions & 10 deletions

File tree

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) {
191191

192192
@Override
193193
public Optional<R> get(ResourceID resourceID) {
194+
// The order of reading from these caches matters
194195
Optional<R> resource = temporaryResourceCache.getResourceFromCache(resourceID);
195-
var res = cache.get(resourceID);
196196
if (comparableResourceVersions
197197
&& resource.isPresent()
198198
&& ReconcilerUtilsInternal.compareResourceVersions(
@@ -201,13 +201,16 @@ public Optional<R> get(ResourceID resourceID) {
201201
> 0) {
202202
log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID);
203203
return resource;
204+
} else {
205+
// this needs to happen after comparison to ensure correctness
206+
var resFromInformer = cache.get(resourceID);
207+
log.debug(
208+
"Resource {} in temp cache; {}found in informer cache" + ", for Resource ID: {}",
209+
resource.isPresent() ? "obsolete" : "not found",
210+
resFromInformer.isPresent() ? "" : "not ",
211+
resourceID);
212+
return resFromInformer;
204213
}
205-
log.debug(
206-
"Resource not found, or older, in temporary cache. Found in informer cache {}, for"
207-
+ " Resource ID: {}",
208-
res.isPresent(),
209-
resourceID);
210-
return res;
211214
}
212215

213216
/**

operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,27 +43,39 @@ public UpdateControl<CachingFilteringUpdateCustomResource> reconcile(
4343
CachingFilteringUpdateCustomResource resource,
4444
Context<CachingFilteringUpdateCustomResource> context) {
4545

46-
context.resourceOperations().serverSideApply(prepareCM(resource));
46+
context.resourceOperations().serverSideApply(prepareCM(resource, 1));
4747
var cachedCM = context.getSecondaryResource(ConfigMap.class);
4848
if (cachedCM.isEmpty()) {
4949
issueFound.set(true);
5050
throw new IllegalStateException("Error for resource: " + ResourceID.fromResource(resource));
5151
}
5252

53+
var updated = context.resourceOperations().serverSideApply(prepareCM(resource, 2));
54+
cachedCM = context.getSecondaryResource(ConfigMap.class);
55+
if (!cachedCM
56+
.orElseThrow()
57+
.getMetadata()
58+
.getResourceVersion()
59+
.equals(updated.getMetadata().getResourceVersion())) {
60+
issueFound.set(true);
61+
throw new IllegalStateException(
62+
"Update error for resource: " + ResourceID.fromResource(resource));
63+
}
64+
5365
ensureStatusExists(resource);
5466
resource.getStatus().setUpdated(true);
5567
return UpdateControl.patchStatus(resource);
5668
}
5769

58-
private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p) {
70+
private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p, int num) {
5971
var cm =
6072
new ConfigMapBuilder()
6173
.withMetadata(
6274
new ObjectMetaBuilder()
6375
.withName(p.getMetadata().getName())
6476
.withNamespace(p.getMetadata().getNamespace())
6577
.build())
66-
.withData(Map.of("name", p.getMetadata().getName()))
78+
.withData(Map.of("name", p.getMetadata().getName(), "num", "" + num))
6779
.build();
6880
cm.addOwnerReference(p);
6981
return cm;

0 commit comments

Comments
 (0)