@@ -87,8 +87,14 @@ void emptyOrchestration() throws TimeoutException {
87
87
void singleTimer () throws IOException , TimeoutException {
88
88
final String orchestratorName = "SingleTimer" ;
89
89
final Duration delay = Duration .ofSeconds (3 );
90
+ AtomicReferenceArray <LocalDateTime > timestamps = new AtomicReferenceArray <>(2 );
91
+ AtomicInteger counter = new AtomicInteger ();
90
92
DurableTaskGrpcWorker worker = this .createWorkerBuilder ()
91
- .addOrchestrator (orchestratorName , ctx -> ctx .createTimer (delay ).await ())
93
+ .addOrchestrator (orchestratorName , ctx -> {
94
+ timestamps .set (counter .get (), LocalDateTime .now ());
95
+ counter .incrementAndGet ();
96
+ ctx .createTimer (delay ).await ();
97
+ })
92
98
.buildAndStart ();
93
99
94
100
DurableTaskClient client = new DurableTaskGrpcClientBuilder ().build ();
@@ -103,11 +109,130 @@ void singleTimer() throws IOException, TimeoutException {
103
109
long expectedCompletionSecond = instance .getCreatedAt ().plus (delay ).getEpochSecond ();
104
110
long actualCompletionSecond = instance .getLastUpdatedAt ().getEpochSecond ();
105
111
assertTrue (expectedCompletionSecond <= actualCompletionSecond );
112
+
113
+ // Verify that the correct number of timers were created
114
+ // This should yield 2 (first invocation + replay invocations for internal timers)
115
+ assertEquals (2 , counter .get ());
116
+
117
+ // Verify that each timer is the expected length
118
+ int [] secondsElapsed = new int [1 ];
119
+ for (int i = 0 ; i < timestamps .length () - 1 ; i ++) {
120
+ secondsElapsed [i ] = timestamps .get (i + 1 ).getSecond () - timestamps .get (i ).getSecond ();
121
+ }
122
+ assertEquals (3 , secondsElapsed [0 ]);
123
+
124
+ }
125
+ }
126
+
127
+
128
+ @ Test
129
+ void loopWithTimer () throws IOException , TimeoutException {
130
+ final String orchestratorName = "LoopWithTimer" ;
131
+ final Duration delay = Duration .ofSeconds (2 );
132
+ AtomicReferenceArray <LocalDateTime > timestamps = new AtomicReferenceArray <>(4 );
133
+ AtomicInteger counter = new AtomicInteger ();
134
+ DurableTaskGrpcWorker worker = this .createWorkerBuilder ()
135
+ .addOrchestrator (orchestratorName , ctx -> {
136
+ for (int i = 0 ; i < 3 ; i ++) {
137
+ if (!ctx .getIsReplaying ()) {
138
+ timestamps .set (counter .get (), LocalDateTime .now ());
139
+ counter .incrementAndGet ();
140
+ }
141
+ ctx .createTimer (delay ).await ();
142
+ }
143
+ })
144
+ .buildAndStart ();
145
+
146
+ DurableTaskClient client = new DurableTaskGrpcClientBuilder ().build ();
147
+ try (worker ; client ) {
148
+ String instanceId = client .scheduleNewOrchestrationInstance (orchestratorName );
149
+ Duration timeout = delay .plus (defaultTimeout );
150
+ OrchestrationMetadata instance = client .waitForInstanceCompletion (instanceId , timeout , false );
151
+ assertNotNull (instance );
152
+ assertEquals (OrchestrationRuntimeStatus .COMPLETED , instance .getRuntimeStatus ());
153
+
154
+ // Verify that the delay actually happened
155
+ long expectedCompletionSecond = instance .getCreatedAt ().plus (delay ).getEpochSecond ();
156
+ long actualCompletionSecond = instance .getLastUpdatedAt ().getEpochSecond ();
157
+ assertTrue (expectedCompletionSecond <= actualCompletionSecond );
158
+
159
+ // Verify that the correct number of timers were created
160
+ assertEquals (3 , counter .get ());
161
+
162
+ // Verify that each timer is the expected length
163
+ int [] secondsElapsed = new int [timestamps .length ()];
164
+ for (int i = 0 ; i < timestamps .length () - 1 ; i ++) {
165
+ if (timestamps .get (i + 1 ) != null && timestamps .get (i ) != null ) {
166
+ secondsElapsed [i ] = timestamps .get (i + 1 ).getSecond () - timestamps .get (i ).getSecond ();
167
+ }else {
168
+ secondsElapsed [i ] = -1 ;
169
+ }
170
+ }
171
+ assertEquals (2 , secondsElapsed [0 ]);
172
+ assertEquals (2 , secondsElapsed [1 ]);
173
+ assertEquals (-1 , secondsElapsed [2 ]);
174
+
175
+
176
+ }
177
+ }
178
+
179
+ @ Test
180
+ void loopWithWaitForEvent () throws IOException , TimeoutException {
181
+ final String orchestratorName = "LoopWithTimer" ;
182
+ final Duration delay = Duration .ofSeconds (2 );
183
+ AtomicReferenceArray <LocalDateTime > timestamps = new AtomicReferenceArray <>(4 );
184
+ AtomicInteger counter = new AtomicInteger ();
185
+ DurableTaskGrpcWorker worker = this .createWorkerBuilder ()
186
+ .addOrchestrator (orchestratorName , ctx -> {
187
+ for (int i = 0 ; i < 4 ; i ++) {
188
+ try {
189
+ ctx .waitForExternalEvent ("HELLO" , delay ).await ();
190
+ }catch (TaskCanceledException tce ){
191
+ if (!ctx .getIsReplaying ()){
192
+ timestamps .set (counter .get (), LocalDateTime .now ());
193
+ counter .incrementAndGet ();
194
+ }
195
+
196
+ }
197
+ }
198
+ })
199
+ .buildAndStart ();
200
+
201
+ DurableTaskClient client = new DurableTaskGrpcClientBuilder ().build ();
202
+ try (worker ; client ) {
203
+ String instanceId = client .scheduleNewOrchestrationInstance (orchestratorName );
204
+ Duration timeout = delay .plus (defaultTimeout );
205
+ OrchestrationMetadata instance = client .waitForInstanceCompletion (instanceId , timeout , false );
206
+ assertNotNull (instance );
207
+ assertEquals (OrchestrationRuntimeStatus .COMPLETED , instance .getRuntimeStatus ());
208
+
209
+ // Verify that the delay actually happened
210
+ long expectedCompletionSecond = instance .getCreatedAt ().plus (delay ).getEpochSecond ();
211
+ long actualCompletionSecond = instance .getLastUpdatedAt ().getEpochSecond ();
212
+ assertTrue (expectedCompletionSecond <= actualCompletionSecond );
213
+
214
+ // Verify that the correct number of timers were created
215
+ assertEquals (4 , counter .get ());
216
+
217
+ // Verify that each timer is the expected length
218
+ int [] secondsElapsed = new int [timestamps .length ()];
219
+ for (int i = 0 ; i < timestamps .length () - 1 ; i ++) {
220
+ if (timestamps .get (i + 1 ) != null && timestamps .get (i ) != null ) {
221
+ secondsElapsed [i ] = timestamps .get (i + 1 ).getSecond () - timestamps .get (i ).getSecond ();
222
+ }else {
223
+ secondsElapsed [i ] = -1 ;
224
+ }
225
+ }
226
+ assertEquals (2 , secondsElapsed [0 ]);
227
+ assertEquals (2 , secondsElapsed [1 ]);
228
+ assertEquals (2 , secondsElapsed [2 ]);
229
+ assertEquals (0 , secondsElapsed [3 ]);
230
+
231
+
106
232
}
107
233
}
108
234
109
235
@ Test
110
- @ Disabled ("Test is disabled for investigation, fixing the test retry pattern exposed the failure (could be timer creation issue)" )
111
236
void longTimer () throws TimeoutException {
112
237
final String orchestratorName = "LongTimer" ;
113
238
final Duration delay = Duration .ofSeconds (7 );
@@ -128,7 +253,7 @@ void longTimer() throws TimeoutException {
128
253
Duration timeout = delay .plus (defaultTimeout );
129
254
OrchestrationMetadata instance = client .waitForInstanceCompletion (instanceId , timeout , false );
130
255
assertNotNull (instance );
131
- assertEquals (OrchestrationRuntimeStatus .COMPLETED , instance .getRuntimeStatus (),
256
+ assertEquals (OrchestrationRuntimeStatus .COMPLETED , instance .getRuntimeStatus (),
132
257
String .format ("Orchestration failed with error: %s" , instance .getFailureDetails ().getErrorMessage ()));
133
258
134
259
// Verify that the delay actually happened
@@ -285,6 +410,31 @@ void singleTimeStampTimer() throws IOException, TimeoutException {
285
410
}
286
411
}
287
412
413
+
414
+ @ Test
415
+ void singleTimeStampCreateTimer () throws IOException , TimeoutException {
416
+ final String orchestratorName = "SingleTimeStampTimer" ;
417
+ final Duration delay = Duration .ofSeconds (3 );
418
+ final ZonedDateTime zonedDateTime = ZonedDateTime .of (LocalDateTime .now ().plusSeconds (delay .getSeconds ()), ZoneId .systemDefault ());
419
+ DurableTaskGrpcWorker worker = this .createWorkerBuilder ()
420
+ .addOrchestrator (orchestratorName , ctx -> ctx .createTimer (zonedDateTime ).await ())
421
+ .buildAndStart ();
422
+
423
+ DurableTaskClient client = new DurableTaskGrpcClientBuilder ().build ();
424
+ try (worker ; client ) {
425
+ String instanceId = client .scheduleNewOrchestrationInstance (orchestratorName );
426
+ Duration timeout = delay .plus (defaultTimeout );
427
+ OrchestrationMetadata instance = client .waitForInstanceCompletion (instanceId , timeout , false );
428
+ assertNotNull (instance );
429
+ assertEquals (OrchestrationRuntimeStatus .COMPLETED , instance .getRuntimeStatus ());
430
+
431
+ // Verify that the delay actually happened
432
+ long expectedCompletionSecond = zonedDateTime .toInstant ().getEpochSecond ();
433
+ long actualCompletionSecond = instance .getLastUpdatedAt ().getEpochSecond ();
434
+ assertTrue (expectedCompletionSecond <= actualCompletionSecond );
435
+ }
436
+ }
437
+
288
438
@ Test
289
439
void isReplaying () throws IOException , InterruptedException , TimeoutException {
290
440
final String orchestratorName = "SingleTimer" ;
@@ -884,13 +1034,13 @@ void multiInstanceQuery() throws TimeoutException{
884
1034
// Test CreatedTimeTo filter
885
1035
query .setCreatedTimeTo (startTime .minus (Duration .ofSeconds (1 )));
886
1036
result = client .queryInstances (query );
887
- assertTrue (result .getOrchestrationState ().isEmpty (),
888
- "Result should be empty but found " + result .getOrchestrationState ().size () + " instances: " +
1037
+ assertTrue (result .getOrchestrationState ().isEmpty (),
1038
+ "Result should be empty but found " + result .getOrchestrationState ().size () + " instances: " +
889
1039
"Start time: " + startTime + ", " +
890
1040
result .getOrchestrationState ().stream ()
891
- .map (state -> String .format ("\n ID: %s, Status: %s, Created: %s" ,
892
- state .getInstanceId (),
893
- state .getRuntimeStatus (),
1041
+ .map (state -> String .format ("\n ID: %s, Status: %s, Created: %s" ,
1042
+ state .getInstanceId (),
1043
+ state .getRuntimeStatus (),
894
1044
state .getCreatedAt ()))
895
1045
.collect (Collectors .joining (", " )));
896
1046
@@ -1203,7 +1353,7 @@ void waitForInstanceStartThrowsException() {
1203
1353
client .scheduleNewOrchestrationInstance (orchestratorName , null , instanceId );
1204
1354
});
1205
1355
thread .start ();
1206
-
1356
+
1207
1357
assertThrows (TimeoutException .class , () -> client .waitForInstanceStart (instanceId , Duration .ofSeconds (2 )) );
1208
1358
}
1209
1359
}
@@ -1591,8 +1741,8 @@ public void taskExecutionIdTest() {
1591
1741
1592
1742
DurableTaskGrpcWorker worker = this .createWorkerBuilder ()
1593
1743
.addOrchestrator (orchestratorName , ctx -> {
1594
- ctx .callActivity (retryActivityName ,null ,taskOptions ).await ();
1595
- ctx .callActivity (retryActivityName ,null ,taskOptions ).await ();
1744
+ ctx .callActivity (retryActivityName ,null ,taskOptions ).await ();
1745
+ ctx .callActivity (retryActivityName ,null ,taskOptions ).await ();
1596
1746
ctx .complete (true );
1597
1747
})
1598
1748
.addActivity (retryActivityName , ctx -> {
0 commit comments