Skip to content

Commit 644ef9c

Browse files
committed
wip
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
1 parent 7bc3891 commit 644ef9c

File tree

3 files changed

+43
-11
lines changed

3 files changed

+43
-11
lines changed

docs/content/en/blog/news/read-after-write-consistency.md

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
title: Welcome read-cache-after-write consistency!
3-
# todo issue with this?
4-
#date: 2026-03-25
3+
date: 2026-03-13
54
author: >-
65
[Attila Mészáros](https://github.com/csviri)
76
---
@@ -42,6 +41,8 @@ much simpler to reason about!**
4241

4342
This post will deep dive in this topic, explore the details and rationale behind it.
4443

44+
See the related umbrella [issue](https://github.com/operator-framework/java-operator-sdk/issues/2944) on GitHub.
45+
4546
## Informers and eventual consistency
4647

4748
First we have to understand a fundamental building block of Kubernetes operators: Informers.
@@ -229,24 +230,54 @@ events that we received meanwhile, and make a decision to propagate any further
229230

230231
However, this way we significantly reduce the number of reconciliations, thus making the whole process much more efficient.
231232

233+
### The case for instant reschedule
234+
235+
We realize that some of our users might rely on the fact that the reconciliation is triggered by own updates.
236+
To support backwards compatibility or rather migration path, we provide now a way to instruct the framework
237+
to queue an instant reconciliation:
238+
239+
```java
240+
public UpdateControl<WebPage> reconcile(WebPage webPage, Context<WebPage> context) {
241+
242+
// omitted reconciliation logic
243+
244+
return UpdateControl.<WebPage>noUpdate().reschedule();
245+
}
246+
```
247+
232248
## Additional considerations and alternatives
233249

234250
An alternative approach would be that when we do an update we don't trigger the next reconciliation until the
235251
target resource is not in the Informers cache. The pro side of this is that we don't have to maintain an
236252
additional cache of the resource, just the target resource version; therefore there this appraoch might have
237-
a smaller memory footprint, but not necessarily.
253+
a smaller memory footprint, but not necessarily. This the related [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/5647-stale-controller-handling#proposal)
254+
that takes this approach.
238255

239256
On the other hand, when we do a request the response object is always deserialized, regardless if we are going
240257
to cache it or not. This object in most cases will be cached for a very short time; and later garbage collected.
258+
Therefore, the memory overhead should be minimal.
241259

260+
Having the TRC has an additional advantage, since we have the resource instantly in our cashes, we can in
261+
the same reconciliation elegantly continue on reconciliation and reconcile resources that are depending
262+
on the latest state. More concretely helps also for our [Dependent resources / Workflow](../../docs/documentation/dependent-resource-and-workflows/workflows.md#reconcile-sample)
263+
which rely on up-to-date caches. In this sense, this is much more optimal regarding throughput.
242264

265+
## Conclusion
243266

267+
I personally worked on a prototype of an Operator which was depending on an unreleased version of JOSDK already
268+
implementing these features. The most obvious gain was how much simpler the reasoning is in some cases and how it reduces the corner
269+
cases that we would have to solve otherwise with [expectation pattern](https://ahmet.im/blog/controller-pitfalls/#expectations-pattern)
270+
or other facilities.
244271

245-
## Conclusion
272+
## Special thanks
273+
274+
I would like to thank all the contributors that directly or indirectly contributed like [metacosm](https://github.com/metacosm),
275+
[manusa](https://github.com/manusa) and [xstefank](https://github.com/xstefank).
246276

247-
## Notes
277+
Last but certainly not least, specially thanks to [Steven Hawkins](https://github.com/shawkins)
278+
with who maintains the Informer implementation in [fabric8 Kubernetes client](https://github.com/fabric8io/kubernetes-client),
279+
implemented the first version of the algorithms, then we together iterated multiple times on it.
280+
Covering all the edge cases was quite an effort.
281+
Just to as a highlight I put here the [last one](https://github.com/operator-framework/java-operator-sdk/issues/3208).
248282

249-
TODO:
250-
- alternatives => deferring reconciliation, this is optimized for throughput
251-
- filter events
252-
- reschedule
283+
Thank you!

docs/content/en/blog/releases/v5-3-release.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ If your reconciler relied on being re-triggered by its own writes, a new `resche
5454
> in-flight updates. Use `context.getSecondaryResources(..)` or `InformerEventSource.get(ResourceID)`
5555
> instead.
5656
57-
See the [reconciler docs](/docs/documentation/reconciler#read-cache-after-write-consistency-and-event-filtering) for details.
57+
See the related [blog post](../news/read-after-write-consistency.md)
58+
and [reconciler docs](/docs/documentation/reconciler#read-cache-after-write-consistency-and-event-filtering) for details.
5859

5960
### MicrometerMetricsV2
6061

operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAReconciler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public UpdateControl<PatchResourceWithSSACustomResource> reconcile(
4242
res.setSpec(new PatchResourceWithSSASpec());
4343
res.getSpec().setControllerManagedValue(ADDED_VALUE);
4444
// test assumes we will run this in the next reconciliation
45-
return UpdateControl.patchResource(res).reschedule();
45+
return UpdateControl.<PatchResourceWithSSACustomResource>noUpdate().reschedule();
4646
} else {
4747
res.setStatus(new PatchResourceWithSSAStatus());
4848
res.getStatus().setSuccessfullyReconciled(true);

0 commit comments

Comments
 (0)