Skip to content

Commit 84d5a4d

Browse files
committed
Expose lastFailure and raw proto on ActivityExecutionDescription.
1 parent 749a8c7 commit 84d5a4d

3 files changed

Lines changed: 119 additions & 1 deletion

File tree

temporal-sdk/src/main/java/io/temporal/client/ActivityExecutionDescription.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ private ActivityExecutionInfo info() {
6666
return info;
6767
}
6868

69+
/** The raw protobuf info returned by the server for this activity execution. */
70+
@Nonnull
71+
public ActivityExecutionInfo getRawInfo() {
72+
return info;
73+
}
74+
6975
/** Current attempt number (starts at 1). */
7076
public int getAttempt() {
7177
return info().getAttempt();
@@ -129,6 +135,12 @@ public Instant getLastStartedTime() {
129135
: null;
130136
}
131137

138+
/** Failure details from the last failed attempt. {@code null} if no failure has occurred. */
139+
@Nullable
140+
public io.temporal.api.failure.v1.Failure getLastFailure() {
141+
return info().hasLastFailure() ? info().getLastFailure() : null;
142+
}
143+
132144
/** Identity of the worker that last processed this activity. */
133145
@Nullable
134146
public String getLastWorkerIdentity() {

temporal-sdk/src/test/java/io/temporal/client/ActivityExecutionDescriptionTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.temporal.api.common.v1.ActivityType;
88
import io.temporal.api.common.v1.Payloads;
99
import io.temporal.api.enums.v1.ActivityExecutionStatus;
10+
import io.temporal.api.failure.v1.Failure;
1011
import io.temporal.common.Priority;
1112
import io.temporal.common.WorkerDeploymentVersion;
1213
import io.temporal.common.converter.DataConverter;
@@ -64,6 +65,7 @@ public void testNullableFieldsAbsentByDefault() {
6465
assertFalse(desc.hasHeartbeatDetails());
6566
assertNull(desc.getWorkerDeploymentVersion());
6667
assertNull(desc.getPriority());
68+
assertNull(desc.getLastFailure());
6769
}
6870

6971
@Test
@@ -130,6 +132,33 @@ public void testGetWorkerDeploymentVersionPresent() {
130132
assertEquals("build-42", version.getBuildId());
131133
}
132134

135+
@Test
136+
public void testGetLastFailureAbsent() {
137+
ActivityExecutionDescription desc =
138+
new ActivityExecutionDescription(buildInfo("id", "run"), CONVERTER, "test-ns", null);
139+
assertNull(desc.getLastFailure());
140+
}
141+
142+
@Test
143+
public void testGetLastFailurePresent() {
144+
Failure failure = Failure.newBuilder().setMessage("boom").build();
145+
ActivityExecutionInfo info = buildInfo("id", "run").toBuilder().setLastFailure(failure).build();
146+
ActivityExecutionDescription desc =
147+
new ActivityExecutionDescription(info, CONVERTER, "test-ns", null);
148+
149+
Failure result = desc.getLastFailure();
150+
assertNotNull(result);
151+
assertEquals("boom", result.getMessage());
152+
}
153+
154+
@Test
155+
public void testGetRawInfo() {
156+
ActivityExecutionInfo info = buildInfo("id", "run");
157+
ActivityExecutionDescription desc =
158+
new ActivityExecutionDescription(info, CONVERTER, "test-ns", null);
159+
assertSame(info, desc.getRawInfo());
160+
}
161+
133162
@Test
134163
public void testGetPriorityPresent() {
135164
io.temporal.api.common.v1.Priority protoPriority =

temporal-sdk/src/test/java/io/temporal/client/functional/StandaloneActivityTest.java

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@
1010
import io.temporal.activity.ActivityInfo;
1111
import io.temporal.activity.ActivityInterface;
1212
import io.temporal.activity.ActivityMethod;
13+
import io.temporal.api.activity.v1.ActivityExecutionInfo;
1314
import io.temporal.api.enums.v1.ActivityExecutionStatus;
1415
import io.temporal.api.enums.v1.ActivityIdConflictPolicy;
1516
import io.temporal.api.enums.v1.ActivityIdReusePolicy;
17+
import io.temporal.api.failure.v1.Failure;
1618
import io.temporal.client.*;
19+
import io.temporal.common.RetryOptions;
1720
import io.temporal.common.interceptors.ActivityClientCallsInterceptor;
1821
import io.temporal.common.interceptors.ActivityClientCallsInterceptor.*;
1922
import io.temporal.common.interceptors.ActivityClientCallsInterceptorBase;
2023
import io.temporal.common.interceptors.ActivityClientInterceptorBase;
24+
import io.temporal.failure.ApplicationFailure;
2125
import io.temporal.failure.CanceledFailure;
2226
import io.temporal.testing.internal.SDKTestWorkflowRule;
2327
import java.time.Duration;
@@ -86,6 +90,12 @@ public interface ConcatActivity {
8690
String concat(String a, String b);
8791
}
8892

93+
@ActivityInterface
94+
public interface AlwaysFailActivity {
95+
@ActivityMethod(name = "AlwaysFail")
96+
void alwaysFail();
97+
}
98+
8999
/** Snapshot of {@link ActivityInfo} fields captured inside an activity body. */
90100
public static class ActivityInfoSnapshot {
91101
public String activityId;
@@ -183,6 +193,13 @@ public String concat(String a, String b) {
183193
}
184194
}
185195

196+
public static class AlwaysFailActivityImpl implements AlwaysFailActivity {
197+
@Override
198+
public void alwaysFail() {
199+
throw ApplicationFailure.newFailure("deliberate failure", "test-type");
200+
}
201+
}
202+
186203
// ---------------------------------------------------------------------------
187204
// Test rule
188205
// ---------------------------------------------------------------------------
@@ -197,7 +214,8 @@ public String concat(String a, String b) {
197214
new AsyncCompletionActivityImpl(),
198215
new InspectInfoActivityImpl(),
199216
new EchoVoidActivityImpl(),
200-
new ConcatActivityImpl())
217+
new ConcatActivityImpl(),
218+
new AlwaysFailActivityImpl())
201219
.build();
202220

203221
// ---------------------------------------------------------------------------
@@ -812,6 +830,65 @@ public void testCompletionClientStandaloneCompleteWithRunId() throws Interrupted
812830
}
813831
}
814832

833+
@Test
834+
public void testDescribeRawInfoMatchesTypedAccessors() {
835+
assumeTrue(SDKTestWorkflowRule.useExternalService);
836+
ActivityClient client = newActivityClient();
837+
String activityId = uniqueId();
838+
839+
ActivityHandle<String> handle =
840+
client.start(SimpleActivity.class, SimpleActivity::execute, simpleOpts(activityId), "raw");
841+
handle.getResult();
842+
843+
ActivityExecutionDescription desc = handle.describe();
844+
ActivityExecutionInfo rawInfo = desc.getRawInfo();
845+
846+
assertEquals(desc.getActivityId(), rawInfo.getActivityId());
847+
assertEquals(desc.getActivityType(), rawInfo.getActivityType().getName());
848+
assertEquals(desc.getTaskQueue(), rawInfo.getTaskQueue());
849+
assertEquals(desc.getStatus(), rawInfo.getStatus());
850+
assertEquals(desc.getAttempt(), rawInfo.getAttempt());
851+
}
852+
853+
@Test
854+
public void testDescribeLastFailureIsPopulatedDuringRetryBackoff() {
855+
assumeTrue(SDKTestWorkflowRule.useExternalService);
856+
ActivityClient client = newActivityClient();
857+
StartActivityOptions opts =
858+
StartActivityOptions.newBuilder()
859+
.setId(uniqueId())
860+
.setTaskQueue(testWorkflowRule.getTaskQueue())
861+
.setScheduleToCloseTimeout(Duration.ofMinutes(5))
862+
.setStartToCloseTimeout(Duration.ofSeconds(10))
863+
.setRetryOptions(
864+
RetryOptions.newBuilder()
865+
.setMaximumAttempts(10)
866+
.setInitialInterval(Duration.ofSeconds(15))
867+
.build())
868+
.build();
869+
870+
ActivityHandle<Void> handle =
871+
client.start(AlwaysFailActivity.class, AlwaysFailActivity::alwaysFail, opts);
872+
try {
873+
assertEventually(
874+
Duration.ofSeconds(60),
875+
() -> {
876+
ActivityExecutionDescription desc = handle.describe();
877+
Failure lastFailure = desc.getLastFailure();
878+
assertNotNull("last_failure should be set after a failed attempt", lastFailure);
879+
assertEquals("deliberate failure", lastFailure.getMessage());
880+
// raw info must agree with the typed accessor
881+
assertTrue(desc.getRawInfo().hasLastFailure());
882+
assertEquals(lastFailure, desc.getRawInfo().getLastFailure());
883+
});
884+
} finally {
885+
try {
886+
handle.terminate("test cleanup");
887+
} catch (Exception ignored) {
888+
}
889+
}
890+
}
891+
815892
// ---------------------------------------------------------------------------
816893
// Interceptor helpers
817894
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)