Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request adds support for Lambda Managed Instances, enabling Swift Lambda functions to handle multiple concurrent invocations within a single execution environment. The implementation introduces thread-safe runtime components with Sendable conformance while maintaining backward compatibility with the existing single-threaded Lambda runtime.
Key Changes:
- Introduced
LambdaManagedRuntimeclass with thread-safe handler execution supporting concurrent invocations based on theAWS_LAMBDA_MAX_CONCURRENCYenvironment variable - Created Sendable-conforming adapter types (
LambdaHandlerAdapterSendable,LambdaCodableAdapterSendable,LambdaJSONEventDecoderSendable,LambdaJSONOutputEncoderSendable) to ensure safe concurrent execution - Refactored
LambdaRuntimeto extract reusable methods (startRuntimeInterfaceClient,startLocalServer) shared withLambdaManagedRuntime
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
scripts/extract_aws_credentials.sh |
Removed script (likely no longer needed) |
Tests/AWSLambdaRuntimeTests/Utils.swift |
Added test helper for creating LambdaContext instances |
Tests/AWSLambdaRuntimeTests/LambdaManagedRuntimeTests.swift |
Comprehensive tests for concurrent handler execution, Sendable constraints, and thread-safe adapters |
Sources/AWSLambdaRuntime/Runtime/LambdaRuntime.swift |
Refactored to extract reusable methods and renamed atomic guard variable for clarity |
Sources/AWSLambdaRuntime/Runtime/LambdaRuntime+Codable.swift |
Added Sendable conformance to VoidEncoder |
Sources/AWSLambdaRuntime/ManagedRuntime/LambdaManagedRuntimeHandlers.swift |
Introduced ClosureHandlerSendable for thread-safe closure-based handlers |
Sources/AWSLambdaRuntime/ManagedRuntime/LambdaManagedRuntime.swift |
Core implementation of managed runtime with concurrency detection and multiple RIC support |
Sources/AWSLambdaRuntime/ManagedRuntime/LambdaManagedRuntime+ServiceLifecycle.swift |
ServiceLifecycle integration for managed runtime |
Sources/AWSLambdaRuntime/ManagedRuntime/LambdaManagedRuntime+Codable.swift |
Thread-safe adapter implementations for managed runtime |
Sources/AWSLambdaRuntime/FoundationSupport/LambdaRuntime+JSON.swift |
Convenience initializers for LambdaRuntime with JSON encoding/decoding |
Sources/AWSLambdaRuntime/FoundationSupport/LambdaManagedRuntime+JSON.swift |
Sendable JSON encoder/decoder implementations and convenience initializers for managed runtime |
Sources/AWSLambdaRuntime/FoundationSupport/LambdaHandler+JSON.swift |
Extracted common JSON handler support to separate file |
Package@swift-6.0.swift |
Added ManagedRuntimeSupport trait and updated dependency versions |
Package.swift |
Added ManagedRuntimeSupport trait to default enabled traits and updated dependency versions |
Examples/Streaming+Codable/Tests/LambdaStreamingCodableTests.swift |
Updated logger label for consistency |
Examples/ManagedInstances/template.yaml |
SAM template for deploying managed instances examples |
Examples/ManagedInstances/Sources/Streaming/main.swift |
Streaming response example for managed instances |
Examples/ManagedInstances/Sources/HelloJSON/main.swift |
Simple JSON request/response example for managed instances |
Examples/ManagedInstances/Sources/BackgroundTasks/main.swift |
Background processing example for managed instances |
Examples/ManagedInstances/README.md |
Documentation for deploying and testing managed instances examples |
Examples/ManagedInstances/Package.swift |
Package definition for managed instances examples |
Examples/ManagedInstances/.gitignore |
Ignore patterns for managed instances examples |
.github/workflows/pull_request.yml |
Added ManagedInstances to CI examples list |
Comments suppressed due to low confidence (3)
Sources/AWSLambdaRuntime/FoundationSupport/LambdaManagedRuntime+JSON.swift:149
- Inconsistent default logger label. This initializer uses "LambdaRuntime" as the default logger label, but the previous initializer on line 122 uses "LambdaManagedRuntime". For consistency, this should likely also use "LambdaManagedRuntime" since it's an extension on LambdaManagedRuntime.
Sources/AWSLambdaRuntime/FoundationSupport/LambdaManagedRuntime+JSON.swift:207 - Inconsistent default logger label. This initializer uses "LambdaRuntime" as the default logger label, but line 122 uses "LambdaManagedRuntime". For consistency across the LambdaManagedRuntime extension, this should likely also use "LambdaManagedRuntime".
Sources/AWSLambdaRuntime/FoundationSupport/LambdaManagedRuntime+JSON.swift:176 - Inconsistent default logger label. This initializer uses "LambdaRuntime" as the default logger label, but line 122 uses "LambdaManagedRuntime". For consistency across the LambdaManagedRuntime extension, this should likely also use "LambdaManagedRuntime".
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Sources/AWSLambdaRuntime/ManagedRuntime/LambdaManagedRuntime.swift
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
added 6 commits
January 13, 2026 11:16
- Remove deleted Lambda+JSON.swift file - Split JSON support into separate files: - LambdaRuntime+JSON.swift for regular runtime - LambdaManagedRuntime+JSON.swift for managed runtime - Clean up ManagedInstances example directory
… error (Bug #635) (#636) On fast machines, the local Lambda server crashes with: ``` Fatal error: Deinited NIOAsyncWriter without calling finish() ``` This occurs in `NIOAsyncChannelHandler.channelActive()` when child connection channels are created. ## Root Cause This is a known issue with NIO's async server channel API (see [swift-nio#2637](apple/swift-nio#2637)). **The fundamental problem:** 1. The async `bind()` API creates `NIOAsyncChannel` instances for incoming connections 2. These channels are yielded through an async stream to the server loop 3. When the serving task is cancelled (or completes), the async stream iteration stops 4. Any channels that were accepted but not yet read from the stream are dropped 5. These unread channels never have `executeThenClose()` called on them 6. Their `NIOAsyncWriter` is deallocated without `finish()` being called → fatal error **Why graceful shutdown doesn't help:** Even closing the server channel gracefully doesn't eliminate the race - there's a timing window where: - A connection is accepted and queued in the async stream - The server task is cancelled or completes - The queued channel is never read and gets dropped IMHO, this is an inherent limitation of the `async bind()` API when combined with task cancellation. ## Solution I stopped using the `async bind()` API entirely. Instead, I use the traditional callback-based `childChannelInitializer`: 1. Create `NIOAsyncChannel` directly in `childChannelInitializer` (synchronous context) 2. Immediately spawn a `Task.detached` to handle the connection 3. Each connection is handled independently, not through a cancellable async stream 4. Detached tasks are not affected by task group cancellation 5. Every channel has `executeThenClose()` called immediately, preventing the writer from being dropped This approach avoids the async stream entirely, eliminating the race condition. ## Changes - Replaced `async bind()` with traditional `childChannelInitializer` - Each connection spawns a `Task.detached` that immediately calls `executeThenClose()` - Removed the connection iteration loop (no longer needed) - Server task now simply waits for the channel to close - Simplified shutdown logic since there's no async stream to drain ## Trade-offs - Uses `Task.detached` (unstructured concurrency) to bridge NIO's event-loop world with Swift concurrency - This is necessary until NIO provides a new bootstrap API that properly handles cancellation - Each connection is handled independently rather than through structured concurrency ## Testing Tested on fast machines where the race condition was reliably reproducible. The crash no longer occurs. ## References - [swift-nio#2637](apple/swift-nio#2637) - Known issue with async server channels and cancellation - [Comment from NIO maintainer](apple/swift-nio#2637 (comment)) - Recommends avoiding cancellation or using callback-based API Fixes #635 --------- Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
….com/awslabs/swift-aws-lambda-runtime into sebsto/lambda-managed-instances-core
…bs/swift-aws-lambda-runtime into sebsto/lambda-managed-instances-v2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
This PR builds on #629 to add convenience structs (Handlers and Adapters) that are
SendableChanges
Added Sendable adapter types: Implemented
ClosureHandlerSendable- a thread-safe version of existing closure handler that enforcesSendableconformance for concurrent execution environments - and added conditional conformance toSendablefor other Adapters when the Handler isSendableEnhanced handler protocols for concurrency: Extended handler protocols to support
Sendableconstraints and concurrent response writing throughLambdaResponseStreamWriter & Sendable, enabling safe multi-threaded invocation processingCreated comprehensive Lambda Managed Instances examples: Built three demonstration functions showcasing concurrent execution capabilities, streaming responses, and background processing patterns specific to the new managed instances deployment model
Context
Lambda Managed Instances support multi-concurrent invocations where multiple invocations execute simultaneously within the same execution environment. The runtime now detects the configured concurrency level and launches the appropriate number of RICs to handle concurrent requests efficiently.
When
AWS_LAMBDA_MAX_CONCURRENCYis 1 or unset, the runtime maintains the existing single-threaded behaviour for optimal performance on traditional Lambda deployments.