Skip to content

Commit ab6a6dc

Browse files
committed
refactor: let ResourceState own the trigger-on-all-events flag (#3549)
markEventReceived and unMarkEventReceived took a boolean that only ever selects which IllegalStateException guards apply, and every EventProcessor call site supplied it by re-reading the same controller configuration value. It can never differ between calls for a given processor, but nothing enforced that: a call site passing the wrong value would silently change which state transitions are legal, and the state machine could not be read without also reading its callers. Decide it once at the edge: ResourceStateManager takes the flag at construction (EventProcessor already knows it there) and passes it to each ResourceState, which keeps it as a final field. Both types are package-private, so this is self-contained.
1 parent 9bd5641 commit ab6a6dc

4 files changed

Lines changed: 30 additions & 21 deletions

File tree

‎operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class EventProcessor<P extends HasMetadata> implements EventHandler, Life
6464
private final Cache<P> cache;
6565
private final EventSourceManager<P> eventSourceManager;
6666
private final RateLimiter<? extends RateLimitState> rateLimiter;
67-
private final ResourceStateManager resourceStateManager = new ResourceStateManager();
67+
private final ResourceStateManager resourceStateManager;
6868
private final Map<String, Object> metricsMetadata;
6969
private ExecutorService executor;
7070

@@ -107,6 +107,8 @@ private EventProcessor(
107107
this.metrics = metrics != null ? metrics : Metrics.NOOP;
108108
this.eventSourceManager = eventSourceManager;
109109
this.rateLimiter = controllerConfiguration.getRateLimiter();
110+
this.resourceStateManager =
111+
new ResourceStateManager(controllerConfiguration.triggerReconcilerOnAllEvents());
110112

111113
metricsMetadata =
112114
Optional.ofNullable(eventSourceManager.getController())
@@ -194,7 +196,7 @@ private void submitReconciliationExecution(ResourceState state) {
194196
state.getRetry(),
195197
state.deleteEventPresent(),
196198
state.isDeleteFinalStateUnknown());
197-
state.unMarkEventReceived(triggerOnAllEvents());
199+
state.unMarkEventReceived();
198200
metrics.reconciliationSubmitted(latest, state.getRetry(), metricsMetadata);
199201
log.debug("Executing events for custom resource. Scope: {}", executionScope);
200202
executor.execute(new ReconcilerExecutor(resourceID, executionScope));
@@ -249,10 +251,10 @@ private void handleEventMarking(Event event, ResourceState state) {
249251
// removed, but also the informers websocket is disconnected and later reconnected. So
250252
// meanwhile the resource could be deleted and recreated. In this case we just mark a new
251253
// event as below.
252-
state.markEventReceived(triggerOnAllEvents());
254+
state.markEventReceived();
253255
}
254256
} else if (!state.deleteEventPresent() && !state.processedMarkForDeletionPresent()) {
255-
state.markEventReceived(triggerOnAllEvents());
257+
state.markEventReceived();
256258
} else if (isTriggerOnAllEventAndDeleteEventPresent(state)) {
257259
state.markAdditionalEventAfterDeleteEvent();
258260
} else if (log.isDebugEnabled()) {
@@ -381,7 +383,7 @@ private void handleRetryOnException(
381383
boolean eventPresent =
382384
state.eventPresent()
383385
|| (triggerOnAllEvents() && state.isAdditionalEventPresentAfterDeleteEvent());
384-
state.markEventReceived(triggerOnAllEvents());
386+
state.markEventReceived();
385387
retryAwareErrorLogging(
386388
state.getRetry(), eventPresent, errorHandledByReconciler, exception, executionScope);
387389
metrics.reconciliationFailed(

‎operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceState.java‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ private enum EventingState {
4747
}
4848

4949
private final ResourceID id;
50+
private final boolean triggerOnAllEvents;
5051

5152
private boolean underProcessing;
5253
private RetryExecution retry;
@@ -55,8 +56,9 @@ private enum EventingState {
5556
private HasMetadata lastKnownResource;
5657
private boolean isDeleteFinalStateUnknown = false;
5758

58-
public ResourceState(ResourceID id) {
59+
public ResourceState(ResourceID id, boolean triggerOnAllEvents) {
5960
this.id = id;
61+
this.triggerOnAllEvents = triggerOnAllEvents;
6062
eventing = EventingState.NO_EVENT_PRESENT;
6163
}
6264

@@ -108,8 +110,8 @@ public boolean processedMarkForDeletionPresent() {
108110
return eventing == EventingState.PROCESSED_MARK_FOR_DELETION;
109111
}
110112

111-
public void markEventReceived(boolean isAllEventMode) {
112-
if (!isAllEventMode && deleteEventPresent()) {
113+
public void markEventReceived() {
114+
if (!triggerOnAllEvents && deleteEventPresent()) {
113115
throw new IllegalStateException("Cannot receive event after a delete event received");
114116
}
115117
log.debug("Marking event received for: {}", getId());
@@ -151,20 +153,20 @@ public HasMetadata getLastKnownResource() {
151153
return lastKnownResource;
152154
}
153155

154-
public void unMarkEventReceived(boolean isAllEventReconcileMode) {
156+
public void unMarkEventReceived() {
155157
switch (eventing) {
156158
case EVENT_PRESENT:
157159
eventing = EventingState.NO_EVENT_PRESENT;
158160
break;
159161
case PROCESSED_MARK_FOR_DELETION:
160162
throw new IllegalStateException("Cannot unmark processed marked for deletion.");
161163
case DELETE_EVENT_PRESENT:
162-
if (!isAllEventReconcileMode) {
164+
if (!triggerOnAllEvents) {
163165
throw new IllegalStateException("Cannot unmark delete event.");
164166
}
165167
break;
166168
case ADDITIONAL_EVENT_PRESENT_AFTER_DELETE_EVENT:
167-
if (!isAllEventReconcileMode) {
169+
if (!triggerOnAllEvents) {
168170
throw new IllegalStateException(
169171
"This state should not happen in non all-event-reconciliation mode");
170172
}

‎operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class ResourceStateManager {
2828
// will process to avoid under- or over-sizing the state maps and avoid too many resizing that
2929
// take time and memory?
3030
private final Map<ResourceID, ResourceState> states = new ConcurrentHashMap<>(100);
31+
private final boolean triggerOnAllEvents;
32+
33+
public ResourceStateManager(boolean triggerOnAllEvents) {
34+
this.triggerOnAllEvents = triggerOnAllEvents;
35+
}
3136

3237
public Optional<ResourceState> getOrCreateOnResourceEvent(Event event) {
3338
var resourceId = event.getRelatedCustomResourceID();
@@ -36,7 +41,7 @@ public Optional<ResourceState> getOrCreateOnResourceEvent(Event event) {
3641
return Optional.of(state);
3742
}
3843
if (event instanceof ResourceEvent) {
39-
state = new ResourceState(resourceId);
44+
state = new ResourceState(resourceId, triggerOnAllEvents);
4045
states.put(resourceId, state);
4146
return Optional.of(state);
4247
} else {
@@ -45,7 +50,7 @@ public Optional<ResourceState> getOrCreateOnResourceEvent(Event event) {
4550
}
4651

4752
public ResourceState getOrCreate(ResourceID resourceID) {
48-
return states.computeIfAbsent(resourceID, ResourceState::new);
53+
return states.computeIfAbsent(resourceID, id -> new ResourceState(id, triggerOnAllEvents));
4954
}
5055

5156
public Optional<ResourceState> get(ResourceID resourceID) {

‎operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
class ResourceStateManagerTest {
2929

30-
private final ResourceStateManager manager = new ResourceStateManager();
30+
private final ResourceStateManager manager = new ResourceStateManager(false);
3131
private final ResourceID sampleResourceID = new ResourceID("test-name");
3232
private final ResourceID sampleResourceID2 = new ResourceID("test-name2");
3333
private ResourceState state;
@@ -49,7 +49,7 @@ public void returnsNoEventPresentIfNotMarkedYet() {
4949

5050
@Test
5151
public void marksEvent() {
52-
state.markEventReceived(false);
52+
state.markEventReceived();
5353

5454
assertThat(state.eventPresent()).isTrue();
5555
assertThat(state.deleteEventPresent()).isFalse();
@@ -65,7 +65,7 @@ public void marksDeleteEvent() {
6565

6666
@Test
6767
public void afterDeleteEventMarkEventIsNotRelevant() {
68-
state.markEventReceived(false);
68+
state.markEventReceived();
6969

7070
state.markDeleteEventReceived(TestUtils.testCustomResource(), true);
7171

@@ -75,7 +75,7 @@ public void afterDeleteEventMarkEventIsNotRelevant() {
7575

7676
@Test
7777
public void cleansUp() {
78-
state.markEventReceived(false);
78+
state.markEventReceived();
7979
state.markDeleteEventReceived(TestUtils.testCustomResource(), true);
8080

8181
manager.remove(sampleResourceID);
@@ -91,15 +91,15 @@ public void cannotMarkEventAfterDeleteEventReceived() {
9191
IllegalStateException.class,
9292
() -> {
9393
state.markDeleteEventReceived(TestUtils.testCustomResource(), true);
94-
state.markEventReceived(false);
94+
state.markEventReceived();
9595
});
9696
}
9797

9898
@Test
9999
public void listsResourceIDSWithEventsPresent() {
100-
state.markEventReceived(false);
101-
state2.markEventReceived(false);
102-
state.unMarkEventReceived(false);
100+
state.markEventReceived();
101+
state2.markEventReceived();
102+
state.unMarkEventReceived();
103103

104104
var res = manager.resourcesWithEventPresent();
105105

0 commit comments

Comments
 (0)