Skip to content

Commit 51a37fa

Browse files
authored
feat: retry remove finalizer (#1249)
1 parent 9ad8070 commit 51a37fa

File tree

15 files changed

+466
-87
lines changed

15 files changed

+466
-87
lines changed

micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ public void receivedEvent(Event event) {
6161
}
6262

6363
@Override
64-
public void cleanupDoneFor(ResourceID customResourceUid) {
65-
incrementCounter(customResourceUid, "events.delete");
64+
public void cleanupDoneFor(ResourceID resourceID) {
65+
incrementCounter(resourceID, "events.delete");
6666
}
6767

6868
@Override

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/monitoring/Metrics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ default void reconcileCustomResource(ResourceID resourceID, RetryInfo retryInfo)
1515

1616
default void failedReconciliation(ResourceID resourceID, Exception exception) {}
1717

18-
default void cleanupDoneFor(ResourceID customResourceUid) {}
18+
default void cleanupDoneFor(ResourceID resourceID) {}
1919

2020
default void finishedReconciliation(ResourceID resourceID) {}
2121

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.stream.Collectors;
77

88
import static io.javaoperatorsdk.operator.processing.event.EventMarker.EventingState.NO_EVENT_PRESENT;
9+
import static io.javaoperatorsdk.operator.processing.event.EventMarker.EventingState.PROCESSED_MARK_FOR_DELETION;
910

1011
/**
1112
* Manages the state of received events. Basically there can be only three distinct states relevant
@@ -18,8 +19,9 @@
1819
class EventMarker {
1920

2021
public enum EventingState {
21-
/** Event but NOT Delete event present */
2222
EVENT_PRESENT, NO_EVENT_PRESENT,
23+
/** Resource has been marked for deletion, and cleanup already executed successfully */
24+
PROCESSED_MARK_FOR_DELETION,
2325
/** Delete event present, from this point other events are not relevant */
2426
DELETE_EVENT_PRESENT,
2527
}
@@ -53,11 +55,21 @@ public void unMarkEventReceived(ResourceID resourceID) {
5355
setEventingState(resourceID,
5456
NO_EVENT_PRESENT);
5557
break;
58+
case PROCESSED_MARK_FOR_DELETION:
59+
throw new IllegalStateException("Cannot unmark processed marked for deletion.");
5660
case DELETE_EVENT_PRESENT:
5761
throw new IllegalStateException("Cannot unmark delete event.");
5862
}
5963
}
6064

65+
public void markProcessedMarkForDeletion(ResourceID resourceID) {
66+
setEventingState(resourceID, PROCESSED_MARK_FOR_DELETION);
67+
}
68+
69+
public boolean processedMarkForDeletionPresent(ResourceID resourceID) {
70+
return getEventingState(resourceID) == PROCESSED_MARK_FOR_DELETION;
71+
}
72+
6173
public void markDeleteEventReceived(Event event) {
6274
markDeleteEventReceived(event.getRelatedCustomResourceID());
6375
}

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

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ public void handleEvent(Event event) {
121121
}
122122

123123
private void handleMarkedEventForResource(ResourceID resourceID) {
124-
if (!eventMarker.deleteEventPresent(resourceID)) {
125-
submitReconciliationExecution(resourceID);
126-
} else {
124+
if (eventMarker.deleteEventPresent(resourceID)) {
127125
cleanupForDeletedEvent(resourceID);
126+
} else if (!eventMarker.processedMarkForDeletionPresent(resourceID)) {
127+
submitReconciliationExecution(resourceID);
128128
}
129129
}
130130

@@ -157,18 +157,50 @@ private void submitReconciliationExecution(ResourceID resourceID) {
157157
}
158158

159159
private void handleEventMarking(Event event) {
160-
if (event instanceof ResourceEvent
161-
&& ((ResourceEvent) event).getAction() == ResourceAction.DELETED) {
162-
log.debug("Marking delete event received for: {}", event.getRelatedCustomResourceID());
163-
eventMarker.markDeleteEventReceived(event);
164-
} else if (!eventMarker.deleteEventPresent(event.getRelatedCustomResourceID())) {
165-
log.debug("Marking event received for: {}", event.getRelatedCustomResourceID());
166-
eventMarker.markEventReceived(event);
160+
final var relatedCustomResourceID = event.getRelatedCustomResourceID();
161+
if (event instanceof ResourceEvent) {
162+
var resourceEvent = (ResourceEvent) event;
163+
if (resourceEvent.getAction() == ResourceAction.DELETED) {
164+
log.debug("Marking delete event received for: {}", relatedCustomResourceID);
165+
eventMarker.markDeleteEventReceived(event);
166+
} else {
167+
if (eventMarker.processedMarkForDeletionPresent(relatedCustomResourceID)
168+
&& isResourceMarkedForDeletion(resourceEvent)) {
169+
log.debug(
170+
"Skipping mark of event received, since already processed mark for deletion and resource marked for deletion: {}",
171+
relatedCustomResourceID);
172+
return;
173+
}
174+
// Normally when eventMarker is in state PROCESSED_MARK_FOR_DELETION it is expected to
175+
// receive a Delete event or an event where resource is marked for deletion. In a rare edge
176+
// case however it can happen that the finalizer related to the current controller is
177+
// removed, but also the informers websocket is disconnected and later reconnected. So
178+
// meanwhile the resource could be deleted and recreated. In this case we just mark a new
179+
// event as below.
180+
markEventReceived(event);
181+
}
182+
} else if (!eventMarker.deleteEventPresent(relatedCustomResourceID) ||
183+
!eventMarker.processedMarkForDeletionPresent(relatedCustomResourceID)) {
184+
markEventReceived(event);
185+
} else if (log.isDebugEnabled()) {
186+
log.debug(
187+
"Skipped marking event as received. Delete event present: {}, processed mark for deletion: {}",
188+
eventMarker.deleteEventPresent(relatedCustomResourceID),
189+
eventMarker.processedMarkForDeletionPresent(relatedCustomResourceID));
167190
}
168191
}
169192

170-
private RetryInfo retryInfo(ResourceID customResourceUid) {
171-
return retryState.get(customResourceUid);
193+
private void markEventReceived(Event event) {
194+
log.debug("Marking event received for: {}", event.getRelatedCustomResourceID());
195+
eventMarker.markEventReceived(event);
196+
}
197+
198+
private boolean isResourceMarkedForDeletion(ResourceEvent resourceEvent) {
199+
return resourceEvent.getResource().map(HasMetadata::isMarkedForDeletion).orElse(false);
200+
}
201+
202+
private RetryInfo retryInfo(ResourceID resourceID) {
203+
return retryState.get(resourceID);
172204
}
173205

174206
void eventProcessingFinished(
@@ -199,6 +231,8 @@ void eventProcessingFinished(
199231
metrics.finishedReconciliation(resourceID);
200232
if (eventMarker.deleteEventPresent(resourceID)) {
201233
cleanupForDeletedEvent(executionScope.getResourceID());
234+
} else if (postExecutionControl.isFinalizerRemoved()) {
235+
eventMarker.markProcessedMarkForDeletion(resourceID);
202236
} else {
203237
postExecutionControl
204238
.getUpdatedCustomResource()
@@ -247,13 +281,13 @@ TimerEventSource<R> retryEventSource() {
247281
private void handleRetryOnException(
248282
ExecutionScope<R> executionScope, Exception exception) {
249283
RetryExecution execution = getOrInitRetryExecution(executionScope);
250-
var customResourceID = executionScope.getResourceID();
251-
boolean eventPresent = eventMarker.eventPresent(customResourceID);
252-
eventMarker.markEventReceived(customResourceID);
284+
var resourceID = executionScope.getResourceID();
285+
boolean eventPresent = eventMarker.eventPresent(resourceID);
286+
eventMarker.markEventReceived(resourceID);
253287

254288
if (eventPresent) {
255-
log.debug("New events exists for for resource id: {}", customResourceID);
256-
submitReconciliationExecution(customResourceID);
289+
log.debug("New events exists for for resource id: {}", resourceID);
290+
submitReconciliationExecution(resourceID);
257291
return;
258292
}
259293
Optional<Long> nextDelay = execution.nextDelay();
@@ -263,8 +297,8 @@ private void handleRetryOnException(
263297
log.debug(
264298
"Scheduling timer event for retry with delay:{} for resource: {}",
265299
delay,
266-
customResourceID);
267-
metrics.failedReconciliation(customResourceID, exception);
300+
resourceID);
301+
metrics.failedReconciliation(resourceID, exception);
268302
retryEventSource().scheduleOnce(executionScope.getResource(), delay);
269303
},
270304
() -> log.error("Exhausted retries for {}", executionScope));
@@ -288,22 +322,22 @@ private RetryExecution getOrInitRetryExecution(ExecutionScope<R> executionScope)
288322
return retryExecution;
289323
}
290324

291-
private void cleanupForDeletedEvent(ResourceID customResourceUid) {
292-
log.debug("Cleaning up for delete event for: {}", customResourceUid);
293-
eventMarker.cleanup(customResourceUid);
294-
metrics.cleanupDoneFor(customResourceUid);
325+
private void cleanupForDeletedEvent(ResourceID resourceID) {
326+
log.debug("Cleaning up for delete event for: {}", resourceID);
327+
eventMarker.cleanup(resourceID);
328+
metrics.cleanupDoneFor(resourceID);
295329
}
296330

297-
private boolean isControllerUnderExecution(ResourceID customResourceUid) {
298-
return underProcessing.contains(customResourceUid);
331+
private boolean isControllerUnderExecution(ResourceID resourceID) {
332+
return underProcessing.contains(resourceID);
299333
}
300334

301-
private void setUnderExecutionProcessing(ResourceID customResourceUid) {
302-
underProcessing.add(customResourceUid);
335+
private void setUnderExecutionProcessing(ResourceID resourceID) {
336+
underProcessing.add(resourceID);
303337
}
304338

305-
private void unsetUnderExecution(ResourceID customResourceUid) {
306-
underProcessing.remove(customResourceUid);
339+
private void unsetUnderExecution(ResourceID resourceID) {
340+
underProcessing.remove(resourceID);
307341
}
308342

309343
private boolean isRetryConfigured() {

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@
66

77
final class PostExecutionControl<R extends HasMetadata> {
88

9-
private final boolean onlyFinalizerHandled;
9+
private final boolean finalizerRemoved;
1010
private final R updatedCustomResource;
1111
private final boolean updateIsStatusPatch;
1212
private final Exception runtimeException;
1313

1414
private Long reScheduleDelay = null;
1515

1616
private PostExecutionControl(
17-
boolean onlyFinalizerHandled,
17+
boolean finalizerRemoved,
1818
R updatedCustomResource,
1919
boolean updateIsStatusPatch, Exception runtimeException) {
20-
this.onlyFinalizerHandled = onlyFinalizerHandled;
20+
this.finalizerRemoved = finalizerRemoved;
2121
this.updatedCustomResource = updatedCustomResource;
2222
this.updateIsStatusPatch = updateIsStatusPatch;
2323
this.runtimeException = runtimeException;
2424
}
2525

2626
public static <R extends HasMetadata> PostExecutionControl<R> onlyFinalizerAdded(
2727
R updatedCustomResource) {
28-
return new PostExecutionControl<>(true, updatedCustomResource, false, null);
28+
return new PostExecutionControl<>(false, updatedCustomResource, false, null);
2929
}
3030

3131
public static <R extends HasMetadata> PostExecutionControl<R> defaultDispatch() {
@@ -42,6 +42,11 @@ public static <R extends HasMetadata> PostExecutionControl<R> customResourceUpda
4242
return new PostExecutionControl<>(false, updatedCustomResource, false, null);
4343
}
4444

45+
public static <R extends HasMetadata> PostExecutionControl<R> customResourceFinalizerRemoved(
46+
R updatedCustomResource) {
47+
return new PostExecutionControl<>(true, updatedCustomResource, false, null);
48+
}
49+
4550
public static <R extends HasMetadata> PostExecutionControl<R> exceptionDuringExecution(
4651
Exception exception) {
4752
return new PostExecutionControl<>(false, null, false, exception);
@@ -76,11 +81,15 @@ public boolean updateIsStatusPatch() {
7681
public String toString() {
7782
return "PostExecutionControl{"
7883
+ "onlyFinalizerHandled="
79-
+ onlyFinalizerHandled
84+
+ finalizerRemoved
8085
+ ", updatedCustomResource="
8186
+ updatedCustomResource
8287
+ ", runtimeException="
8388
+ runtimeException
8489
+ '}';
8590
}
91+
92+
public boolean isFinalizerRemoved() {
93+
return finalizerRemoved;
94+
}
8695
}

0 commit comments

Comments
 (0)