Skip to content

Commit a5d3f33

Browse files
committed
Add interleaved update replay reproducer
Add a Java replay test that mirrors the Kotlin GreetingWorkflow sample and replays the provided workflow history fixture. Assert that replay fails with the embedded TMPRL1100 NonDeterministicException message so the reproducer stays pinned to the reported failure mode.
1 parent 5d969b8 commit a5d3f33

File tree

2 files changed

+1189
-0
lines changed

2 files changed

+1189
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package io.temporal.workflow.versionTests;
2+
3+
import static org.junit.Assert.assertThrows;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import io.temporal.activity.ActivityInterface;
7+
import io.temporal.activity.ActivityMethod;
8+
import io.temporal.activity.ActivityOptions;
9+
import io.temporal.common.RetryOptions;
10+
import io.temporal.testing.WorkflowReplayer;
11+
import io.temporal.workflow.UpdateMethod;
12+
import io.temporal.workflow.Workflow;
13+
import io.temporal.workflow.WorkflowInterface;
14+
import io.temporal.workflow.WorkflowMethod;
15+
import java.time.Duration;
16+
import java.time.OffsetDateTime;
17+
import java.util.UUID;
18+
import org.junit.Test;
19+
import org.slf4j.Logger;
20+
21+
/**
22+
* Mirrors app/src/main/kotlin/io/temporal/samples/update_nde/GreetingWorkflow.kt from
23+
* gauravthadani/samples-kotlin and replays a history where update completions are interleaved with
24+
* version markers.
25+
*/
26+
public class GetVersionInterleavedUpdateReplayTest {
27+
private static final String HISTORY_RESOURCE =
28+
"testGetVersionInterleavedUpdateReplayHistory.json";
29+
private static final String EXPECTED_NON_DETERMINISTIC_MESSAGE =
30+
"[TMPRL1100] getVersion call before the existing version marker event. The most probable cause is retroactive addition of a getVersion call with an existing 'changeId'";
31+
private static final String EXPECTED_NON_DETERMINISTIC_FRAGMENT =
32+
"io.temporal.worker.NonDeterministicException: " + EXPECTED_NON_DETERMINISTIC_MESSAGE;
33+
34+
@Test
35+
public void testReplayHistory() {
36+
RuntimeException thrown =
37+
assertThrows(
38+
RuntimeException.class,
39+
() ->
40+
WorkflowReplayer.replayWorkflowExecutionFromResource(
41+
HISTORY_RESOURCE, GreetingWorkflowImpl.class));
42+
assertTrue(thrown.getMessage().contains(EXPECTED_NON_DETERMINISTIC_FRAGMENT));
43+
}
44+
45+
public static class Request {
46+
private final String name;
47+
private final OffsetDateTime date;
48+
49+
public Request(String name, OffsetDateTime date) {
50+
this.name = name;
51+
this.date = date;
52+
}
53+
54+
public String getName() {
55+
return name;
56+
}
57+
58+
public OffsetDateTime getDate() {
59+
return date;
60+
}
61+
}
62+
63+
@WorkflowInterface
64+
public interface GreetingWorkflow {
65+
@WorkflowMethod
66+
String greeting(String name);
67+
68+
@UpdateMethod
69+
String notify(String name);
70+
}
71+
72+
public static class GreetingWorkflowImpl implements GreetingWorkflow {
73+
private final Logger logger = Workflow.getLogger(GreetingWorkflow.class);
74+
75+
public GreetingWorkflowImpl() {
76+
logger.info("Workflow is initialized");
77+
}
78+
79+
private GreetingActivities getActivities() {
80+
return Workflow.newActivityStub(
81+
GreetingActivities.class,
82+
ActivityOptions.newBuilder()
83+
.setStartToCloseTimeout(Duration.ofSeconds(30))
84+
.setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())
85+
.build());
86+
}
87+
88+
@Override
89+
public String greeting(String name) {
90+
logger.info("Workflow started");
91+
92+
Workflow.getVersion("ChangeId1", 0, 1);
93+
Workflow.getVersion("ChangeId2", 0, 1);
94+
95+
Workflow.await(() -> false);
96+
return getActivities().composeGreeting("hello", name);
97+
}
98+
99+
@Override
100+
public String notify(String name) {
101+
logger.info("Signal received: {}", name);
102+
Workflow.sideEffect(UUID.class, UUID::randomUUID);
103+
return "works";
104+
}
105+
}
106+
107+
public static class GreetingActivitiesImpl implements GreetingActivities {
108+
@Override
109+
public String composeGreeting(String greeting, String name) {
110+
System.out.println("Greeting started: " + greeting);
111+
return greeting + ", " + name + "!";
112+
}
113+
}
114+
115+
@ActivityInterface
116+
public interface GreetingActivities {
117+
@ActivityMethod(name = "greet")
118+
String composeGreeting(String greeting, String name);
119+
}
120+
}

0 commit comments

Comments
 (0)