Skip to content

Commit 9a2d5a3

Browse files
committed
improve: status cache for next reconciliation - only the lock version
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 4caf4d0 commit 9a2d5a3

19 files changed

+35
-847
lines changed

docs/content/en/docs/documentation/reconciler.md

Lines changed: 9 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -182,16 +182,12 @@ require the latest status version possible, for example, if the status subresour
182182
See [Representing Allocated Values](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#representing-allocated-values)
183183
from the Kubernetes docs for more details.
184184

185-
The framework provides utilities to help with these use cases with
185+
The framework provides utilities to help with these use cases:
186186
[`PrimaryUpdateAndCacheUtils`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java).
187-
These utility methods come in multiple flavors:
188187

189-
#### Using internal cache
190-
191-
In almost all cases for this purpose, you can use internal caches in combination with update methods that use
192-
optimistic locking (end with *WithLock(...)). If the update method fails on optimistic locking, it will retry
193-
using a fresh resource from the server as base for modification. Again, this is the default option and will probably
194-
work for you.
188+
Framework you can use internal caches in combination with update methods that use
189+
optimistic locking. If the update method fails on optimistic locking, it will retry
190+
using a fresh resource from the server as base for modification.
195191

196192
```java
197193
@Override
@@ -204,88 +200,16 @@ public UpdateControl<StatusPatchCacheCustomResource> reconcile(
204200
var freshCopy = createFreshCopy(primary);
205201
freshCopy.getStatus().setValue(statusWithState());
206202

207-
var updatedResource = PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResourceWithLock(resource, freshCopy, context);
203+
var updatedResource = PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResource(resource, freshCopy, context);
208204

209205
return UpdateControl.noUpdate();
210206
}
211207
```
212208

213-
After the update `PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResourceWithLock` puts the result of the update into an internal
214-
cache and will the framework will make sure that the next reconciliation will contain the most recent version of the resource.
209+
After the update `PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResource` puts the response of the update into an internal
210+
cache and the framework will make sure that the next reconciliation will contain the most recent version of the resource.
215211
Note that it is not necessarily the version of the resource you got as response from the update, it can be newer since other parties
216-
can do additional updates meanwhile, but if not explicitly modified, it will contain the up-to-date status.
217-
218-
See related integration test [here](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/internalwithlock).
219-
220-
This approach works with the default configuration of the framework and should be good to go in most of the cases.
221-
222-
Without going further into the details, a bit more experimental way we provide overloaded methods without optimistic locking,
223-
to use those you have to set `ConfigurationService.parseResourceVersionsForEventFilteringAndCaching`
224-
to `true`. This in practice would mean that request won't fail on optimistic locking, but requires bending a bit
225-
the rules regarding Kubernetes API contract. This might be needed only if you have multiple resources frequently
226-
writing the resource.
227-
228-
#### Fallback approach: using `PrimaryResourceCache` cache
229-
230-
For the sake of completeness, we also provide a more explicit approach to manage the cache yourself.
231-
This approach has the advantage that you don't have to do neither optimistic locking nor
232-
setting the `parseResourceVersionsForEventFilteringAndCaching` to `true`:
233-
234-
```java
235-
236-
// We on purpose don't use the provided predicate to show what a custom one could look like.
237-
private final PrimaryResourceCache<StatusPatchPrimaryCacheCustomResource> cache =
238-
new PrimaryResourceCache<>(
239-
(statusPatchCacheCustomResourcePair, statusPatchCacheCustomResource) ->
240-
statusPatchCacheCustomResource.getStatus().getValue()
241-
>= statusPatchCacheCustomResourcePair.afterUpdate().getStatus().getValue());
242-
243-
@Override
244-
public UpdateControl<StatusPatchPrimaryCacheCustomResource> reconcile(
245-
StatusPatchPrimaryCacheCustomResource primary,
246-
Context<StatusPatchPrimaryCacheCustomResource> context) {
247-
248-
// cache will compare the current and the cached resource and return the more recent. (And evict the old)
249-
primary = cache.getFreshResource(primary);
250-
251-
// omitted logic
252-
253-
var freshCopy = createFreshCopy(primary);
254-
255-
freshCopy.getStatus().setValue(statusWithState());
256-
257-
var updated =
258-
PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResource(primary, freshCopy, context, cache);
259-
260-
return UpdateControl.noUpdate();
261-
}
262-
263-
@Override
264-
public DeleteControl cleanup(
265-
StatusPatchPrimaryCacheCustomResource resource,
266-
Context<StatusPatchPrimaryCacheCustomResource> context)
267-
throws Exception {
268-
// cleanup the cache on resource deletion
269-
cache.cleanup(resource);
270-
return DeleteControl.defaultDelete();
271-
}
272-
273-
```
274-
275-
[`PrimaryResourceCache`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/support/PrimaryResourceCache.java)
276-
is designed for this purpose. As shown in the example above, it is up to you to provide a predicate to determine if the
277-
resource is more recent than the one available. In other words, when to evict the resource from the cache. Typically, as
278-
shown in
279-
the [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/primarycache)
280-
you can have a counter in status to check on that.
281-
282-
Since all of this happens explicitly, you cannot use this approach for managed dependent resources and workflows and
283-
will need to use the unmanaged approach instead. This is due to the fact that managed dependent resources always get
284-
their associated primary resource from the underlying informer event source cache.
212+
can do additional updates meanwhile. However, if not explicitly modified, it will contain the up-to-date resource.
285213

286-
#### Additional remarks
214+
See related integration test [here](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache).
287215

288-
As shown in the last two cases, there is no optimistic locking used when updating the
289-
[resource](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/internal/StatusPatchCacheReconciler.java#L41)
290-
(in other words `metadata.resourceVersion` is set to `null`). This has nice property the request will be successful.
291-
However, it might be desirable to configure retry on [Fabric8 client](https://github.com/fabric8io/kubernetes-client?tab=readme-ov-file#configuring-the-client).

0 commit comments

Comments
 (0)