Invoke operations¶
Invoke other Lambda functions durably¶
The invoke operation calls another Lambda function and waits for its result. When you
call context.invoke(), the SDK checkpoints the start of the operation and suspends the
calling function. The durable functions backend invokes the target function. The calling
function does not consume compute while waiting for the result. When the target function
completes, the backend checkpoints the result and then starts a new invocation to resume
execution.
You can invoke both durable functions and standard on-demand Lambda functions.
Note
context.invoke() is a durable functions operation that invokes a Lambda function on
your behalf. It is distinct from the
Lambda Invoke API,
which invokes a function directly. context.invoke() suspends the calling function
entirely and resumes it in a new invocation when the result is ready.
Invoke lifecycle¶
sequenceDiagram
participant Inv1 as Invocation 1
participant Backend as Durable Backend
participant Target as Target Function
participant Inv2 as Invocation 2
note over Inv1, Inv2: Single Execution
Inv1->>Backend: context.invoke() → checkpoint start
Backend->>Target: Invoke target function
Inv1->>Backend: Suspend (invocation terminates, no compute)
Target->>Target: Execute
Target->>Backend: Return result
Backend->>Backend: Checkpoint result
Backend->>Inv2: Start new invocation, resume from checkpoint
Inv2->>Inv2: Continue execution with result
- Function The durable Lambda function. This contains your code.
- Target Function The Lambda function targetted by the durable invoke.
- Execution The complete end-to-end lifecycle of a durable function, spanning potentially multiple invocations.
- Invocation A single Lambda invocation within the execution. The invocation terminates at the durable invoke operation. The backend starts a new invocation when the target function completes, and replays to resume at the invoke point with the result.
Invoke walkthrough¶
import { DurableContext, durableExecution } from "aws-durable-execution-sdk-js";
interface OrderEvent {
orderId: string;
amount: number;
}
interface ValidationResult {
valid: boolean;
reason?: string;
}
interface PaymentResult {
transactionId: string;
}
export const handler = durableExecution(
async (event: OrderEvent, context: DurableContext) => {
const validation = await context.invoke<OrderEvent, ValidationResult>(
"validate-order",
"validate-order-function:live",
{ orderId: event.orderId, amount: event.amount },
);
if (!validation.valid) {
return { status: "rejected", reason: validation.reason };
}
const payment = await context.invoke<OrderEvent, PaymentResult>(
"process-payment",
"payment-processor-function:live",
{ orderId: event.orderId, amount: event.amount },
);
return { status: "completed", transactionId: payment.transactionId };
},
);
from aws_durable_execution_sdk_python import DurableContext, durable_execution
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
validation = context.invoke(
"validate-order-function:live",
{"order_id": event["order_id"], "amount": event["amount"]},
name="validate-order",
)
if not validation["valid"]:
return {"status": "rejected", "reason": validation.get("reason")}
payment = context.invoke(
"payment-processor-function:live",
{"order_id": event["order_id"], "amount": event["amount"]},
name="process-payment",
)
return {"status": "completed", "transaction_id": payment["transaction_id"]}
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
public class ProcessOrder extends DurableHandler<OrderEvent, OrderResult> {
@Override
public OrderResult handle(OrderEvent event, DurableContext context) {
ValidationResult validation = context.invoke(
"validate-order",
"validate-order-function:live",
event,
ValidationResult.class
);
if (!validation.isValid()) {
return OrderResult.rejected(validation.getReason());
}
PaymentResult payment = context.invoke(
"process-payment",
"payment-processor-function:live",
event,
PaymentResult.class
);
return OrderResult.completed(payment.getTransactionId());
}
}
When this function runs:
- The SDK checkpoints the first invoke operation's start and triggers
validate-order-function - The calling function suspends until the validation result is available
- The backend checkpoints the invoke's result and reinvokes the calling function
- Execution resumes with the validation result
- The SDK checkpoints the second invoke's start and triggers
payment-processor-function - The calling function suspends again until the payment result is available
Method signature¶
invoke¶
// Named overload
invoke<TInput, TOutput>(
name: string,
funcId: string,
input?: TInput,
config?: InvokeConfig<TInput, TOutput>,
): DurablePromise<TOutput>
// Unnamed overload
invoke<TInput, TOutput>(
funcId: string,
input?: TInput,
config?: InvokeConfig<TInput, TOutput>,
): DurablePromise<TOutput>
Parameters:
name(optional) A name for the invoke operation. Passundefinedto omit.funcId(required) The function ID or ARN. An alias or version qualifier is required for durable functions. On-demand functions do not require a qualifier.input(optional) The payload to send to the invoked function.config(optional) AnInvokeConfigobject.
Returns: DurablePromise<TOutput>. Use await to get the result.
Throws: InvokeError if the invoked function fails or times out.
def invoke(
function_name: str,
payload: P,
name: str | None = None,
config: InvokeConfig[P, R] | None = None,
) -> R: ...
Parameters:
function_name(required) The function name or ARN. An alias or version qualifier is required for durable functions. On-demand functions do not require a qualifier.payload(required) The payload to send to the invoked function.name(optional) A name for the invoke operation.config(optional) AnInvokeConfigobject.
Returns: R, the return value of the invoked function.
Raises: CallableRuntimeError if the invoked function fails or times out.
// sync
<T, U> T invoke(String name, String functionName, U payload, Class<T> resultType)
<T, U> T invoke(String name, String functionName, U payload, Class<T> resultType, InvokeConfig config)
<T, U> T invoke(String name, String functionName, U payload, TypeToken<T> resultType)
<T, U> T invoke(String name, String functionName, U payload, TypeToken<T> resultType, InvokeConfig config)
// async
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, Class<T> resultType)
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, Class<T> resultType, InvokeConfig config)
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, TypeToken<T> resultType)
<T, U> DurableFuture<T> invokeAsync(String name, String functionName, U payload, TypeToken<T> resultType, InvokeConfig config)
Parameters:
name(required) A nullable name for the invoke operation.functionName(required) The function name or ARN. An alias or version qualifier is required for durable functions. On-demand functions do not require a qualifier.payload(required) The payload to send to the invoked function.resultType(required) TheClass<T>orTypeToken<T>for deserialization.config(optional) AnInvokeConfigobject.
Returns: T (sync) or DurableFuture<T> (async via invokeAsync()).
Throws:
InvokeFailedExceptionif the invoked function fails.InvokeTimedOutExceptionif the invocation times out (Python only, viaInvokeConfig.timeout).InvokeStoppedExceptionif the invocation was stopped.
InvokeConfig¶
interface InvokeConfig<I, O> {
payloadSerdes?: Serdes<I>;
resultSerdes?: Serdes<O>;
tenantId?: string;
}
Parameters:
payloadSerdes(optional) CustomSerdes<I>for the payload sent to the invoked function. Defaults to JSON serialization.resultSerdes(optional) CustomSerdes<O>for the result returned from the invoked function. Defaults to JSON serialization.tenantId(optional) Tenant identifier for multi-tenant isolation.
@dataclass(frozen=True)
class InvokeConfig(Generic[P, R]):
serdes_payload: SerDes[P] | None = None
serdes_result: SerDes[R] | None = None
tenant_id: str | None = None
Parameters:
serdes_payload(optional) CustomSerDesfor the payload. Defaults to JSON serialization.serdes_result(optional) CustomSerDesfor the result. Defaults to JSON serialization.tenant_id(optional) Tenant identifier for multi-tenant isolation.
InvokeConfig.builder()
.payloadSerDes(SerDes) // optional
.serDes(SerDes) // optional, sets result SerDes
.tenantId(String) // optional
.build()
Parameters:
payloadSerDes(optional) CustomSerDesfor the payload. Defaults to JSON serialization.serDes(optional) CustomSerDesfor the result. Defaults to JSON serialization.tenantId(optional) Tenant identifier for multi-tenant isolation.
Naming invoke operations¶
Name invoke operations to make them easier to identify in logs and tests. You can use names to describe what the invocation does rather than which function it calls.
The name is the first argument. Pass undefined to omit it.
Pass name as a keyword argument. If omitted, the operation has no name in logs.
The name is always the first argument. Pass null to omit it.
Configuration¶
Configure invoke behavior using InvokeConfig:
import {
DurableContext,
InvokeConfig,
durableExecution,
} from "aws-durable-execution-sdk-js";
export const handler = durableExecution(
async (event: { orderId: string }, context: DurableContext) => {
const config: InvokeConfig<{ orderId: string }, { status: string }> = {
tenantId: event.orderId,
};
const result = await context.invoke(
"process-order",
"order-processor-function:live",
{ orderId: event.orderId },
config,
);
return result;
},
);
from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.config import InvokeConfig
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
config = InvokeConfig(tenant_id=event.get("tenant_id"))
result = context.invoke(
"order-processor-function:live",
{"order_id": event["order_id"]},
name="process-order",
config=config,
)
return result
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.config.InvokeConfig;
public class InvokeWithConfig extends DurableHandler<OrderEvent, OrderResult> {
@Override
public OrderResult handle(OrderEvent event, DurableContext context) {
InvokeConfig config = InvokeConfig.builder()
.tenantId(event.getTenantId())
.build();
return context.invoke(
"process-order",
"order-processor-function:live",
event,
OrderResult.class,
config
);
}
}
Error handling¶
Errors from the invoked function propagate to the calling function. Catch them to handle failures without letting them terminate the calling function.
import {
DurableContext,
InvokeError,
durableExecution,
} from "aws-durable-execution-sdk-js";
export const handler = durableExecution(
async (event: { orderId: string }, context: DurableContext) => {
try {
const result = await context.invoke(
"process-payment",
"payment-processor-function:live",
{ orderId: event.orderId },
);
return { status: "success", result };
} catch (err) {
if (err instanceof InvokeError) {
return { status: "failed", reason: err.message };
}
throw err;
}
},
);
from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.exceptions import CallableRuntimeError
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
try:
result = context.invoke(
"payment-processor-function:live",
{"order_id": event["order_id"]},
name="process-payment",
)
return {"status": "success", "result": result}
except CallableRuntimeError as e:
return {"status": "failed", "reason": str(e)}
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.exception.InvokeFailedException;
import software.amazon.lambda.durable.exception.InvokeTimedOutException;
public class HandleInvocationError extends DurableHandler<OrderEvent, OrderResult> {
@Override
public OrderResult handle(OrderEvent event, DurableContext context) {
try {
PaymentResult payment = context.invoke(
"process-payment",
"payment-processor-function:live",
event,
PaymentResult.class
);
return OrderResult.success(payment.getTransactionId());
} catch (InvokeTimedOutException e) {
return OrderResult.failed("payment timed out");
} catch (InvokeFailedException e) {
return OrderResult.failed(e.getMessage());
}
}
}
Java exposes separate exception types for different failure modes:
InvokeFailedException for function errors, InvokeTimedOutException for timeouts
(Python only), and InvokeStoppedException when the invocation was stopped. All extend
InvokeException.
See also¶
- Callback Wait for callback response from an external system
- Child contexts Group operations
- Parallel operations Execute operations concurrently
- Map operations Run operation for each item in a collection