Skip to content

Commit 18ae802

Browse files
zhongkechenphipag
andauthored
[feat: invoke] initial invoke implementation (#20)
* initial invoke implementation * Update sdk/src/main/java/com/amazonaws/lambda/durable/InvokeConfig.java Co-authored-by: Philipp Page <pagejep@amazon.com> * Update sdk/src/main/java/com/amazonaws/lambda/durable/InvokeConfig.java Co-authored-by: Philipp Page <pagejep@amazon.com> * format with Spotless * add local runner for invoke * Update sdk/src/main/java/com/amazonaws/lambda/durable/operation/InvokeOperation.java Co-authored-by: Philipp Page <pagejep@amazon.com> * remove a local var * add timeout and stop for invoke in local runner * refactor Invoke exceptions * update README with invoke --------- Co-authored-by: Philipp Page <pagejep@amazon.com>
1 parent 7bfcc88 commit 18ae802

22 files changed

Lines changed: 1230 additions & 6 deletions

File tree

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ Build resilient, long-running AWS Lambda functions that automatically checkpoint
1818
Your durable function extends `DurableHandler<I, O>` and implements `handleRequest(I input, DurableContext ctx)`. The `DurableContext` is your interface to durable operations:
1919

2020
- `ctx.step()` – Execute code and checkpoint the result
21-
- `ctx.stepAsync()` – Start concurrent operations
21+
- `ctx.stepAsync()` – Start a concurrent step
2222
- `ctx.wait()` – Suspend execution without compute charges
2323
- `ctx.createCallback()` – Wait for external events (approvals, webhooks)
24+
- `ctx.invoke()` – Invoke another Lambda function and wait for the result
25+
- `ctx.invokeAsync()` – Start a concurrent Lambda function invocation
2426

2527
## Quick Start
2628

@@ -168,6 +170,25 @@ try {
168170
}
169171
```
170172

173+
### invoke() - Invoke another Lambda function
174+
175+
176+
```java
177+
// Basic invoke
178+
var result = ctx.invoke("invoke-function",
179+
"function-name",
180+
"\"payload\"",
181+
Result.class,
182+
InvokeConfig.builder()
183+
.payloadSerDes(...) // payload serializer
184+
.resultSerDes(...) // result deserializer
185+
.timeout(Duration.of(...)) // wait timeout
186+
.tenantId(...) // Lambda tenantId
187+
.build()
188+
);
189+
190+
```
191+
171192
## Step Configuration
172193

173194
Configure step behavior with `StepConfig`:

docs/design.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,28 @@ aws-durable-execution-sdk-java/
3434
T step(String name, Class<T> type, Supplier<T> func)
3535
T step(String name, Class<T> type, Supplier<T> func, StepConfig config)
3636
T step(String name, TypeToken<T> type, Supplier<T> func)
37+
T step(String name, TypeToken<T> type, Supplier<T> func, StepConfig config)
3738

3839
// Asynchronous step
3940
DurableFuture<T> stepAsync(String name, Class<T> type, Supplier<T> func)
4041
DurableFuture<T> stepAsync(String name, Class<T> type, Supplier<T> func, StepConfig config)
42+
DurableFuture<T> stepAsync(String name, TypeToken<T> type, Supplier<T> func)
43+
DurableFuture<T> stepAsync(String name, TypeToken<T> type, Supplier<T> func, StepConfig config)
4144

4245
// Wait
4346
void wait(Duration duration)
4447
void wait(String name, Duration duration)
48+
49+
// Invoke
50+
T invoke(String name, String functionName, U payload, Class<T> resultType)
51+
T invoke(String name, String functionName, U payload, TypeToken<T> resultType)
52+
T invoke(String name, String functionName, U payload, Class<T> resultType, InvokeConfig config)
53+
T invoke(String name, String functionName, U payload, TypeToken<T> resultType, InvokeConfig config)
54+
55+
DurableFuture<T> invokeAsync(String name, String functionName, U payload, Class<T> resultType)
56+
DurableFuture<T> invokeAsync(String name, String functionName, U payload, Class<T> resultType, InvokeConfig config)
57+
DurableFuture<T> invokeAsync(String name, String functionName, U payload, TypeToken<T> resultType)
58+
DurableFuture<T> invokeAsync(String name, String functionName, U payload, TypeToken<T> resultType, InvokeConfig config)
4559

4660
// Lambda context access
4761
Context getLambdaContext()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.amazonaws.lambda.durable.examples;
4+
5+
import com.amazonaws.lambda.durable.DurableContext;
6+
import com.amazonaws.lambda.durable.DurableHandler;
7+
import com.amazonaws.lambda.durable.InvokeConfig;
8+
9+
/**
10+
* Simple example demonstrating basic invoke execution with the Durable Execution SDK.
11+
*
12+
* <p>This handler invokes another durable lambda function simple-step-example
13+
*/
14+
public class SimpleInvokeExample extends DurableHandler<GreetingRequest, String> {
15+
16+
@Override
17+
public String handleRequest(GreetingRequest input, DurableContext context) {
18+
// invoke `simple-step-example` function
19+
var future = context.invokeAsync(
20+
"call-greeting1",
21+
"simple-step-example:$LATEST",
22+
input,
23+
String.class,
24+
InvokeConfig.builder().build());
25+
var result2 = context.invoke(
26+
"call-greeting2",
27+
"simple-step-example:$LATEST",
28+
input,
29+
String.class,
30+
InvokeConfig.builder().build());
31+
return future.get() + result2;
32+
}
33+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.amazonaws.lambda.durable.examples;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
7+
import com.amazonaws.lambda.durable.model.ExecutionStatus;
8+
import com.amazonaws.lambda.durable.testing.LocalDurableTestRunner;
9+
import org.junit.jupiter.api.Test;
10+
import software.amazon.awssdk.services.lambda.model.ErrorObject;
11+
12+
class InvokeExampleTest {
13+
14+
@Test
15+
void testSimpleInvokeExample_completeSequentially() {
16+
var handler = new SimpleInvokeExample();
17+
var runner = LocalDurableTestRunner.create(GreetingRequest.class, handler);
18+
19+
// First run
20+
var input = new GreetingRequest("world");
21+
var output1 = runner.run(input);
22+
23+
assertEquals(ExecutionStatus.PENDING, output1.getStatus());
24+
25+
// Second run
26+
runner.completeChainedInvoke("call-greeting1", "\"hello\"");
27+
var output2 = runner.run(input);
28+
assertEquals(ExecutionStatus.PENDING, output2.getStatus());
29+
30+
// Third run
31+
runner.completeChainedInvoke("call-greeting2", "\"world\"");
32+
var output3 = runner.run(input);
33+
assertEquals(ExecutionStatus.SUCCEEDED, output3.getStatus());
34+
assertEquals("helloworld", output3.getResult(String.class));
35+
}
36+
37+
@Test
38+
void testSimpleInvokeExample_completeConcurrently() {
39+
var handler = new SimpleInvokeExample();
40+
var runner = LocalDurableTestRunner.create(GreetingRequest.class, handler);
41+
42+
// First run
43+
var input = new GreetingRequest("world");
44+
var output1 = runner.run(input);
45+
46+
assertEquals(ExecutionStatus.PENDING, output1.getStatus());
47+
48+
// Second run
49+
runner.completeChainedInvoke("call-greeting1", "\"hello\"");
50+
runner.completeChainedInvoke("call-greeting2", "\"world\"");
51+
var output2 = runner.run(input);
52+
assertEquals(ExecutionStatus.SUCCEEDED, output2.getStatus());
53+
assertEquals("helloworld", output2.getResult(String.class));
54+
}
55+
56+
@Test
57+
void testSimpleInvokeExample_failFirst() {
58+
var handler = new SimpleInvokeExample();
59+
var runner = LocalDurableTestRunner.create(GreetingRequest.class, handler);
60+
61+
// First run
62+
var input = new GreetingRequest("world");
63+
var output1 = runner.run(input);
64+
65+
assertEquals(ExecutionStatus.PENDING, output1.getStatus());
66+
67+
// Second run, fail the async invoke
68+
runner.failChainedInvoke("call-greeting1", ErrorObject.builder().build());
69+
var output2 = runner.run(input);
70+
assertEquals(ExecutionStatus.PENDING, output2.getStatus());
71+
72+
// Third run
73+
runner.completeChainedInvoke("call-greeting2", "\"world\"");
74+
var output3 = runner.run(input);
75+
assertEquals(ExecutionStatus.FAILED, output3.getStatus());
76+
}
77+
78+
@Test
79+
void testSimpleInvokeExample_failSecond() {
80+
var handler = new SimpleInvokeExample();
81+
var runner = LocalDurableTestRunner.create(GreetingRequest.class, handler);
82+
83+
// First run
84+
var input = new GreetingRequest("world");
85+
var output1 = runner.run(input);
86+
87+
assertEquals(ExecutionStatus.PENDING, output1.getStatus());
88+
89+
// Second run, fail the async invoke
90+
runner.failChainedInvoke("call-greeting2", ErrorObject.builder().build());
91+
var output2 = runner.run(input);
92+
assertEquals(ExecutionStatus.FAILED, output2.getStatus());
93+
}
94+
}

examples/template.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,33 @@ Resources:
3232
DockerContext: ../
3333
DockerTag: durable-examples
3434

35+
SimpleInvokeExampleFunction:
36+
Type: AWS::Serverless::Function
37+
Properties:
38+
PackageType: Image
39+
FunctionName: simple-invoke-example
40+
ImageConfig:
41+
Command: ["com.amazonaws.lambda.durable.examples.SimpleInvokeExample::handleRequest"]
42+
DurableConfig:
43+
ExecutionTimeout: 300
44+
RetentionPeriodInDays: 7
45+
Policies:
46+
- Statement:
47+
- Effect: Allow
48+
Action:
49+
- lambda:CheckpointDurableExecutions
50+
- lambda:GetDurableExecutionState
51+
- lambda:InvokeFunction
52+
Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:simple-invoke-example"
53+
- Effect: Allow
54+
Action:
55+
- lambda:InvokeFunction
56+
Resource: '*'
57+
Metadata:
58+
Dockerfile: examples/Dockerfile
59+
DockerContext: ../
60+
DockerTag: durable-examples
61+
3562
WaitExampleFunction:
3663
Type: AWS::Serverless::Function
3764
Properties:
@@ -283,6 +310,14 @@ Outputs:
283310
Description: Simple Step Example Function Name
284311
Value: !Ref SimpleStepExampleFunction
285312

313+
SimpleInvokeExampleFunction:
314+
Description: Simple Invoke Example Function ARN
315+
Value: !GetAtt SimpleInvokeExampleFunction.Arn
316+
317+
SimpleInvokeExampleFunctionName:
318+
Description: Simple Invoke Example Function Name
319+
Value: !Ref SimpleInvokeExampleFunction
320+
286321
WaitExampleFunction:
287322
Description: Wait Example Function ARN
288323
Value: !GetAtt WaitExampleFunction.Arn

0 commit comments

Comments
 (0)