Skip to content

Commit b89f220

Browse files
committed
add initialization context
motivation: in more complext initialization scearios you may want access to a logger or other utilities changes: * introduce new InitializationContext type that can be extened in the future without breaking the API in semantic-major way * instead of passing in EventLoop to the handler factory, pass in a context that includes a Logger and and EventLoop * fix a bug where we dont hope back to the event loop when coming back from the handler * adjust tests to the new signature
1 parent c683b41 commit b89f220

File tree

7 files changed

+47
-18
lines changed

7 files changed

+47
-18
lines changed

Sources/AWSLambdaRuntimeCore/Lambda.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public enum Lambda {
2727

2828
/// `ByteBufferLambdaHandler` factory.
2929
///
30-
/// A function that takes a `EventLoop` and returns an `EventLoopFuture` of a `ByteBufferLambdaHandler`
31-
public typealias HandlerFactory = (EventLoop) -> EventLoopFuture<Handler>
30+
/// A function that takes a `InitializationContext` and returns an `EventLoopFuture` of a `ByteBufferLambdaHandler`
31+
public typealias HandlerFactory = (InitializationContext) -> EventLoopFuture<Handler>
3232

3333
/// Run a Lambda defined by implementing the `LambdaHandler` protocol.
3434
///
@@ -58,7 +58,7 @@ public enum Lambda {
5858
/// - factory: A `ByteBufferLambdaHandler` factory.
5959
///
6060
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
61-
public static func run(_ factory: @escaping (EventLoop) throws -> Handler) {
61+
public static func run(_ factory: @escaping (InitializationContext) throws -> Handler) {
6262
self.run(factory: factory)
6363
}
6464

@@ -73,19 +73,19 @@ public enum Lambda {
7373
// for testing and internal use
7474
@discardableResult
7575
internal static func run(configuration: Configuration = .init(), handler: Handler) -> Result<Int, Error> {
76-
self.run(configuration: configuration, factory: { $0.makeSucceededFuture(handler) })
76+
self.run(configuration: configuration, factory: { $0.eventLoop.makeSucceededFuture(handler) })
7777
}
7878

7979
// for testing and internal use
8080
@discardableResult
81-
internal static func run(configuration: Configuration = .init(), factory: @escaping (EventLoop) throws -> Handler) -> Result<Int, Error> {
82-
self.run(configuration: configuration, factory: { eventloop -> EventLoopFuture<Handler> in
83-
let promise = eventloop.makePromise(of: Handler.self)
81+
internal static func run(configuration: Configuration = .init(), factory: @escaping (InitializationContext) throws -> Handler) -> Result<Int, Error> {
82+
self.run(configuration: configuration, factory: { context -> EventLoopFuture<Handler> in
83+
let promise = context.eventLoop.makePromise(of: Handler.self)
8484
// if we have a callback based handler factory, we offload the creation of the handler
8585
// onto the default offload queue, to ensure that the eventloop is never blocked.
8686
Lambda.defaultOffloadQueue.async {
8787
do {
88-
promise.succeed(try factory(eventloop))
88+
promise.succeed(try factory(context))
8989
} catch {
9090
promise.fail(error)
9191
}

Sources/AWSLambdaRuntimeCore/LambdaContext.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@ import Dispatch
1616
import Logging
1717
import NIO
1818

19+
// MARK: - InitializationContext
20+
21+
extension Lambda {
22+
/// Lambda runtime initialization context.
23+
/// The Lambda runtime generates and passes the `InitializationContext` to the Lambda handler as an argument.
24+
public final class InitializationContext {
25+
/// `Logger` to log with
26+
///
27+
/// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable.
28+
public let logger: Logger
29+
30+
/// The `EventLoop` the Lambda is executed on. Use this to schedule work with.
31+
/// This is useful when implementing the `EventLoopLambdaHandler` protocol.
32+
///
33+
/// - note: The `EventLoop` is shared with the Lambda runtime engine and should be handled with extra care.
34+
/// Most importantly the `EventLoop` must never be blocked.
35+
public let eventLoop: EventLoop
36+
37+
internal init(logger: Logger, eventLoop: EventLoop) {
38+
self.eventLoop = eventLoop
39+
self.logger = logger
40+
}
41+
}
42+
}
43+
44+
// MARK: - Context
45+
1946
extension Lambda {
2047
/// Lambda runtime context.
2148
/// The Lambda runtime generates and passes the `Context` to the Lambda handler as an argument.

Sources/AWSLambdaRuntimeCore/LambdaRunner.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ extension Lambda {
3636
logger.debug("initializing lambda")
3737
// 1. create the handler from the factory
3838
// 2. report initialization error if one occured
39-
return factory(self.eventLoop).hop(to: self.eventLoop).peekError { error in
39+
let context = InitializationContext(logger: logger, eventLoop: self.eventLoop)
40+
return factory(context).hop(to: self.eventLoop).peekError { error in
4041
self.runtimeClient.reportInitializationError(logger: logger, error: error).peekError { reportingError in
4142
// We're going to bail out because the init failed, so there's not a lot we can do other than log
4243
// that we couldn't report this error back to the runtime.
@@ -57,6 +58,7 @@ extension Lambda {
5758
let context = Context(logger: logger, eventLoop: self.eventLoop, invocation: invocation)
5859
logger.debug("sending invocation to lambda handler \(handler)")
5960
return handler.handle(context: context, event: event)
61+
.hop(to: self.eventLoop)
6062
.mapResult { result in
6163
if case .failure(let error) = result {
6264
logger.warning("lambda handler returned an error: \(error)")

Tests/AWSLambdaRuntimeCoreTests/Lambda+StringTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class StringLambdaTest: XCTestCase {
185185
typealias In = String
186186
typealias Out = String
187187

188-
init(eventLoop: EventLoop) throws {
188+
init(context: Lambda.InitializationContext) throws {
189189
throw TestError("kaboom")
190190
}
191191

Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class LambdaRuntimeClientTest: XCTestCase {
3030

3131
func testBootstrapFailure() {
3232
let behavior = Behavior()
33-
XCTAssertThrowsError(try runLambda(behavior: behavior, factory: { $0.makeFailedFuture(TestError("boom")) })) { error in
33+
XCTAssertThrowsError(try runLambda(behavior: behavior, factory: { $0.eventLoop.makeFailedFuture(TestError("boom")) })) { error in
3434
XCTAssertEqual(error as? TestError, TestError("boom"))
3535
}
3636
XCTAssertEqual(behavior.state, 1)
@@ -186,7 +186,7 @@ class LambdaRuntimeClientTest: XCTestCase {
186186
.failure(.internalServerError)
187187
}
188188
}
189-
XCTAssertThrowsError(try runLambda(behavior: Behavior(), factory: { $0.makeFailedFuture(TestError("boom")) })) { error in
189+
XCTAssertThrowsError(try runLambda(behavior: Behavior(), factory: { $0.eventLoop.makeFailedFuture(TestError("boom")) })) { error in
190190
XCTAssertEqual(error as? TestError, TestError("boom"))
191191
}
192192
}

Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class LambdaTest: XCTestCase {
5151

5252
var initialized = false
5353

54-
init(eventLoop: EventLoop) {
54+
init(context: Lambda.InitializationContext) {
5555
XCTAssertFalse(self.initialized)
5656
self.initialized = true
5757
}
@@ -72,7 +72,7 @@ class LambdaTest: XCTestCase {
7272
XCTAssertNoThrow(try server.start().wait())
7373
defer { XCTAssertNoThrow(try server.stop().wait()) }
7474

75-
let result = Lambda.run(factory: { $0.makeFailedFuture(TestError("kaboom")) })
75+
let result = Lambda.run(factory: { $0.eventLoop.makeFailedFuture(TestError("kaboom")) })
7676
assertLambdaLifecycleResult(result, shouldFailWithError: TestError("kaboom"))
7777
}
7878

@@ -85,7 +85,7 @@ class LambdaTest: XCTestCase {
8585
typealias In = String
8686
typealias Out = Void
8787

88-
init(eventLoop: EventLoop) throws {
88+
init(context: Lambda.InitializationContext) throws {
8989
throw TestError("kaboom")
9090
}
9191

@@ -124,7 +124,7 @@ class LambdaTest: XCTestCase {
124124
XCTAssertNoThrow(try server.start().wait())
125125
defer { XCTAssertNoThrow(try server.stop().wait()) }
126126

127-
let result = Lambda.run(factory: { $0.makeFailedFuture(TestError("kaboom")) })
127+
let result = Lambda.run(factory: { $0.eventLoop.makeFailedFuture(TestError("kaboom")) })
128128
assertLambdaLifecycleResult(result, shouldFailWithError: TestError("kaboom"))
129129
}
130130

@@ -143,7 +143,7 @@ class LambdaTest: XCTestCase {
143143
usleep(100_000)
144144
kill(getpid(), signal.rawValue)
145145
}
146-
let result = Lambda.run(configuration: configuration, factory: { $0.makeSucceededFuture(EchoHandler()) })
146+
let result = Lambda.run(configuration: configuration, factory: { $0.eventLoop.makeSucceededFuture(EchoHandler()) })
147147

148148
switch result {
149149
case .success(let invocationCount):

Tests/AWSLambdaRuntimeCoreTests/Utils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import NIO
1818
import XCTest
1919

2020
func runLambda(behavior: LambdaServerBehavior, handler: Lambda.Handler) throws {
21-
try runLambda(behavior: behavior, factory: { $0.makeSucceededFuture(handler) })
21+
try runLambda(behavior: behavior, factory: { $0.eventLoop.makeSucceededFuture(handler) })
2222
}
2323

2424
func runLambda(behavior: LambdaServerBehavior, factory: @escaping Lambda.HandlerFactory) throws {

0 commit comments

Comments
 (0)