1
1
package io .javaoperatorsdk .operator .processing .event ;
2
2
3
+ import java .time .Duration ;
3
4
import java .util .HashMap ;
4
5
import java .util .HashSet ;
5
6
import java .util .Map ;
20
21
import io .javaoperatorsdk .operator .api .reconciler .RetryInfo ;
21
22
import io .javaoperatorsdk .operator .processing .LifecycleAware ;
22
23
import io .javaoperatorsdk .operator .processing .MDCUtils ;
24
+ import io .javaoperatorsdk .operator .processing .event .rate .RateLimiter ;
23
25
import io .javaoperatorsdk .operator .processing .event .source .Cache ;
24
26
import io .javaoperatorsdk .operator .processing .event .source .controller .ResourceAction ;
25
27
import io .javaoperatorsdk .operator .processing .event .source .controller .ResourceEvent ;
@@ -44,6 +46,7 @@ class EventProcessor<R extends HasMetadata> implements EventHandler, LifecycleAw
44
46
private final Cache <R > cache ;
45
47
private final EventSourceManager <R > eventSourceManager ;
46
48
private final EventMarker eventMarker = new EventMarker ();
49
+ private final RateLimiter rateLimiter ;
47
50
48
51
EventProcessor (EventSourceManager <R > eventSourceManager ) {
49
52
this (
@@ -92,6 +95,8 @@ private EventProcessor(
92
95
this .cache = cache ;
93
96
this .metrics = metrics != null ? metrics : Metrics .NOOP ;
94
97
this .eventSourceManager = eventSourceManager ;
98
+ // todo configure
99
+ this .rateLimiter = new RateLimiter (Duration .ofSeconds (1 ), 5 );
95
100
}
96
101
97
102
@ Override
@@ -128,6 +133,11 @@ private void submitReconciliationExecution(ResourceID resourceID) {
128
133
Optional <R > latest = cache .get (resourceID );
129
134
latest .ifPresent (MDCUtils ::addResourceInfo );
130
135
if (!controllerUnderExecution && latest .isPresent ()) {
136
+ var rateLimiterPermission = rateLimiter .acquirePermission (resourceID );
137
+ if (rateLimiterPermission .isPresent ()) {
138
+ handleRateLimitedSubmission (resourceID , rateLimiterPermission .get ());
139
+ return ;
140
+ }
131
141
setUnderExecutionProcessing (resourceID );
132
142
final var retryInfo = retryInfo (resourceID );
133
143
ExecutionScope <R > executionScope = new ExecutionScope <>(latest .get (), retryInfo );
@@ -193,6 +203,13 @@ private boolean isResourceMarkedForDeletion(ResourceEvent resourceEvent) {
193
203
return resourceEvent .getResource ().map (HasMetadata ::isMarkedForDeletion ).orElse (false );
194
204
}
195
205
206
+ private void handleRateLimitedSubmission (ResourceID resourceID , Duration minimalDuration ) {
207
+ var minimalDurationMillis = minimalDuration .toMillis ();
208
+ log .debug ("Rate limited resource: {}, rescheduled in {} millis" , resourceID ,
209
+ minimalDurationMillis );
210
+ retryEventSource ().scheduleOnce (resourceID , minimalDurationMillis );
211
+ }
212
+
196
213
private RetryInfo retryInfo (ResourceID resourceID ) {
197
214
return retryState .get (resourceID );
198
215
}
@@ -251,11 +268,10 @@ private void reScheduleExecutionIfInstructed(
251
268
postExecutionControl
252
269
.getReScheduleDelay ()
253
270
.ifPresent (delay -> {
254
- if (log .isDebugEnabled ()) {
255
- log .debug ("ReScheduling event for resource: {} with delay: {}" ,
256
- ResourceID .fromResource (customResource ), delay );
257
- }
258
- retryEventSource ().scheduleOnce (customResource , delay );
271
+ var resourceID = ResourceID .fromResource (customResource );
272
+ log .debug ("ReScheduling event for resource: {} with delay: {}" ,
273
+ resourceID , delay );
274
+ retryEventSource ().scheduleOnce (resourceID , delay );
259
275
});
260
276
}
261
277
@@ -289,7 +305,7 @@ private void handleRetryOnException(
289
305
delay ,
290
306
resourceID );
291
307
metrics .failedReconciliation (resourceID , exception );
292
- retryEventSource ().scheduleOnce (executionScope . getResource () , delay );
308
+ retryEventSource ().scheduleOnce (resourceID , delay );
293
309
},
294
310
() -> log .error ("Exhausted retries for {}" , executionScope ));
295
311
}
0 commit comments