Lambda Extension¶
ShutdownExtension ¶
AWS Lambda Extension that executes shutdown handlers on container termination.
This extension registers with the Lambda Runtime Extensions API, waits for SHUTDOWN events, and calls the RestMachine application's shutdown_sync() method.
The extension runs as a separate process from your Lambda handler, monitoring the Lambda lifecycle and ensuring cleanup code runs before the container terminates.
Example
Parameters:
Name | Type | Description | Default |
---|---|---|---|
handler_module
|
str
|
Python module containing the Lambda handler (default: "lambda_function") |
'lambda_function'
|
app_name
|
str
|
Name of the RestApplication variable in the handler module (default: "app") |
'app'
|
Initialize the Lambda Extension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
handler_module
|
str
|
Module name where the RestApplication is defined |
'lambda_function'
|
app_name
|
str
|
Variable name of the RestApplication instance |
'app'
|
Source code in packages/restmachine-aws/src/restmachine_aws/extension.py
Functions¶
register ¶
Register this extension with the Lambda Runtime Extensions API.
Sends a POST request to the Extensions API to register for SHUTDOWN events. The Lambda runtime will send a SHUTDOWN event when the container is about to terminate, allowing cleanup handlers to execute.
Returns:
Type | Description |
---|---|
str
|
Extension ID assigned by the Lambda runtime |
Raises:
Type | Description |
---|---|
RuntimeError
|
If registration fails |
Source code in packages/restmachine-aws/src/restmachine_aws/extension.py
wait_for_event ¶
Wait for the next lifecycle event from Lambda.
This is a blocking call that waits for the Lambda runtime to send the next event. For SHUTDOWN-only extensions, this will block until the container is terminating.
Returns:
Type | Description |
---|---|
dict
|
Event dictionary containing eventType and other event details |
Raises:
Type | Description |
---|---|
RuntimeError
|
If event retrieval fails or extension is not registered |
Source code in packages/restmachine-aws/src/restmachine_aws/extension.py
load_app ¶
Load the RestMachine application from the handler module.
Imports the handler module and retrieves the RestApplication instance. The Lambda task root is added to sys.path to ensure imports work correctly.
Returns:
Type | Description |
---|---|
Any
|
The RestMachine application instance |
Raises:
Type | Description |
---|---|
ImportError
|
If the handler module or app variable cannot be found |
Source code in packages/restmachine-aws/src/restmachine_aws/extension.py
run ¶
Main extension loop.
Performs the following steps: 1. Register with Lambda Extensions API 2. Load the RestMachine application 3. Wait for lifecycle events 4. On SHUTDOWN event, call app.shutdown_sync()
This method blocks until a SHUTDOWN event is received or an error occurs.
Raises:
Type | Description |
---|---|
Exception
|
If any step in the extension lifecycle fails |
Source code in packages/restmachine-aws/src/restmachine_aws/extension.py
Overview¶
The Lambda Extension enables shutdown handlers to run when your Lambda container terminates. This is essential for cleaning up resources like database connections, file handles, and pending operations.
Why Use an Extension?¶
Lambda functions don't have a traditional shutdown phase - they're frozen after execution. The extension solves this by:
- Running as a separate process alongside your Lambda function
- Listening for SHUTDOWN signals from AWS
- Calling your
@app.on_shutdown
handlers before termination
Installation¶
1. Create Extension Script¶
Create extensions/restmachine-shutdown
in your Lambda deployment package:
2. Make It Executable¶
3. Deploy with Lambda¶
The extension is automatically discovered when included in your deployment package:
lambda_function/
├── lambda_function.py # Your Lambda handler
├── extensions/
│ └── restmachine-shutdown # Extension (executable)
└── ...
Using Shutdown Handlers¶
Define shutdown handlers in your application:
from restmachine import RestApplication
from restmachine_aws import AwsApiGatewayAdapter
app = RestApplication()
@app.on_startup
def database():
"""Open database connection on cold start."""
print("Opening database connection...")
return create_db_connection()
@app.on_shutdown
def close_database(database):
"""Close database connection on shutdown."""
print("Closing database connection...")
database.close()
print("Database closed successfully")
# AWS Lambda adapter
adapter = AwsApiGatewayAdapter(app)
def lambda_handler(event, context):
return adapter.handle_event(event, context)
When AWS terminates the Lambda container, the extension calls close_database()
automatically.
How It Works¶
Lifecycle Phases¶
1. Initialization¶
When Lambda starts:
2. Request Processing¶
Your Lambda function handles requests normally. Shutdown handlers are not called between invocations.
3. Shutdown¶
When AWS terminates the container:
# Extension receives SHUTDOWN event
# Extension imports your app and calls app.shutdown_sync()
# Your @app.on_shutdown handlers execute
# Extension reports completion to AWS
Configuration¶
Environment Variables¶
Configure extension behavior:
# SAM template.yaml
Environment:
Variables:
RESTMACHINE_HANDLER_MODULE: "lambda_function" # Module with your app
RESTMACHINE_APP_NAME: "app" # Variable name
Defaults:
- RESTMACHINE_HANDLER_MODULE
: "lambda_function"
- RESTMACHINE_APP_NAME
: "app"
Custom App Location¶
If your app is in a different module:
# my_api/application.py
from restmachine import RestApplication
my_app = RestApplication()
@my_app.get("/hello")
def hello():
return {"message": "Hello"}
Set environment variables:
Environment:
Variables:
RESTMACHINE_HANDLER_MODULE: "my_api.application"
RESTMACHINE_APP_NAME: "my_app"
Resource Injection¶
Shutdown handlers support dependency injection:
@app.on_startup
def database():
return create_db_connection()
@app.on_startup
def cache():
return redis.Redis()
@app.on_shutdown
def cleanup(database, cache):
"""Both dependencies are injected automatically."""
database.close()
cache.close()
Example: Database Connection Pool¶
from restmachine import RestApplication
from restmachine_aws import AwsApiGatewayAdapter
import psycopg2.pool
app = RestApplication()
@app.on_startup
def db_pool():
"""Create connection pool on cold start."""
print("Creating database pool...")
return psycopg2.pool.SimpleConnectionPool(
minconn=1,
maxconn=10,
host="db.example.com",
database="myapp",
user="user",
password="password"
)
@app.get('/users/{user_id}')
def get_user(db_pool, request):
"""Use connection from pool."""
conn = db_pool.getconn()
try:
with conn.cursor() as cur:
cur.execute("SELECT * FROM users WHERE id = %s",
(request.path_params['user_id'],))
user = cur.fetchone()
return {"user": user}
finally:
db_pool.putconn(conn)
@app.on_shutdown
def close_pool(db_pool):
"""Close all connections on shutdown."""
print("Closing database pool...")
db_pool.closeall()
print("All connections closed")
adapter = AwsApiGatewayAdapter(app)
def lambda_handler(event, context):
return adapter.handle_event(event, context)
Monitoring¶
CloudWatch Logs¶
Shutdown handlers write to CloudWatch:
[Extension] Shutdown signal received
[Function] Closing database pool...
[Function] All connections closed
[Extension] Shutdown handlers completed successfully
Testing Locally¶
Test shutdown without deploying:
from restmachine_aws import AwsApiGatewayAdapter
app = RestApplication()
# ... define handlers ...
adapter = AwsApiGatewayAdapter(app)
# Manually trigger shutdown for testing
if hasattr(app, 'shutdown_sync'):
app.shutdown_sync()
Limitations¶
- 5-Second Timeout: Extensions have limited time during shutdown
- No Guarantees: AWS may forcibly terminate if timeout exceeded
- Cold Start Only: Shutdown doesn't run between warm invocations
- No Return Values: Shutdown handlers shouldn't return data
Best Practices¶
- Keep Shutdown Fast - Close connections quickly, avoid complex cleanup
- Log Everything - Use logging to track shutdown execution
- Handle Failures - Use try/except to ensure shutdown completes
- Test Thoroughly - Test shutdown logic locally before deploying
Troubleshooting¶
Shutdown Handlers Not Running¶
- Verify extension is included in deployment package
- Check
extensions/
directory exists in Lambda - Review CloudWatch logs for extension errors
Timeout Errors¶
- Reduce cleanup operations
- Remove blocking I/O from shutdown handlers
Extension Causing Cold Start Delay¶
- Extension adds ~50-100ms to cold start
- This is normal and acceptable for most use cases
CLI Tool¶
Generate extension script automatically:
This creates extensions/restmachine-shutdown
with proper permissions.
See Also¶
- Lambda Deployment Guide - Complete deployment guide
- Lambda Extensions Guide - Detailed usage guide
- Lifecycle Handlers - General lifecycle patterns