@@ -32,7 +32,7 @@ class EventSchedulerTest {
32
32
@ SuppressWarnings ("unchecked" )
33
33
private EventDispatcher eventDispatcher = mock (EventDispatcher .class );
34
34
35
- private EventScheduler eventScheduler = new EventScheduler ( eventDispatcher , new GenericRetry (). setMaxAttempts ( MAX_RETRY_ATTEMPTS ). withLinearRetry () );
35
+ private EventScheduler eventScheduler = initScheduler ( true );
36
36
37
37
private List <EventProcessingDetail > eventProcessingList = Collections .synchronizedList (new ArrayList <>());
38
38
@@ -55,6 +55,7 @@ public void eventsAreNotExecutedConcurrentlyForSameResource() throws Interrupted
55
55
CustomResource resource1 = sampleResource ();
56
56
CustomResource resource2 = sampleResource ();
57
57
resource2 .getMetadata ().setResourceVersion ("2" );
58
+ resource2 .getMetadata ().setGeneration (2l );
58
59
59
60
eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource1 );
60
61
eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource2 );
@@ -70,14 +71,55 @@ public void eventsAreNotExecutedConcurrentlyForSameResource() throws Interrupted
70
71
"Start time of event 2 is after end time of event 1" );
71
72
}
72
73
74
+ @ Test
75
+ public void generationAwareSchedulingSkipsEventsWithoutIncreasedGeneration () {
76
+ normalDispatcherExecution ();
77
+ CustomResource resource1 = sampleResource ();
78
+ CustomResource resource2 = sampleResource ();
79
+ resource2 .getMetadata ().setResourceVersion ("2" );
80
+
81
+ eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource1 );
82
+ eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource2 );
83
+
84
+ waitTimeForExecution (2 );
85
+ assertThat (eventProcessingList ).hasSize (1 )
86
+ .matches (list ->
87
+ eventProcessingList .get (0 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("1" ));
88
+
89
+ }
90
+
91
+ @ Test
92
+ public void notGenerationAwareSchedulingProcessesAllEventsRegardlessOfGeneration () {
93
+ generationUnAwareScheduler ();
94
+ normalDispatcherExecution ();
95
+ CustomResource resource1 = sampleResource ();
96
+ CustomResource resource2 = sampleResource ();
97
+ resource2 .getMetadata ().setResourceVersion ("2" );
98
+
99
+ eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource1 );
100
+ eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource2 );
101
+
102
+ waitTimeForExecution (2 );
103
+ log .info ("Event processing details 1.: {}. 2: {}" , eventProcessingList .get (0 ), eventProcessingList .get (1 ));
104
+ assertThat (eventProcessingList ).hasSize (2 )
105
+ .matches (list -> eventProcessingList .get (0 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("1" ) &&
106
+ eventProcessingList .get (1 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("2" ),
107
+ "Events processed in correct order" )
108
+ .matches (list -> eventExecutedBefore (0 , 1 ),
109
+ "Start time of event 2 is after end time of event 1" );
110
+ }
111
+
112
+ // note that this is true for generation aware scheduling
73
113
@ Test
74
114
public void onlyLastEventIsScheduledIfMoreReceivedDuringAndExecution () {
75
115
normalDispatcherExecution ();
76
116
CustomResource resource1 = sampleResource ();
77
117
CustomResource resource2 = sampleResource ();
78
118
resource2 .getMetadata ().setResourceVersion ("2" );
119
+ resource2 .getMetadata ().setGeneration (2l );
79
120
CustomResource resource3 = sampleResource ();
80
121
resource3 .getMetadata ().setResourceVersion ("3" );
122
+ resource3 .getMetadata ().setGeneration (3l );
81
123
82
124
eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource1 );
83
125
eventScheduler .eventReceived (Watcher .Action .MODIFIED , resource2 );
@@ -89,8 +131,7 @@ public void onlyLastEventIsScheduledIfMoreReceivedDuringAndExecution() {
89
131
.matches (list -> eventProcessingList .get (0 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("1" ) &&
90
132
eventProcessingList .get (1 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("3" ),
91
133
"Events processed in correct order" )
92
- .matches (list ->
93
- eventProcessingList .get (0 ).getEndTime ().isBefore (eventProcessingList .get (1 ).startTime ),
134
+ .matches (list -> eventExecutedBefore (0 , 1 ),
94
135
"Start time of event 2 is after end time of event 1" );
95
136
}
96
137
@@ -118,6 +159,7 @@ public void processesNewEventIfItIsReceivedAfterExecutionInError() {
118
159
CustomResource resource1 = sampleResource ();
119
160
CustomResource resource2 = sampleResource ();
120
161
resource2 .getMetadata ().setResourceVersion ("2" );
162
+ resource2 .getMetadata ().setGeneration (2l );
121
163
122
164
doAnswer (this ::exceptionInExecution ).when (eventDispatcher ).handleEvent (any (Watcher .Action .class ), eq (resource1 ));
123
165
doAnswer (this ::normalExecution ).when (eventDispatcher ).handleEvent (any (Watcher .Action .class ), eq (resource2 ));
@@ -131,16 +173,15 @@ public void processesNewEventIfItIsReceivedAfterExecutionInError() {
131
173
.matches (list -> eventProcessingList .get (0 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("1" ) &&
132
174
eventProcessingList .get (1 ).getCustomResource ().getMetadata ().getResourceVersion ().equals ("2" ),
133
175
"Events processed in correct order" )
134
- .matches (list ->
135
- eventProcessingList .get (0 ).getEndTime ().isBefore (eventProcessingList .get (1 ).startTime ),
176
+ .matches (list -> eventExecutedBefore (0 , 1 ),
136
177
"Start time of event 2 is after end time of event 1" );
137
178
138
179
assertThat (eventProcessingList .get (0 ).getException ()).isNotNull ();
139
180
assertThat (eventProcessingList .get (1 ).getException ()).isNull ();
140
181
}
141
182
142
183
@ Test
143
- public void numberOfRetriesIsLimited () {
184
+ public void numberOfRetriesCanBeLimited () {
144
185
doAnswer (this ::exceptionInExecution ).when (eventDispatcher ).handleEvent (any (Watcher .Action .class ), any (CustomResource .class ));
145
186
146
187
eventScheduler .eventReceived (Watcher .Action .MODIFIED , sampleResource ());
@@ -166,6 +207,14 @@ private Object normalExecution(InvocationOnMock invocation) {
166
207
}
167
208
}
168
209
210
+ private void generationUnAwareScheduler () {
211
+ eventScheduler = initScheduler (false );
212
+ }
213
+
214
+ private EventScheduler initScheduler (boolean generationAware ) {
215
+ return new EventScheduler (eventDispatcher ,
216
+ new GenericRetry ().setMaxAttempts (MAX_RETRY_ATTEMPTS ).withLinearRetry (), generationAware );
217
+ }
169
218
170
219
private Object exceptionInExecution (InvocationOnMock invocation ) {
171
220
try {
@@ -181,6 +230,11 @@ private Object exceptionInExecution(InvocationOnMock invocation) {
181
230
}
182
231
}
183
232
233
+ private boolean eventExecutedBefore (int event1Index , int event2Index ) {
234
+ return eventProcessingList .get (event1Index ).getEndTime ().isBefore (eventProcessingList .get (event2Index ).startTime ) ||
235
+ eventProcessingList .get (event1Index ).getEndTime ().equals (eventProcessingList .get (event2Index ).startTime );
236
+ }
237
+
184
238
private void waitMinimalTimeForExecution () {
185
239
waitTimeForExecution (1 );
186
240
}
@@ -203,7 +257,7 @@ CustomResource sampleResource() {
203
257
resource .setMetadata (new ObjectMetaBuilder ()
204
258
.withCreationTimestamp ("creationTimestamp" )
205
259
.withDeletionGracePeriodSeconds (10L )
206
- .withGeneration (10L )
260
+ .withGeneration (1L )
207
261
.withName ("name" )
208
262
.withNamespace ("namespace" )
209
263
.withResourceVersion ("1" )
0 commit comments