Skip to content

Commit 568fd6b

Browse files
committed
createTimerChain was comparing the same timestamp for future validation
Signed-off-by: salaboy <[email protected]>
1 parent f72440d commit 568fd6b

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

client/src/main/java/io/dapr/durabletask/TaskOrchestrationExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ public TimerTask(Instant finalFireAt) {
10381038
// if necessary. Otherwise, we return and no more sub-timers are created.
10391039
private CompletableFuture<Void> createTimerChain(Instant finalFireAt, CompletableFuture<Void> currentFuture) {
10401040
return currentFuture.thenRun(() -> {
1041-
if (currentInstant.compareTo(finalFireAt) > 0) {
1041+
if (currentInstant.compareTo(finalFireAt) >= 0) {
10421042
return;
10431043
}
10441044
Task<Void> nextTimer = createTimerTask(finalFireAt);

client/src/test/java/io/dapr/durabletask/IntegrationTests.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,14 @@ void emptyOrchestration() throws TimeoutException {
8787
void singleTimer() throws IOException, TimeoutException {
8888
final String orchestratorName = "SingleTimer";
8989
final Duration delay = Duration.ofSeconds(3);
90+
AtomicReferenceArray<LocalDateTime> timestamps = new AtomicReferenceArray<>(2);
91+
AtomicInteger counter = new AtomicInteger();
9092
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+
})
9298
.buildAndStart();
9399

94100
DurableTaskClient client = new DurableTaskGrpcClientBuilder().build();
@@ -103,11 +109,22 @@ void singleTimer() throws IOException, TimeoutException {
103109
long expectedCompletionSecond = instance.getCreatedAt().plus(delay).getEpochSecond();
104110
long actualCompletionSecond = instance.getLastUpdatedAt().getEpochSecond();
105111
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(secondsElapsed[0], 3);
123+
106124
}
107125
}
108126

109127
@Test
110-
@Disabled("Test is disabled for investigation, fixing the test retry pattern exposed the failure (could be timer creation issue)")
111128
void longTimer() throws TimeoutException {
112129
final String orchestratorName = "LongTimer";
113130
final Duration delay = Duration.ofSeconds(7);
@@ -285,6 +302,31 @@ void singleTimeStampTimer() throws IOException, TimeoutException {
285302
}
286303
}
287304

305+
306+
@Test
307+
void singleTimeStampCreateTimer() throws IOException, TimeoutException {
308+
final String orchestratorName = "SingleTimeStampTimer";
309+
final Duration delay = Duration.ofSeconds(3);
310+
final ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now().plusSeconds(delay.getSeconds()), ZoneId.systemDefault());
311+
DurableTaskGrpcWorker worker = this.createWorkerBuilder()
312+
.addOrchestrator(orchestratorName, ctx -> ctx.createTimer(zonedDateTime).await())
313+
.buildAndStart();
314+
315+
DurableTaskClient client = new DurableTaskGrpcClientBuilder().build();
316+
try (worker; client) {
317+
String instanceId = client.scheduleNewOrchestrationInstance(orchestratorName);
318+
Duration timeout = delay.plus(defaultTimeout);
319+
OrchestrationMetadata instance = client.waitForInstanceCompletion(instanceId, timeout, false);
320+
assertNotNull(instance);
321+
assertEquals(OrchestrationRuntimeStatus.COMPLETED, instance.getRuntimeStatus());
322+
323+
// Verify that the delay actually happened
324+
long expectedCompletionSecond = zonedDateTime.toInstant().getEpochSecond();
325+
long actualCompletionSecond = instance.getLastUpdatedAt().getEpochSecond();
326+
assertTrue(expectedCompletionSecond <= actualCompletionSecond);
327+
}
328+
}
329+
288330
@Test
289331
void isReplaying() throws IOException, InterruptedException, TimeoutException {
290332
final String orchestratorName = "SingleTimer";

0 commit comments

Comments
 (0)