feat: support Lambda Managed Instances (draft) #1067
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📬 Issue #: #1065
✍️ Description of changes:
Support Lambda Managed Instances / concurrent runtime (refs #1065)
Note
I originally built this to experiment with Lambda Managed Instances in a test workload and thought it might be a useful starting point for upstream support. Parts of the implementation were drafted with the help of an AI assistant and have only been exercised in my own workloads and the existing test suite so far, so please treat this as a starting point for discussion rather than a final design. A super simple demo/benchmark of this implementation is at https://github.com/alessandrobologna/rust-lmi-demo
This draft PR implements a concurrent runtime mode for Lambda Managed Instances, plus the API changes required in
lambda-runtimeandlambda-http. It preserves existing single-invocation behavior for classic Lambda. The implementaion is based on the AWS guidance for building runtimes that support Lambda Managed Instances, as described in Building custom runtimes for Lambda Managed Instances.Architecture (high level)
The runtime chooses between a classic "one invocation at a time" loop and a concurrent mode controlled by
AWS_LAMBDA_MAX_CONCURRENCY:/nextlong-poll loop fetches one event at a time and invokes the handler synchronously, preserving existing behavior.AWS_LAMBDA_MAX_CONCURRENCY >= 2): a window of/nextrequests feeds a queue of invocations; a semaphore bounds the number of in-flight handler tasks, and a shared shutdown signal lets the runtime stop opening new polls and drain in-flight work before exit.sequenceDiagram participant Lambda as Lambda orchestrator participant Runtime as lambda-runtime participant API as Runtime API participant Handler as User handler Lambda->>Runtime: Start process (env AWS_LAMBDA_MAX_CONCURRENCY = N) loop up to N concurrent polls Runtime->>API: GET /runtime/invocation/next API-->>Runtime: Invocation event + context headers Runtime->>Handler: Spawn handler task(event, context) Handler-->>Runtime: Result or error Runtime->>API: POST /response or /error end Lambda-->>Runtime: Shutdown (SIGINT/SIGTERM via extension) Runtime->>Runtime: Stop new polls, wait for in-flight handlers to finishBreaking changes & compatibility
lambda_runtime::run,lambda_http::run, andlambda_http::run_with_streaming_response) keep their original signatures and sequential behavior. Handlers that compiled against the current release should continue to compile unchanged.lambda_runtime::run_concurrentlambda_http::run_concurrentlambda_http::run_with_streaming_response_concurrentThese require handler services to implement
Clone + Send + 'static(with responses/stream bodiesSend/Sync + 'static) so they can be safely cloned and driven by the concurrent runtime.Configgains a new public fieldmax_concurrency: Option<u32>, populated fromAWS_LAMBDA_MAX_CONCURRENCY. This is a semver-visible change because struct literals in downstream code may need updating; I'm happy to follow whatever versioning/visibility strategy maintainers prefer (e.g., major bump vs. constructor helpers).AWS_LAMBDA_MAX_CONCURRENCY > 1), the runtime no longer sets_X_AMZN_TRACE_IDin the process environment. The per-invocation X-Ray trace ID is available viaContext::xray_trace_idand tracing spans instead. Sequential mode behavior is unchanged.AWS_LAMBDA_MAX_CONCURRENCYis set changes from "always sequential per environment" to "per-environment concurrency up to that value". Code that continues to call the existingrunfunctions will remain strictly sequential even if the env var is set.In other words: the earlier versions of this branch tightened the bounds on the existing
runfunctions, but after maintainer feedback those entrypoints are left as-is and concurrency is opt-in via the new*_concurrentAPIs.Below is a concise summary of the changes (unfortunately many) by area.
Runtime & Config (
lambda-runtime)Config.max_concurrency: Option<u32>populated fromAWS_LAMBDA_MAX_CONCURRENCY, plusConfig::is_concurrent()to decide whether concurrent mode should be enabled.Runtime::newnow sizes thelambda_runtime_api_clientHTTP pool frommax_concurrencyso the number of idle connections matches expected concurrency.Runtime::runremains the original sequential/nextloop viarun_with_incoming, preserving existing behavior.Runtime::run_concurrentimplements a windowed/nextloop (FuturesUnordered) plus aSemaphoreto enforce at mostmax_concurrencyactive handler tasks, with graceful shutdown coordinated viaSHUTDOWN_NOTIFY. WhenConfig::is_concurrent()is false, it falls back to the same sequential loop asRuntime::run.api_client,api_response,trace) now implement/deriveCloneso they can be composed into a cloneable service stack for the concurrent entrypoints.Context::newis more robust whenlambda-runtime-client-context/lambda-runtime-cognito-identityheaders are present but empty (treated asNoneinstead of failing JSON parse).HTTP & streaming (
lambda-http,lambda-runtime-api-client)lambda_runtime_api_client::Client:with_pool_size(usize)on the builder and threads apool_size: Option<usize>into the Hyper client to setpool_max_idle_per_host.pool_sizeis not provided.lambda_http::runandrun_with_streaming_responsekeep their existing signatures and sequential behavior, delegating tolambda_runtime::run.lambda_http::run_concurrentandlambda_http::run_with_streaming_response_concurrentwrap the same handler types but require them to beClone + Send + 'static(with response/stream bounds aligned tolambda_runtime::run_concurrent) so they can be driven by the concurrent runtime.Adapter,StreamAdapter) are nowClonewhen the inner service isClone, and the streaming path usesBoxCloneServiceinternally for the concurrent entrypoint so the composed service stack can be cloned.Tooling & examples
Makefileandscripts/test-rie.sh:RIE_MAX_CONCURRENCYand atest-rie-lmitarget that runs RIE withAWS_LAMBDA_MAX_CONCURRENCYset, making it easy to exercise managed-instances behavior locally.examples/basic-lambda/src/main.rs:lambda_runtime::run(func).awaitin anif let Err(err)block to log and propagate runtime errors when testing under RIE.Validation
On
feat/concurrent-lambda-runtime, I ran:cargo +stable fmt --allcargo +stable test --allTEST_ENDPOINTstill fails as expected without a deployed stack.cargo +stable clippy --all-targets --all-features./scripts/test-rie.sh basic-lambda:curl -XPOST http://localhost:9000/2015-03-31/functions/function/invocations -d '{"command":"test from RIE"}'{"req_id": "...", "msg": "Command test from RIE executed."}If maintainers prefer, this could be split into smaller PRs (e.g., builder/Config prep, handler
Clonechanges, and finally the concurrent runtime), but this branch shows the full "end-to-end" implementation so that it can be tested with Lambda Managed Instances.🔏 By submitting this pull request
cargo +nightly fmt.cargo clippy --fix.