diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java
index bdaf575814..ea1df583a0 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java
@@ -102,8 +102,16 @@ public synchronized void handleEvent(Event event) {
try {
log.debug("Received event: {}", event);
+ final var optionalState = resourceStateManager.getOrCreateOnResourceEvent(event);
+ if (optionalState.isEmpty()) {
+ log.debug(
+ "Skipping event, since no state present and it is not a resource event. Resource ID:"
+ + " {}",
+ event.getRelatedCustomResourceID());
+ return;
+ }
+ var state = optionalState.orElseThrow();
final var resourceID = event.getRelatedCustomResourceID();
- final var state = resourceStateManager.getOrCreate(event.getRelatedCustomResourceID());
MDCUtils.addResourceIDInfo(resourceID);
metrics.receivedEvent(event, metricsMetadata);
handleEventMarking(event, state);
@@ -231,7 +239,7 @@ synchronized void eventProcessingFinished(
return;
}
ResourceID resourceID = executionScope.getResourceID();
- final var state = resourceStateManager.getOrCreate(resourceID);
+ final var state = resourceStateManager.getOrThrow(resourceID);
log.debug(
"Event processing finished. Scope: {}, PostExecutionControl: {}",
executionScope,
@@ -378,13 +386,13 @@ private void cleanupOnSuccessfulExecution(ExecutionScope
executionScope) {
log.debug(
"Cleanup for successful execution for resource: {}", getName(executionScope.getResource()));
if (isRetryConfigured()) {
- resourceStateManager.getOrCreate(executionScope.getResourceID()).setRetry(null);
+ resourceStateManager.getOrThrow(executionScope.getResourceID()).setRetry(null);
}
retryEventSource().cancelOnceSchedule(executionScope.getResourceID());
}
private ResourceState getOrInitRetryExecution(ExecutionScope
executionScope) {
- final var state = resourceStateManager.getOrCreate(executionScope.getResourceID());
+ final var state = resourceStateManager.getOrThrow(executionScope.getResourceID());
RetryExecution retryExecution = state.getRetry();
if (retryExecution == null) {
retryExecution = retry.initExecution();
@@ -404,7 +412,7 @@ private boolean isControllerUnderExecution(ResourceState state) {
}
private void unsetUnderExecution(ResourceID resourceID) {
- resourceStateManager.getOrCreate(resourceID).setUnderProcessing(false);
+ resourceStateManager.getOrThrow(resourceID).setUnderProcessing(false);
}
private boolean isRetryConfigured() {
@@ -430,7 +438,7 @@ public synchronized void start() throws OperatorException {
}
public boolean isNextReconciliationImminent(ResourceID resourceID) {
- return resourceStateManager.getOrCreate(resourceID).eventPresent();
+ return resourceStateManager.getOrThrow(resourceID).eventPresent();
}
private void handleAlreadyMarkedEvents() {
@@ -495,7 +503,7 @@ private String controllerName() {
}
public synchronized boolean isUnderProcessing(ResourceID resourceID) {
- return isControllerUnderExecution(resourceStateManager.getOrCreate(resourceID));
+ return isControllerUnderExecution(resourceStateManager.getOrThrow(resourceID));
}
public synchronized boolean isRunning() {
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java
index 6932e1ca5e..f45e317d94 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java
@@ -2,17 +2,39 @@
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
+import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEvent;
+
class ResourceStateManager {
// maybe we should have a way for users to specify a hint on the amount of CRs their reconciler
// will process to avoid under- or over-sizing the state maps and avoid too many resizing that
// take time and memory?
private final Map states = new ConcurrentHashMap<>(100);
- public ResourceState getOrCreate(ResourceID resourceID) {
- return states.computeIfAbsent(resourceID, ResourceState::new);
+ public Optional getOrCreateOnResourceEvent(Event event) {
+ var resourceId = event.getRelatedCustomResourceID();
+ var state = states.get(event.getRelatedCustomResourceID());
+ if (state != null) {
+ return Optional.of(state);
+ }
+ if (event instanceof ResourceEvent) {
+ state = new ResourceState(resourceId);
+ states.put(resourceId, state);
+ return Optional.of(state);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public ResourceState getOrThrow(ResourceID resourceID) {
+ var state = states.get(resourceID);
+ if (state == null) {
+ throw new IllegalStateException("No state for resource id: " + resourceID);
+ }
+ return state;
}
public ResourceState remove(ResourceID resourceID) {
diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java
index fe2e6e9514..9819eb7ee9 100644
--- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java
+++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java
@@ -276,6 +276,30 @@ void cancelScheduleOnceEventsOnSuccessfulExecution() {
verify(retryTimerEventSourceMock, times(1)).cancelOnceSchedule(eq(crID));
}
+ @Test
+ void skipsGenericEventIfNoResourceEventReceivedBefore() {
+ var crID = new ResourceID("test-cr", TEST_NAMESPACE);
+ eventProcessor =
+ spy(
+ new EventProcessor(
+ controllerConfiguration(null, LinearRateLimiter.deactivatedRateLimiter()),
+ reconciliationDispatcherMock,
+ eventSourceManagerMock,
+ metricsMock));
+
+ verify(reconciliationDispatcherMock, timeout(100).times(0)).handleExecution(any());
+
+ eventProcessor.start();
+ eventProcessor.handleEvent(new Event(crID));
+
+ await()
+ .pollDelay(Duration.ofMillis(100))
+ .untilAsserted(
+ () -> {
+ verify(reconciliationDispatcherMock, never()).handleExecution(any());
+ });
+ }
+
@Test
void startProcessedMarkedEventReceivedBefore() {
var crID = new ResourceID("test-cr", TEST_NAMESPACE);
@@ -287,7 +311,7 @@ void startProcessedMarkedEventReceivedBefore() {
eventSourceManagerMock,
metricsMock));
when(controllerEventSourceMock.get(eq(crID))).thenReturn(Optional.of(testCustomResource()));
- eventProcessor.handleEvent(new Event(crID));
+ eventProcessor.handleEvent(new ResourceEvent(ResourceAction.ADDED, crID, testCustomResource()));
verify(reconciliationDispatcherMock, timeout(100).times(0)).handleExecution(any());
diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java
index 2c4d9fa4f3..0d5c2868a7 100644
--- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java
+++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java
@@ -4,6 +4,10 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import io.javaoperatorsdk.operator.TestUtils;
+import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction;
+import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEvent;
+
import static org.assertj.core.api.Assertions.assertThat;
class ResourceStateManagerTest {
@@ -19,8 +23,8 @@ void init() {
manager.remove(sampleResourceID);
manager.remove(sampleResourceID2);
- state = manager.getOrCreate(sampleResourceID);
- state2 = manager.getOrCreate(sampleResourceID2);
+ state = manager.getOrThrow(sampleResourceID);
+ state2 = manager.getOrThrow(sampleResourceID2);
}
@Test
@@ -61,7 +65,7 @@ public void cleansUp() {
manager.remove(sampleResourceID);
- state = manager.getOrCreate(sampleResourceID);
+ state = manager.getOrThrow(sampleResourceID);
assertThat(state.deleteEventPresent()).isFalse();
assertThat(state.eventPresent()).isFalse();
}
@@ -87,4 +91,26 @@ public void listsResourceIDSWithEventsPresent() {
assertThat(res).hasSize(1);
assertThat(res.get(0).getId()).isEqualTo(sampleResourceID2);
}
+
+ @Test
+ void createStateOnlyOnResourceEvent() {
+ var state = manager.getOrCreateOnResourceEvent(new Event(new ResourceID("newEvent")));
+
+ assertThat(state).isEmpty();
+
+ state =
+ manager.getOrCreateOnResourceEvent(
+ new ResourceEvent(
+ ResourceAction.ADDED, new ResourceID("newEvent"), TestUtils.testCustomResource()));
+
+ assertThat(state).isNotNull();
+ }
+
+ @Test
+ void createsOnlyResourceEventReturnsPreviouslyCreatedState() {
+ manager.getOrThrow(new ResourceID("newEvent"));
+
+ var res = manager.getOrCreateOnResourceEvent(new Event(new ResourceID("newEvent")));
+ assertThat(res).isNotNull();
+ }
}