Skip to content

Commit 5b1ead9

Browse files
committed
refactor exceptions
1 parent 475c38e commit 5b1ead9

27 files changed

Lines changed: 264 additions & 243 deletions

examples/src/main/java/com/amazonaws/lambda/durable/examples/ErrorHandlingExample.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ public String handleRequest(Object input, DurableContext context) {
8585
.semantics(StepSemantics.AT_MOST_ONCE_PER_RETRY)
8686
.build());
8787
} catch (StepInterruptedException e) {
88-
logger.warn("Payment step interrupted, checking external status: {}", e.getOperationId());
88+
logger.warn(
89+
"Payment step interrupted, checking external status: {}",
90+
e.getOperation().id());
8991
// In real code: check payment provider for transaction status
9092
// If payment went through, return success; otherwise, handle appropriately
9193
paymentResult = context.step("verify-payment-status", String.class, () -> "verified-payment");

sdk-integration-tests/src/test/java/com/amazonaws/lambda/durable/CallbackIntegrationTest.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
import static org.junit.jupiter.api.Assertions.*;
66

7-
import com.amazonaws.lambda.durable.exception.CallbackFailedException;
8-
import com.amazonaws.lambda.durable.exception.CallbackTimeoutException;
97
import com.amazonaws.lambda.durable.model.ExecutionStatus;
108
import com.amazonaws.lambda.durable.serde.JacksonSerDes;
119
import com.amazonaws.lambda.durable.serde.SerDes;
@@ -90,9 +88,8 @@ void callbackFailureFlow() {
9088
result = runner.run("test");
9189
assertEquals(ExecutionStatus.FAILED, result.getStatus());
9290
assertTrue(result.getError().isPresent());
93-
assertEquals(
94-
CallbackFailedException.class.getName(), result.getError().get().errorType());
95-
assertTrue(result.getError().get().errorMessage().contains("Rejected"));
91+
assertEquals("Rejected", result.getError().get().errorType());
92+
assertEquals("Request denied", result.getError().get().errorMessage());
9693
}
9794

9895
@Test
@@ -116,10 +113,9 @@ void callbackTimeoutFlow() {
116113
// Re-run - callback timed out, throws CallbackTimeoutException
117114
result = runner.run("test");
118115
assertEquals(ExecutionStatus.FAILED, result.getStatus());
119-
assertTrue(result.getError().isPresent());
116+
assertFalse(result.getError().isPresent());
120117
assertEquals(
121-
CallbackTimeoutException.class.getName(),
122-
result.getError().get().errorType());
118+
OperationStatus.TIMED_OUT, result.getFailedOperations().get(0).getStatus());
123119
}
124120

125121
@Test
@@ -258,8 +254,8 @@ void callbackFailedExceptionHandlesVariousErrorFormats() {
258254
var result = runner.run("test");
259255
assertEquals(ExecutionStatus.FAILED, result.getStatus());
260256
assertTrue(result.getError().isPresent());
261-
assertTrue(result.getError().get().errorMessage().contains("ValidationError"));
262-
assertTrue(result.getError().get().errorMessage().contains("Invalid input data"));
257+
assertEquals("ValidationError", result.getError().get().errorType());
258+
assertEquals("Invalid input data", result.getError().get().errorMessage());
263259
assertNotNull(result.getError().get().stackTrace());
264260
assertEquals(1, result.getError().get().stackTrace().size());
265261
}

sdk-integration-tests/src/test/java/com/amazonaws/lambda/durable/InvokeIntegrationTest.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package com.amazonaws.lambda.durable;
44

55
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
67
import static org.junit.jupiter.api.Assertions.assertNull;
78

89
import com.amazonaws.lambda.durable.exception.InvokeFailedException;
@@ -93,8 +94,8 @@ void testInvokeWithFailedResults() {
9394
return new TestOutput(result);
9495
} catch (InvokeFailedException ex) {
9596
assertEquals("error output", ex.getMessage());
96-
assertEquals("error data", ex.getErrorData());
97-
assertEquals("error type", ex.getErrorType());
97+
assertEquals("error data", ex.getErrorObject().errorData());
98+
assertEquals("error type", ex.getErrorObject().errorType());
9899
throw ex;
99100
}
100101
});
@@ -114,9 +115,8 @@ void testInvokeWithFailedResults() {
114115
var output2 = runner.run(new TestInput("test"));
115116

116117
assertEquals(ExecutionStatus.FAILED, output2.getStatus());
117-
// todo: the error object should equal to the error object returned by chained invoke
118118
ErrorObject error = output2.getError().orElseThrow();
119-
assertEquals("com.amazonaws.lambda.durable.exception.InvokeFailedException", error.errorType());
119+
assertEquals("error type", error.errorType());
120120
assertEquals("error output", error.errorMessage());
121121
}
122122

@@ -128,8 +128,8 @@ void testInvokeWithStoppedResults() {
128128
return new TestOutput(result);
129129
} catch (InvokeFailedException ex) {
130130
assertEquals("error output", ex.getMessage());
131-
assertEquals("error data", ex.getErrorData());
132-
assertEquals("error type", ex.getErrorType());
131+
assertEquals("error data", ex.getErrorObject().errorData());
132+
assertEquals("error type", ex.getErrorObject().errorType());
133133
throw ex;
134134
}
135135
});
@@ -149,9 +149,8 @@ void testInvokeWithStoppedResults() {
149149
var output2 = runner.run(new TestInput("test"));
150150

151151
assertEquals(ExecutionStatus.FAILED, output2.getStatus());
152-
// todo: the error object should equal to the error object returned by chained invoke
153152
ErrorObject error = output2.getError().orElseThrow();
154-
assertEquals("com.amazonaws.lambda.durable.exception.InvokeStoppedException", error.errorType());
153+
assertEquals("error type", error.errorType());
155154
assertEquals("error output", error.errorMessage());
156155
}
157156

@@ -163,8 +162,8 @@ void testInvokeWithTimeoutResults() {
163162
return new TestOutput(result);
164163
} catch (InvokeFailedException ex) {
165164
assertNull(ex.getMessage());
166-
assertNull(ex.getErrorData());
167-
assertNull(ex.getErrorType());
165+
assertNull(ex.getErrorObject().errorData());
166+
assertNull(ex.getErrorObject().errorType());
168167
throw ex;
169168
}
170169
});
@@ -178,9 +177,6 @@ void testInvokeWithTimeoutResults() {
178177
var output2 = runner.run(new TestInput("test"));
179178

180179
assertEquals(ExecutionStatus.FAILED, output2.getStatus());
181-
// todo: the error object should equal to the error object returned by chained invoke
182-
ErrorObject error = output2.getError().orElseThrow();
183-
assertEquals("com.amazonaws.lambda.durable.exception.InvokeTimedOutException", error.errorType());
184-
assertNull(error.errorMessage());
180+
assertFalse(output2.getError().isPresent());
185181
}
186182
}

sdk-testing/src/main/java/com/amazonaws/lambda/durable/testing/TestResult.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.Optional;
11+
import java.util.Set;
1112
import java.util.stream.Collectors;
1213
import software.amazon.awssdk.services.lambda.model.ErrorObject;
1314
import software.amazon.awssdk.services.lambda.model.Event;
1415
import software.amazon.awssdk.services.lambda.model.OperationStatus;
1516

1617
public class TestResult<O> {
18+
private static final Set<OperationStatus> FAIL_OPERATION_STATUS = Set.of(
19+
OperationStatus.FAILED, OperationStatus.CANCELLED, OperationStatus.TIMED_OUT, OperationStatus.STOPPED);
1720
private final ExecutionStatus status;
1821
private final String resultPayload;
1922
private final ErrorObject error;
@@ -90,7 +93,7 @@ public List<TestOperation> getSucceededOperations() {
9093

9194
public List<TestOperation> getFailedOperations() {
9295
return operations.stream()
93-
.filter(op -> op.getStatus() == OperationStatus.FAILED)
96+
.filter(op -> FAIL_OPERATION_STATUS.contains(op.getStatus()))
9497
.toList();
9598
}
9699
}

sdk/src/main/java/com/amazonaws/lambda/durable/DurableExecutor.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package com.amazonaws.lambda.durable;
44

5+
import com.amazonaws.lambda.durable.exception.DurableOperationException;
6+
import com.amazonaws.lambda.durable.exception.StepFailedException;
57
import com.amazonaws.lambda.durable.execution.ExecutionManager;
68
import com.amazonaws.lambda.durable.model.DurableExecutionInput;
79
import com.amazonaws.lambda.durable.model.DurableExecutionOutput;
810
import com.amazonaws.lambda.durable.serde.SerDes;
11+
import com.amazonaws.lambda.durable.util.AsyncHelper;
912
import com.amazonaws.services.lambda.runtime.Context;
1013
import com.amazonaws.services.lambda.runtime.RequestHandler;
1114
import java.nio.charset.StandardCharsets;
1215
import java.util.concurrent.CompletableFuture;
1316
import java.util.function.BiFunction;
1417
import org.slf4j.Logger;
1518
import org.slf4j.LoggerFactory;
19+
import software.amazon.awssdk.services.lambda.model.ErrorObject;
1620
import software.amazon.awssdk.services.lambda.model.Operation;
1721
import software.amazon.awssdk.services.lambda.model.OperationAction;
1822
import software.amazon.awssdk.services.lambda.model.OperationType;
@@ -97,9 +101,9 @@ public static <I, O> DurableExecutionOutput execute(
97101
try {
98102
handlerFuture.join(); // Will throw the exception
99103
} catch (Exception e) {
100-
Throwable cause = e.getCause() != null ? e.getCause() : e;
104+
Throwable cause = AsyncHelper.unwrap(e);
101105
logger.debug("Execution failed: {}", cause.getMessage());
102-
return DurableExecutionOutput.failure(cause, serDes);
106+
return DurableExecutionOutput.failure(buildErrorObject(cause, serDes));
103107
}
104108
}
105109

@@ -134,8 +138,7 @@ public static <I, O> DurableExecutionOutput execute(
134138
logger.debug("Execution completed");
135139
return DurableExecutionOutput.success(outputPayload);
136140
} catch (Exception e) {
137-
Throwable cause = e.getCause() != null ? e.getCause() : e;
138-
return DurableExecutionOutput.failure(cause, serDes);
141+
return DurableExecutionOutput.failure(buildErrorObject(AsyncHelper.unwrap(e), serDes));
139142
} finally {
140143
// We shutdown the execution to make sure remaining checkpoint calls in the queue are drained
141144
executionManager.shutdown();
@@ -146,6 +149,18 @@ public static <I, O> DurableExecutionOutput execute(
146149
}
147150
}
148151

152+
private static ErrorObject buildErrorObject(Throwable e, SerDes serDes) {
153+
if (e instanceof DurableOperationException) {
154+
return ((DurableOperationException) e).getErrorObject();
155+
}
156+
return ErrorObject.builder()
157+
.errorType(e.getClass().getName())
158+
.errorMessage(e.getMessage())
159+
.stackTrace(StepFailedException.serializeStackTrace(e.getStackTrace()))
160+
.errorData(serDes.serialize(e))
161+
.build();
162+
}
163+
149164
private static <I> I extractUserInput(Operation executionOp, SerDes serDes, Class<I> inputType) {
150165

151166
if (executionOp.executionDetails() == null) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.amazonaws.lambda.durable.exception;
4+
5+
import software.amazon.awssdk.services.lambda.model.ErrorObject;
6+
import software.amazon.awssdk.services.lambda.model.Operation;
7+
8+
public class CallbackException extends DurableOperationException {
9+
private final String callbackId;
10+
11+
public CallbackException(String callbackId, Operation operation, ErrorObject errorObject, String message) {
12+
super(operation, errorObject, message);
13+
this.callbackId = callbackId;
14+
}
15+
16+
public String getCallbackId() {
17+
return callbackId;
18+
}
19+
}

sdk/src/main/java/com/amazonaws/lambda/durable/exception/CallbackFailedException.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,12 @@
33
package com.amazonaws.lambda.durable.exception;
44

55
import software.amazon.awssdk.services.lambda.model.ErrorObject;
6+
import software.amazon.awssdk.services.lambda.model.Operation;
67

78
/** Exception thrown when a callback fails due to an error from the external system. */
8-
public class CallbackFailedException extends DurableExecutionException {
9-
public CallbackFailedException(ErrorObject error) {
10-
super(
11-
buildMessage(error),
12-
null,
13-
error.stackTrace() != null && !error.stackTrace().isEmpty()
14-
? deserializeStackTrace(error.stackTrace())
15-
: new StackTraceElement[0]);
16-
}
17-
18-
public CallbackFailedException(String message) {
19-
super(message);
9+
public class CallbackFailedException extends CallbackException {
10+
public CallbackFailedException(String callbackId, Operation operation, ErrorObject error) {
11+
super(callbackId, operation, error, buildMessage(error));
2012
}
2113

2214
private static String buildMessage(ErrorObject error) {

sdk/src/main/java/com/amazonaws/lambda/durable/exception/CallbackTimeoutException.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package com.amazonaws.lambda.durable.exception;
44

5+
import software.amazon.awssdk.services.lambda.model.Operation;
6+
57
/** Exception thrown when a callback times out. */
6-
public class CallbackTimeoutException extends DurableExecutionException {
7-
public CallbackTimeoutException(String callbackId) {
8-
super("Callback timed out: " + callbackId);
8+
public class CallbackTimeoutException extends CallbackException {
9+
public CallbackTimeoutException(String callbackId, Operation operation) {
10+
super(callbackId, operation, null, "Callback timed out: " + callbackId);
911
}
1012
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.amazonaws.lambda.durable.exception;
4+
5+
import software.amazon.awssdk.services.lambda.model.ErrorObject;
6+
import software.amazon.awssdk.services.lambda.model.Operation;
7+
import software.amazon.awssdk.services.lambda.model.OperationStatus;
8+
9+
public class DurableOperationException extends DurableExecutionException {
10+
private final Operation operation;
11+
private final ErrorObject errorObject;
12+
13+
public DurableOperationException(Operation operation, ErrorObject errorObject) {
14+
this(operation, errorObject, errorObject != null ? errorObject.errorMessage() : null);
15+
}
16+
17+
public DurableOperationException(Operation operation, ErrorObject errorObject, String errorMessage) {
18+
this(
19+
operation,
20+
errorObject,
21+
errorMessage,
22+
errorObject != null ? DurableExecutionException.deserializeStackTrace(errorObject.stackTrace()) : null);
23+
}
24+
25+
public DurableOperationException(
26+
Operation operation, ErrorObject errorObject, String errorMessage, StackTraceElement[] stackTrace) {
27+
super(errorMessage, null, stackTrace);
28+
this.operation = operation;
29+
this.errorObject = errorObject;
30+
}
31+
32+
public ErrorObject getErrorObject() {
33+
return errorObject;
34+
}
35+
36+
public Operation getOperation() {
37+
return operation;
38+
}
39+
40+
public OperationStatus getOperationStatus() {
41+
return operation.status();
42+
}
43+
44+
public String operationId() {
45+
return operation.id();
46+
}
47+
}

sdk/src/main/java/com/amazonaws/lambda/durable/exception/InvokeException.java

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,10 @@
33
package com.amazonaws.lambda.durable.exception;
44

55
import software.amazon.awssdk.services.lambda.model.ErrorObject;
6-
import software.amazon.awssdk.services.lambda.model.OperationStatus;
6+
import software.amazon.awssdk.services.lambda.model.Operation;
77

8-
public class InvokeException extends DurableExecutionException {
9-
private final ErrorObject errorObject;
10-
private final OperationStatus operationStatus;
11-
12-
public InvokeException(OperationStatus operationStatus, ErrorObject errorObject) {
13-
super(
14-
errorObject != null ? errorObject.errorMessage() : null,
15-
null,
16-
errorObject != null ? DurableExecutionException.deserializeStackTrace(errorObject.stackTrace()) : null);
17-
this.operationStatus = operationStatus;
18-
this.errorObject = errorObject;
19-
}
20-
21-
public String getErrorData() {
22-
return errorObject == null ? null : errorObject.errorData();
23-
}
24-
25-
public String getErrorType() {
26-
return errorObject == null ? null : errorObject.errorType();
27-
}
28-
29-
public OperationStatus getOperationStatus() {
30-
return operationStatus;
8+
public class InvokeException extends DurableOperationException {
9+
public InvokeException(Operation operation, ErrorObject errorObject) {
10+
super(operation, errorObject);
3111
}
3212
}

0 commit comments

Comments
 (0)