diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java index 9471d52cc4..e02725c3d0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java @@ -33,6 +33,7 @@ public abstract class AbstractDependentResource private final boolean updatable = this instanceof Updater; private final boolean deletable = this instanceof Deleter; private final DependentResourceReconciler dependentResourceReconciler; + private final ThreadLocal desiredCache = new ThreadLocal<>(); protected Creator creator; protected Updater updater; protected String name; @@ -67,52 +68,56 @@ public ReconcileResult reconcile(P primary, Context

context) { } protected ReconcileResult reconcile(P primary, R actualResource, Context

context) { - if (creatable() || updatable()) { - if (actualResource == null) { - if (creatable) { - var desired = desired(primary, context); - throwIfNull(desired, primary, "Desired"); - logForOperation("Creating", primary, desired); - var createdResource = handleCreate(desired, primary, context); - return ReconcileResult.resourceCreated(createdResource); - } - } else { - if (updatable()) { - final Matcher.Result match = match(actualResource, primary, context); - if (!match.matched()) { - final var desired = match.computedDesired().orElseGet(() -> desired(primary, context)); + try { + if (creatable() || updatable()) { + if (actualResource == null) { + if (creatable) { + var desired = cachedDesired(primary, context); throwIfNull(desired, primary, "Desired"); - logForOperation("Updating", primary, desired); - var updatedResource = handleUpdate(actualResource, desired, primary, context); - return ReconcileResult.resourceUpdated(updatedResource); + logForOperation("Creating", primary, desired); + var createdResource = handleCreate(desired, primary, context); + return ReconcileResult.resourceCreated(createdResource); + } + } else { + if (updatable()) { + final Result match = match(actualResource, primary, context); + if (!match.matched()) { + final var desired = + match.computedDesired().orElseGet(() -> cachedDesired(primary, context)); + throwIfNull(desired, primary, "Desired"); + logForOperation("Updating", primary, desired); + var updatedResource = handleUpdate(actualResource, desired, primary, context); + return ReconcileResult.resourceUpdated(updatedResource); + } else { + log.debug( + "Update skipped for dependent {} as it matched the existing one", + actualResource instanceof HasMetadata + ? ResourceID.fromResource((HasMetadata) actualResource) + : getClass().getSimpleName()); + } } else { log.debug( - "Update skipped for dependent {} as it matched the existing one", + "Update skipped for dependent {} implement Updater interface to modify it", actualResource instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) actualResource) : getClass().getSimpleName()); } - } else { - log.debug( - "Update skipped for dependent {} implement Updater interface to modify it", - actualResource instanceof HasMetadata - ? ResourceID.fromResource((HasMetadata) actualResource) - : getClass().getSimpleName()); } + } else { + log.debug( + "Dependent {} is read-only, implement Creator and/or Updater interfaces to modify it", + getClass().getSimpleName()); } - } else { - log.debug( - "Dependent {} is read-only, implement Creator and/or Updater interfaces to modify it", - getClass().getSimpleName()); + return ReconcileResult.noOperation(actualResource); + } finally { + desiredCache.remove(); } - return ReconcileResult.noOperation(actualResource); } public abstract Result match(R resource, P primary, Context

context); @Override public Optional getSecondaryResource(P primary, Context

context) { - var secondaryResources = context.getSecondaryResources(resourceType()); if (secondaryResources.isEmpty()) { return Optional.empty(); @@ -136,7 +141,7 @@ public Optional getSecondaryResource(P primary, Context

context) { */ protected Optional selectTargetSecondaryResource( Set secondaryResources, P primary, Context

context) { - R desired = desired(primary, context); + R desired = cachedDesired(primary, context); var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList(); if (targetResources.size() > 1) { throw new IllegalStateException( @@ -199,6 +204,16 @@ protected R handleUpdate(R actual, R desired, P primary, Context

context) { return updated; } + protected R cachedDesired(P primary, Context

context) { + var desired = desiredCache.get(); + if (desired != null) { + return desired; + } + desired = desired(primary, context); + desiredCache.set(desired); + return desired; + } + protected R desired(P primary, Context

context) { throw new IllegalStateException( "desired method must be implemented if this DependentResource can be created and/or" diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java index 4c828b7eb9..3bbd785160 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java @@ -83,7 +83,7 @@ protected void handleExplicitStateCreation(P primary, R created, Context

cont @Override public Matcher.Result match(R resource, P primary, Context

context) { - var desired = desired(primary, context); + var desired = cachedDesired(primary, context); return Matcher.Result.computed(resource.equals(desired), desired); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java index f96c28d60a..f0e5d40bd4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java @@ -123,7 +123,7 @@ public static Matcher.Result m Context

context, boolean labelsAndAnnotationsEquality, String... ignorePaths) { - final var desired = dependentResource.desired(primary, context); + final var desired = dependentResource.cachedDesired(primary, context); return match(desired, actualResource, labelsAndAnnotationsEquality, context, ignorePaths); } @@ -135,7 +135,7 @@ public static Matcher.Result m boolean specEquality, boolean labelsAndAnnotationsEquality, String... ignorePaths) { - final var desired = dependentResource.desired(primary, context); + final var desired = dependentResource.cachedDesired(primary, context); return match( desired, actualResource, labelsAndAnnotationsEquality, specEquality, context, ignorePaths); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java index ebd6089aa7..9b3fcefdda 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java @@ -107,7 +107,7 @@ public R update(R actual, R desired, P primary, Context

context) { @Override public Result match(R actualResource, P primary, Context

context) { - final var desired = desired(primary, context); + final var desired = cachedDesired(primary, context); return match(actualResource, desired, primary, context); } @@ -286,7 +286,7 @@ protected Optional selectTargetSecondaryResource( * @return id of the target managed resource */ protected ResourceID targetSecondaryResourceID(P primary, Context

context) { - return ResourceID.fromResource(desired(primary, context)); + return ResourceID.fromResource(cachedDesired(primary, context)); } protected boolean addOwnerReference() { @@ -294,8 +294,8 @@ protected boolean addOwnerReference() { } @Override - protected R desired(P primary, Context

context) { - return super.desired(primary, context); + protected R cachedDesired(P primary, Context

context) { + return super.cachedDesired(primary, context); } @Override