Skip to content

Commit e6a7426

Browse files
committed
test: deflake additionalEventDuringRetryOnDeleteEvent
The test released the blocked reconciler right after the update call returned, but the update event still had to travel back through the informer. When the reconciliation failed before the event was registered, the framework treated the failure as a plain retry (consuming the last attempt) instead of instantly re-triggering because of a superseding event, so only 4 instead of 5 reconciliations happened. Wait for isNextReconciliationImminent() in the reconciler, which reflects exactly the state the framework checks after the reconciliation fails and cannot be unset while it is in progress. Also fix the isWaiting() assertion that had no terminal assertion and was therefore a no-op.
1 parent 748f3a3 commit e6a7426

2 files changed

Lines changed: 18 additions & 3 deletions

File tree

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/triggerallevent/eventing/TriggerReconcilerOnAllEventIT.java‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,16 @@ void additionalEventDuringRetryOnDeleteEvent() {
192192
await()
193193
.untilAsserted(
194194
() -> {
195-
assertThat(reconciler.isWaiting());
195+
assertThat(reconciler.isWaiting()).isTrue();
196196
});
197197

198198
// trigger reconciliation while waiting in reconciler
199199
res = getResource();
200200
res.getMetadata().getAnnotations().put("my-annotation", "true");
201201
extension.update(res);
202-
// continue reconciliation
202+
// continue reconciliation; the reconciler additionally waits until the framework actually
203+
// registered the event above, otherwise the failure below would consume a retry attempt
204+
// instead of being instantly re-triggered by the superseding event
203205
reconciler.setContinuerOnRetryWait(true);
204206

205207
await()

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/triggerallevent/eventing/TriggerReconcilerOnAllEventReconciler.java‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public class TriggerReconcilerOnAllEventReconciler
3535
public static final String ADDITIONAL_FINALIZER = "all.event.mode/finalizer2";
3636
public static final String NO_MORE_EXCEPTION_ANNOTATION_KEY = "no.more.exception";
3737

38+
// safety net so a missing event does not block the reconciler thread forever, the test assertions
39+
// fail long before this elapses
40+
private static final long MAX_WAIT_FOR_SUPERSEDING_EVENT_MILLIS = 30_000;
41+
3842
private static final Logger log =
3943
LoggerFactory.getLogger(TriggerReconcilerOnAllEventReconciler.class);
4044

@@ -82,7 +86,16 @@ public UpdateControl<TriggerReconcilerOnAllEventCustomResource> reconcile(
8286
&& context.getRetryInfo().isPresent()
8387
&& context.getRetryInfo().orElseThrow().getAttemptCount() == 1) {
8488
waiting = true;
85-
while (!continuerOnRetryWait) {
89+
// Releasing on continuerOnRetryWait alone is racy: the test sets that flag right after the
90+
// update call returns, but the update event still has to travel back through the informer.
91+
// If this reconciliation failed before the event was registered, the framework would treat
92+
// the failure as a plain retry (consuming the last attempt) instead of instantly
93+
// re-triggering because of a superseding event. isNextReconciliationImminent() reflects
94+
// exactly the state (event marked as received) the framework checks after this
95+
// reconciliation fails, and it cannot be unset while this reconciliation is in progress.
96+
var waitUntil = System.currentTimeMillis() + MAX_WAIT_FOR_SUPERSEDING_EVENT_MILLIS;
97+
while ((!continuerOnRetryWait || !context.isNextReconciliationImminent())
98+
&& System.currentTimeMillis() < waitUntil) {
8699
Thread.sleep(50);
87100
}
88101
waiting = false;

0 commit comments

Comments
 (0)