Skip to content

Commit b491b6a

Browse files
csviriCopilotmetacosm
authored
docs: blog about 5.3.0 release (#3205)
Signed-off-by: Attila Mészáros <a_meszaros@apple.com> Signed-off-by: Chris Laprun <metacosm@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Chris Laprun <metacosm@gmail.com>
1 parent 3ec985d commit b491b6a

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
---
2+
title: Version 5.3 Released!
3+
date: 2026-03-13
4+
author: >-
5+
[Attila Mészáros](https://github.com/csviri)
6+
---
7+
8+
We're pleased to announce the release of Java Operator SDK v5.3.0! This minor version brings
9+
two headline features — read-cache-after-write consistency and a new metrics implementation —
10+
along with a configuration adapter system, MDC improvements, and a number of smaller improvements
11+
and cleanups.
12+
13+
## Key Features
14+
15+
### Read-cache-after-write Consistency and Event Filtering
16+
17+
This is the headline feature of 5.3. Informer caches are inherently eventually consistent: after
18+
your reconciler updates a resource, there is a window of time before the change is visible in the
19+
cache. This can cause subtle bugs, particularly when storing allocated values in the status
20+
sub-resource and reading them back in the next reconciliation.
21+
22+
From 5.3.0, the framework provides two guarantees when you use
23+
[`ResourceOperations`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceOperations.java)
24+
(accessible from `Context`):
25+
26+
1. **Read-after-write**: Reading from the cache after your update — even within the same
27+
reconciliation — returns at least the version of the resource from your update response.
28+
2. **Event filtering**: Events produced by your own writes no longer trigger a redundant
29+
reconciliation.
30+
31+
`UpdateControl` and `ErrorStatusUpdateControl` use this automatically. Secondary resources benefit
32+
via `context.resourceOperations()`:
33+
34+
```java
35+
public UpdateControl<WebPage> reconcile(WebPage webPage, Context<WebPage> context) {
36+
37+
ConfigMap managedConfigMap = prepareConfigMap(webPage);
38+
// update is cached and will suppress the resulting event
39+
context.resourceOperations().serverSideApply(managedConfigMap);
40+
41+
// fresh resource instantly available from the cache
42+
var upToDateResource = context.getSecondaryResource(ConfigMap.class);
43+
44+
makeStatusChanges(webPage);
45+
// UpdateControl also uses this by default
46+
return UpdateControl.patchStatus(webPage);
47+
}
48+
```
49+
50+
If your reconciler relied on being re-triggered by its own writes, a new `reschedule()` method on
51+
`UpdateControl` lets you explicitly request an immediate re-queue.
52+
53+
> **Note**: `InformerEventSource.list(..)` bypasses the additional caches and will not reflect
54+
> in-flight updates. Use `context.getSecondaryResources(..)` or `InformerEventSource.get(ResourceID)`
55+
> instead.
56+
57+
See the [reconciler docs](/docs/documentation/reconciler#read-cache-after-write-consistency-and-event-filtering) for details.
58+
59+
### MicrometerMetricsV2
60+
61+
A new micrometer-based `Metrics` implementation designed with low cardinality in mind. All meters
62+
are scoped to the controller, not to individual resources, avoiding unbounded cardinality growth as
63+
resources come and go.
64+
65+
```java
66+
MeterRegistry registry; // initialize your registry
67+
Metrics metrics = MicrometerMetricsV2.newBuilder(registry).build();
68+
Operator operator = new Operator(client, o -> o.withMetrics(metrics));
69+
```
70+
71+
Optionally attach a `namespace` tag to per-reconciliation counters (disabled by default):
72+
73+
```java
74+
Metrics metrics = MicrometerMetricsV2.newBuilder(registry)
75+
.withNamespaceAsTag()
76+
.build();
77+
```
78+
79+
The full list of meters:
80+
81+
| Meter | Type | Description |
82+
|---|---|---|
83+
| `reconciliations.active` | gauge | Reconciler executions currently running |
84+
| `reconciliations.queue` | gauge | Resources queued for reconciliation |
85+
| `custom_resources` | gauge | Resources tracked by the controller |
86+
| `reconciliations.execution.duration` | timer | Execution duration with explicit histogram buckets |
87+
| `reconciliations.started.total` | counter | Reconciliations started |
88+
| `reconciliations.success.total` | counter | Successful reconciliations |
89+
| `reconciliations.failure.total` | counter | Failed reconciliations |
90+
| `reconciliations.retries.total` | counter | Retry attempts |
91+
| `events.received` | counter | Kubernetes events received |
92+
93+
The execution timer uses explicit bucket boundaries (10ms–30s) to ensure compatibility with
94+
`histogram_quantile()` in both `PrometheusMeterRegistry` and `OtlpMeterRegistry`.
95+
96+
A ready-to-use **Grafana dashboard** is included at
97+
[`observability/josdk-operator-metrics-dashboard.json`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/observability/josdk-operator-metrics-dashboard.json).
98+
99+
The
100+
[`metrics-processing` sample operator](https://github.com/java-operator-sdk/java-operator-sdk/tree/main/sample-operators/metrics-processing)
101+
provides a complete end-to-end setup with Prometheus, Grafana, and an OpenTelemetry Collector,
102+
installable via `observability/install-observability.sh`. This is a good starting point for
103+
verifying metrics in a real cluster.
104+
105+
> **Deprecated**: The original `MicrometerMetrics` (V1) is deprecated as of 5.3.0. It attaches
106+
> resource-specific metadata as tags to every meter, causing unbounded cardinality. Migrate to
107+
> `MicrometerMetricsV2`.
108+
109+
See the [observability docs](/docs/documentation/observability#micrometermetricsv2) for the full
110+
reference.
111+
112+
### Configuration Adapters
113+
114+
A new `ConfigLoader` bridges any key-value configuration source to the JOSDK operator and
115+
controller configuration APIs. This lets you drive operator behaviour from environment variables,
116+
system properties, YAML files, or any config library without writing glue code by hand.
117+
118+
The default instance stacks environment variables over system properties out of the box:
119+
120+
```java
121+
Operator operator = new Operator(ConfigLoader.getDefault().applyConfigs());
122+
```
123+
124+
Built-in providers: `EnvVarConfigProvider`, `PropertiesConfigProvider`, `YamlConfigProvider`,
125+
and `AggregatePriorityListConfigProvider` for explicit priority ordering.
126+
127+
`ConfigProvider` is a single-method interface, so adapting any config library (MicroProfile Config,
128+
SmallRye Config, etc.) takes only a few lines:
129+
130+
```java
131+
public class SmallRyeConfigProvider implements ConfigProvider {
132+
private final SmallRyeConfig config;
133+
134+
@Override
135+
public <T> Optional<T> getValue(String key, Class<T> type) {
136+
return config.getOptionalValue(key, type);
137+
}
138+
}
139+
```
140+
141+
Pass the results when constructing the operator and registering reconcilers:
142+
143+
```java
144+
var configLoader = new ConfigLoader(new SmallRyeConfigProvider(smallRyeConfig));
145+
146+
Operator operator = new Operator(configLoader.applyConfigs());
147+
operator.register(new MyReconciler(), configLoader.applyControllerConfigs(MyReconciler.NAME));
148+
```
149+
150+
See the [configuration docs](/docs/documentation/configuration#loading-configuration-from-external-sources)
151+
for the full list of supported keys.
152+
153+
> **Note**: This new configuration mechanism is useful when using the SDK by itself. Framework (Spring Boot, Quarkus, …)
154+
> integrations usually provide their own configuration mechanisms that should be used instead of this new mechanism.
155+
156+
### MDC Improvements
157+
158+
**MDC in workflow execution**: MDC context is now propagated through workflow (dependent resource
159+
graph) execution threads, not just the top-level reconciler thread. Logging from dependent
160+
resources now carries the same contextual fields as the primary reconciliation.
161+
162+
**`NO_NAMESPACE` for cluster-scoped resources**: Instead of omitting the `resource.namespace` MDC
163+
key for cluster-scoped resources, the framework now emits `MDCUtils.NO_NAMESPACE`. This makes log
164+
queries for cluster-scoped resources reliable.
165+
166+
### De-duplicated Secondary Resources from Context
167+
168+
When multiple event sources manage the same resource type, `context.getSecondaryResources(..)` now
169+
returns a de-duplicated stream. When the same resource appears from more than one source, only the
170+
copy with the highest resource version is returned.
171+
172+
### Record Desired State in Context
173+
174+
Dependent resources now record their desired state in the `Context` during reconciliation. This allows reconcilers and
175+
downstream dependents in a workflow to inspect what a dependent resource computed as its desired state and guarantees
176+
that the desired state is computed only once per reconciliation.
177+
178+
### Informer Health Checks
179+
180+
Informer health checks no longer rely on `isWatching`. For readiness and startup probes, you should
181+
primarily use `hasSynced`. Once an informer has started, `isWatching` is not suitable for liveness
182+
checks.
183+
184+
## Additional Improvements
185+
186+
- **Annotation removal using locking**: Finalizer and annotation management no longer uses
187+
`createOrReplace`; a locking-based `createOrUpdate` avoids conflicts under concurrent updates.
188+
- **`KubernetesDependentResource` uses `ResourceOperations` directly**, removing an indirection
189+
layer and automatically benefiting from the read-after-write guarantees.
190+
- **Skip namespace deletion in JUnit extension**: The JUnit extension now supports a flag to skip
191+
namespace deletion after a test run, useful for debugging CI failures.
192+
- **`ManagedInformerEventSource.getCachedValue()` deprecated**: Use
193+
`context.getSecondaryResource(..)` instead.
194+
- **Improved event filtering for multiple parallel updates**: The filtering algorithm now handles
195+
cases where multiple parallel updates are in flight for the same resource.
196+
- `exitOnStopLeading` is being prepared for removal from the public API.
197+
198+
## Migration Notes
199+
200+
### JUnit module rename
201+
202+
```xml
203+
<!-- before -->
204+
<artifactId>operator-framework-junit-5</artifactId>
205+
<!-- after -->
206+
<artifactId>operator-framework-junit</artifactId>
207+
```
208+
209+
### `Metrics` interface renames
210+
211+
| v5.2 | v5.3 |
212+
|---|---|
213+
| `reconcileCustomResource` | `reconciliationSubmitted` |
214+
| `reconciliationExecutionStarted` | `reconciliationStarted` |
215+
| `reconciliationExecutionFinished` | `reconciliationSucceeded` |
216+
| `failedReconciliation` | `reconciliationFailed` |
217+
| `finishedReconciliation` | `reconciliationFinished` |
218+
| `cleanupDoneFor` | `cleanupDone` |
219+
| `receivedEvent` | `eventReceived` |
220+
221+
`reconciliationFinished(..)` is extended with `RetryInfo`. `monitorSizeOf(..)` is removed.
222+
223+
See the full [migration guide](/docs/migration/v5-3-migration) for details.
224+
225+
## Getting Started
226+
227+
```xml
228+
<dependency>
229+
<groupId>io.javaoperatorsdk</groupId>
230+
<artifactId>operator-framework</artifactId>
231+
<version>5.3.0</version>
232+
</dependency>
233+
```
234+
235+
## All Changes
236+
237+
See the [comparison view](https://github.com/operator-framework/java-operator-sdk/compare/v5.2.0...v5.3.0)
238+
for the full list of changes.
239+
240+
## Feedback
241+
242+
Please report issues or suggest improvements on our
243+
[GitHub repository](https://github.com/operator-framework/java-operator-sdk/issues).
244+
245+
Happy operator building! 🚀

0 commit comments

Comments
 (0)