⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@sebsto
Copy link
Collaborator

@sebsto sebsto commented Jan 4, 2026

This PR builds on #629 to add convenience structs (Handlers and Adapters) that are Sendable

Changes

  • Added Sendable adapter types: Implemented ClosureHandlerSendable - a thread-safe version of existing closure handler that enforces Sendable conformance for concurrent execution environments - and added conditional conformance to Sendable for other Adapters when the Handler is Sendable

  • Enhanced handler protocols for concurrency: Extended handler protocols to support Sendable constraints and concurrent response writing through LambdaResponseStreamWriter & Sendable, enabling safe multi-threaded invocation processing

  • Created 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_CONCURRENCY is 1 or unset, the runtime maintains the existing single-threaded behaviour for optimal performance on traditional Lambda deployments.

@sebsto sebsto self-assigned this Jan 4, 2026
@sebsto sebsto added the 🆕 semver/minor Adds new public API. label Jan 4, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 LambdaManagedRuntime class with thread-safe handler execution supporting concurrent invocations based on the AWS_LAMBDA_MAX_CONCURRENCY environment variable
  • Created Sendable-conforming adapter types (LambdaHandlerAdapterSendable, LambdaCodableAdapterSendable, LambdaJSONEventDecoderSendable, LambdaJSONOutputEncoderSendable) to ensure safe concurrent execution
  • Refactored LambdaRuntime to extract reusable methods (startRuntimeInterfaceClient, startLocalServer) shared with LambdaManagedRuntime

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
[email protected] 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.

@sebsto sebsto requested review from adam-fowler and removed request for fabianfett January 13, 2026 09:08
@sebsto sebsto marked this pull request as draft January 13, 2026 09:18
@sebsto sebsto removed the request for review from adam-fowler January 13, 2026 09:18
Sebastien Stormacq 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
@sebsto sebsto marked this pull request as ready for review January 13, 2026 17:35
@sebsto sebsto requested a review from fabianfett January 13, 2026 17:35
sebsto and others added 23 commits February 8, 2026 05:53
… 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 <[email protected]>
…bs/swift-aws-lambda-runtime into sebsto/lambda-managed-instances-v2
@sebsto sebsto merged commit 190eb81 into main Feb 11, 2026
44 of 45 checks passed
@sebsto sebsto deleted the sebsto/lambda-managed-instances-v2 branch February 11, 2026 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant