Skip to content

Commit d488548

Browse files
docs: adding new Lambda features (#7917)
* docs: adding new Lambda features * Addressing all feedback * Addressing Phillip's feedback * Addressing Steffano's feedback
1 parent 4cb9997 commit d488548

File tree

18 files changed

+480
-0
lines changed

18 files changed

+480
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
---
2+
title: Durable Functions
3+
description: Using Powertools for AWS Lambda (Python) with Lambda Durable Functions
4+
---
5+
6+
<!-- markdownlint-disable MD043 -->
7+
8+
[Lambda Durable Functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html){target="_blank" rel="nofollow"} enable you to build resilient multi-step workflows that can execute for up to one year. They use checkpoints to track progress and automatically recover from failures through replay.
9+
10+
## Key concepts
11+
12+
| Concept | Description |
13+
| --------------------- | ------------------------------------------------------------------ |
14+
| **Durable execution** | Complete lifecycle of a durable function, from start to completion |
15+
| **Checkpoint** | Saved state that tracks progress through the workflow |
16+
| **Replay** | Re-execution from the beginning, skipping completed checkpoints |
17+
| **Step** | Business logic with built-in retries and progress tracking |
18+
| **Wait** | Suspend execution without incurring compute charges |
19+
20+
## How it works
21+
22+
Durable functions use a **checkpoint/replay mechanism**:
23+
24+
1. Your code runs always from the beginning
25+
2. Completed operations are skipped using stored results
26+
3. Execution of new steps continues from where it left off
27+
4. State is automatically managed by the SDK
28+
29+
## Powertools integration
30+
31+
Powertools for AWS Lambda (Python) works seamlessly with Durable Functions. The [Durable Execution SDK](https://github.com/aws/aws-durable-execution-sdk-python){target="_blank" rel="nofollow"} has native integration with Logger via `context.set_logger()`.
32+
33+
???+ note "Found an issue?"
34+
If you encounter any issues using Powertools for AWS Lambda (Python) with Durable Functions, please [open an issue](https://github.com/aws-powertools/powertools-lambda-python/issues/new?template=bug_report.yml){target="_blank"}.
35+
36+
### Logger
37+
38+
The Durable Execution SDK provides a `context.logger` instance that automatically handles **log deduplication during replays**. You can integrate Logger to get structured JSON logging while keeping the deduplication benefits.
39+
40+
For the best experience, set the Logger on the durable context. This gives you structured JSON logging with automatic log deduplication during replays:
41+
42+
```python hl_lines="5 8 12 15" title="Integrating Logger with Durable Functions"
43+
--8<-- "examples/lambda_features/durable_functions/src/using_logger.py"
44+
```
45+
46+
This gives you:
47+
48+
- **JSON structured logging** from Powertools for AWS Lambda (Python)
49+
- **Log deduplication** during replays (logs from completed operations don't repeat)
50+
- **Automatic SDK enrichment** (execution_arn, parent_id, name, attempt)
51+
- **Lambda context injection** (request_id, function_name, etc.)
52+
53+
???+ warning "Direct logger usage"
54+
If you use the Logger directly (not through `context.logger`), logs will be emitted on every replay:
55+
56+
```python
57+
# Logs will duplicate during replays
58+
logger.info("This appears on every replay")
59+
60+
# Use context.logger instead for deduplication
61+
context.logger.info("This appears only once")
62+
```
63+
64+
### Tracer
65+
66+
Tracer works with Durable Functions. Each execution creates trace segments.
67+
68+
???+ note "Trace continuity"
69+
Due to the replay mechanism, traces may be interleaved. Each execution (including replays) creates separate trace segments. Use the `execution_arn` to correlate traces.
70+
71+
```python hl_lines="5-6 9-10" title="Using Tracer with Durable Functions"
72+
--8<-- "examples/lambda_features/durable_functions/src/using_tracer.py"
73+
```
74+
75+
### Metrics
76+
77+
Metrics work with Durable Functions, but be aware that **metrics may be emitted multiple times** during replay if not handled carefully. Emit metrics at workflow completion rather than during intermediate steps to avoid counting replays as new executions.
78+
79+
```python hl_lines="6 9 18 19 20 21" title="Using Metrics with Durable Functions"
80+
--8<-- "examples/lambda_features/durable_functions/src/best_practice_metrics.py"
81+
```
82+
83+
### Idempotency
84+
85+
The `@idempotent` decorator integrates with Durable Functions and is **replay-aware**. It's useful for protecting the Lambda handler entry point, especially for Event Source Mapping (ESM) invocations like SQS, Kinesis, or DynamoDB Streams.
86+
87+
```python hl_lines="8 15" title="Using Idempotency with Durable Functions"
88+
--8<-- "examples/lambda_features/durable_functions/src/using_idempotency.py"
89+
```
90+
91+
???+ warning "Decorator ordering matters"
92+
The `@idempotent` decorator must be placed **above** `@durable_execution`. This ensures the idempotency check runs first, preventing duplicate executions before the durable workflow begins. Reversing the order would cause the durable execution to start before the idempotency check, defeating its purpose.
93+
94+
**When to use Powertools Idempotency:**
95+
96+
- Protecting the Lambda handler entry point from duplicate invocations
97+
- Methods you don't want to convert into steps but need idempotency guarantees
98+
- Event Source Mapping triggers (SQS, Kinesis, DynamoDB Streams)
99+
100+
**When you don't need it:**
101+
102+
- Steps within a durable function are already idempotent via the checkpoint mechanism
103+
104+
### Parameters
105+
106+
Parameters work normally with Durable Functions.
107+
108+
```python hl_lines="13" title="Using Parameters with Durable Functions"
109+
--8<-- "examples/lambda_features/durable_functions/src/using_parameters.py"
110+
```
111+
112+
???+ note "Parameter freshness"
113+
If the replay or execution happens within the cache TTL on the same execution environment, the parameter value may come from cache. For long-running workflows (hours/days), parameters fetched at the start may become stale. Consider fetching parameters within steps that need the latest values, and customize the caching behavior with `max_age` to control freshness.
114+
115+
## Best practices
116+
117+
### Use Idempotency for ESM triggers
118+
119+
When your durable function is triggered by Event Source Mappings (SQS, Kinesis, DynamoDB Streams), use the `@idempotent` decorator to protect against duplicate invocations.
120+
121+
```python title="Idempotency for ESM"
122+
--8<-- "examples/lambda_features/durable_functions/src/best_practice_idempotency.py"
123+
```
124+
125+
## FAQ
126+
127+
### Do I need Idempotency utility with Durable Functions?
128+
129+
It depends on your use case. Steps within a durable function are already idempotent via checkpoints. However, the `@idempotent` decorator is useful for protecting the Lambda handler entry point, especially for Event Source Mapping invocations (SQS, Kinesis, DynamoDB Streams) where the same event might trigger multiple invocations.
130+
131+
### Why do I see duplicate logs?
132+
133+
If you're using the logger directly instead of `context.logger`, logs will be emitted on every replay. Use `context.set_logger(logger)` and then `context.logger.info()` to get automatic log deduplication.
134+
135+
### How do I correlate logs across replays?
136+
137+
Use the `execution_arn` field that's automatically added to every log entry when using `context.logger`:
138+
139+
```sql
140+
fields @timestamp, @message, execution_arn
141+
| filter execution_arn = "arn:aws:lambda:us-east-1:123456789012:function:my-function:execution-id"
142+
| sort @timestamp asc
143+
```
144+
145+
### Can I use Tracer with Durable Functions?
146+
147+
Yes, but be aware that each execution (including replays) creates separate trace segments. Use the `execution_arn` as a correlation identifier for end-to-end visibility.
148+
149+
### How should I emit metrics without duplicates?
150+
151+
Emit metrics at workflow completion rather than during intermediate steps. This ensures you count completed workflows, not replay attempts.

docs/lambda-features/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: Lambda Features
3+
description: Using Powertools with advanced Lambda features
4+
---
5+
6+
<!-- markdownlint-disable MD043 -->
7+
8+
This section covers how to use Powertools for AWS Lambda (Python) with advanced Lambda features like Lambda Managed Instances and Durable Functions.
9+
10+
<div class="grid cards" markdown>
11+
12+
- :material-server:{ .lg .middle } __Lambda Managed Instances__
13+
14+
---
15+
16+
Run Lambda functions on EC2 instances with multi-concurrent invocations
17+
18+
[:octicons-arrow-right-24: Getting started](./managed-instances.md)
19+
20+
- :material-state-machine:{ .lg .middle } __Durable Functions__
21+
22+
---
23+
24+
Build resilient multi-step workflows that can execute for up to one year
25+
26+
[:octicons-arrow-right-24: Getting started](./durable-functions.md)
27+
28+
</div>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
title: Lambda Managed Instances
3+
description: Using Powertools for AWS Lambda (Python) with Lambda Managed Instances
4+
---
5+
6+
<!-- markdownlint-disable MD043 -->
7+
8+
[Lambda Managed Instances](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances.html){target="_blank" rel="nofollow"} enables you to run Lambda functions on Amazon EC2 instances without managing infrastructure. It supports multi-concurrent invocations, EC2 pricing models, and specialized compute options like Graviton4.
9+
10+
## Key differences from Lambda On Demand
11+
12+
| Aspect | Lambda On Demand | Lambda Managed Instances |
13+
| ---------------- | ------------------------------------------- | ----------------------------------------------- |
14+
| **Concurrency** | Single invocation per execution environment | Multiple concurrent invocations per environment |
15+
| **Python model** | One process, one request | Multiple processes, one request each |
16+
| **Pricing** | Per-request duration | EC2-based with Savings Plans support |
17+
| **Scaling** | Scale on demand with cold starts | Async scaling based on CPU |
18+
| **Isolation** | Firecracker microVMs | Containers on EC2 Nitro |
19+
20+
## How Lambda Python runtime handles concurrency
21+
22+
The **Lambda Python runtime uses multiple processes** for concurrent requests. Each request runs in a separate process, which provides natural isolation between requests.
23+
24+
This means:
25+
26+
- **Each process has its own memory** - global variables are isolated per process
27+
- **`/tmp` directory is shared** across all processes - use caution with file operations
28+
29+
For more details on the isolation model, see [Lambda Managed Instances documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances.html){target="_blank" rel="nofollow"}.
30+
31+
## Powertools integration
32+
33+
Powertools for AWS Lambda (Python) works seamlessly with Lambda Managed Instances. All utilities are compatible with the multi-process concurrency model used by Python.
34+
35+
### Logger, Tracer, and Metrics
36+
37+
Core utilities work without any changes. Each process has its own instances, so correlation IDs and traces are naturally isolated per request.
38+
39+
???+ note "VPC connectivity required"
40+
Lambda Managed Instances run in your VPC. Ensure you have [network connectivity](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances-networking.html){target="_blank" rel="nofollow"} to send logs to CloudWatch, traces to X-Ray, and metrics to CloudWatch.
41+
42+
```python hl_lines="5 6 7 10 11 12 20 25" title="Using Logger, Tracer, and Metrics with Managed Instances"
43+
--8<-- "examples/lambda_features/managed_instances/src/using_tracer.py"
44+
```
45+
46+
### Parameters
47+
48+
The Parameters utility works as expected, but be aware that **caching is per-process**.
49+
50+
```python hl_lines="9" title="Using Parameters with Managed Instances"
51+
--8<-- "examples/lambda_features/managed_instances/src/using_parameters.py"
52+
```
53+
54+
???+ tip "Cache behavior"
55+
Since each process has its own cache, you might see more calls to SSM/Secrets Manager during initial warm-up. Once each process has cached the value, subsequent requests within that process use the cache. You can customize the caching behavior with `max_age` to control the TTL.
56+
57+
### Idempotency
58+
59+
Idempotency works without any changes. It uses DynamoDB for state management, which is external to the process.
60+
61+
```python hl_lines="7 10" title="Using Idempotency with Managed Instances"
62+
--8<-- "examples/lambda_features/managed_instances/src/using_idempotency.py"
63+
```
64+
65+
## VPC connectivity
66+
67+
Lambda Managed Instances require VPC configuration for:
68+
69+
- Sending logs to CloudWatch Logs
70+
- Sending traces to X-Ray
71+
- Accessing AWS services (SSM, Secrets Manager, DynamoDB, etc.)
72+
73+
Configure connectivity using one of these options:
74+
75+
1. **VPC Endpoints** - Private connectivity without internet access
76+
2. **NAT Gateway** - Internet access from private subnets
77+
3. **Public subnet with Internet Gateway** - Direct internet access
78+
4. **Egress-only Internet Gateway** - IPv6 outbound connectivity without inbound access ([learn more](https://docs.aws.amazon.com/vpc/latest/userguide/egress-only-internet-gateway.html){target="_blank" rel="nofollow"})
79+
80+
See [Networking for Lambda Managed Instances](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances-networking.html){target="_blank" rel="nofollow"} for detailed setup instructions.
81+
82+
## FAQ
83+
84+
### Does Powertools for AWS Lambda (Python) work with Lambda Managed Instances?
85+
86+
Yes, all Powertools for AWS Lambda (Python) utilities work seamlessly with Lambda Managed Instances. The multi-process model in Python provides natural isolation between concurrent requests.
87+
88+
### Is my code thread-safe?
89+
90+
Lambda Managed Instances uses **multiple processes**, instead of threads. Each request runs in its own process with isolated memory. If you implement multi-threading within your handler, you are responsible for thread safety.
91+
92+
### Why is my cache not shared between requests?
93+
94+
Each process maintains its own cache (for Parameters, Feature Flags, etc.). This is expected behavior. The cache will warm up independently per process, which may result in slightly more calls to backend services during initial warm-up.
95+
96+
### Can I use global variables?
97+
98+
Yes, but remember they are **per-process**, not shared across concurrent requests. This is actually safer than shared state.
99+
100+
### Do I need to change my existing Powertools for AWS Lambda (Python) code?
101+
102+
No changes are required if you are running Powertools for AWS Lambda (Python) version **3.4.0** or later. Your existing code will work as-is with Lambda Managed Instances.

examples/lambda_features/__init__.py

Whitespace-only changes.

examples/lambda_features/durable_functions/__init__.py

Whitespace-only changes.

examples/lambda_features/durable_functions/src/__init__.py

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from aws_durable_execution_sdk_python import DurableContext, durable_execution # type: ignore[import-not-found]
2+
3+
from aws_lambda_powertools.utilities.idempotency import (
4+
DynamoDBPersistenceLayer,
5+
idempotent,
6+
)
7+
8+
persistence_layer = DynamoDBPersistenceLayer(table_name="IdempotencyTable")
9+
10+
11+
@idempotent(persistence_store=persistence_layer)
12+
@durable_execution
13+
def handler(event: dict, context: DurableContext) -> str:
14+
# Protected against duplicate SQS/Kinesis/DynamoDB triggers
15+
16+
result: str = context.step(
17+
lambda _: "processed",
18+
name="process",
19+
)
20+
21+
return result
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from aws_durable_execution_sdk_python import DurableContext, durable_execution # type: ignore[import-not-found]
2+
3+
from aws_lambda_powertools import Metrics
4+
from aws_lambda_powertools.metrics import MetricUnit
5+
6+
metrics = Metrics()
7+
8+
9+
@metrics.log_metrics
10+
@durable_execution
11+
def handler(event: dict, context: DurableContext) -> str:
12+
result: str = context.step(
13+
lambda _: "processed",
14+
name="process",
15+
)
16+
17+
# Emit metrics in a dedicated step to ensure they are only counted once
18+
context.step(
19+
lambda _: metrics.add_metric(name="WorkflowCompleted", unit=MetricUnit.Count, value=1),
20+
name="emit_completion_metric",
21+
)
22+
23+
return result
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from aws_durable_execution_sdk_python import DurableContext, durable_execution # type: ignore[import-not-found]
2+
3+
from aws_lambda_powertools.utilities.idempotency import (
4+
DynamoDBPersistenceLayer,
5+
idempotent,
6+
)
7+
8+
persistence_layer = DynamoDBPersistenceLayer(table_name="IdempotencyTable")
9+
10+
11+
def process_order(event: dict) -> str:
12+
return f"processed-{event.get('order_id')}"
13+
14+
15+
@idempotent(persistence_store=persistence_layer)
16+
@durable_execution
17+
def handler(event: dict, context: DurableContext) -> str:
18+
# Idempotency protects against duplicate ESM invocations
19+
# Steps within the workflow are already idempotent via checkpoints
20+
21+
result: str = context.step(
22+
lambda _: process_order(event),
23+
name="process_order",
24+
)
25+
26+
return result
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from aws_durable_execution_sdk_python import DurableContext, durable_execution # type: ignore[import-not-found]
2+
3+
from aws_lambda_powertools import Logger
4+
5+
logger = Logger(service="order-processing")
6+
7+
8+
@logger.inject_lambda_context
9+
@durable_execution
10+
def handler(event: dict, context: DurableContext) -> str:
11+
# Set Logger on the context for automatic deduplication
12+
context.set_logger(logger)
13+
14+
# Logs via context.logger appear only once, even during replays
15+
context.logger.info("Starting workflow", extra={"order_id": event.get("order_id")})
16+
17+
result: str = context.step(
18+
lambda _: "processed",
19+
name="process_order",
20+
)
21+
22+
# This log won't repeat when the function replays after completing the step above
23+
context.logger.info("Workflow completed", extra={"result": result})
24+
25+
return result

0 commit comments

Comments
 (0)