Skip to content

Commit 6488d9a

Browse files
committed
Updated to use a ShutdownContext
1 parent 06e8d7b commit 6488d9a

File tree

5 files changed

+45
-19
lines changed

5 files changed

+45
-19
lines changed

Sources/AWSLambdaRuntimeCore/LambdaContext.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,28 @@ extension Lambda {
9191
}
9292
}
9393
}
94+
95+
// MARK: - ShutdownContext
96+
97+
extension Lambda {
98+
/// Lambda runtime shutdown context.
99+
/// The Lambda runtime generates and passes the `ShutdownContext` to the Lambda handler as an argument.
100+
public final class ShutdownContext {
101+
/// `Logger` to log with
102+
///
103+
/// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable.
104+
public let logger: Logger
105+
106+
/// The `EventLoop` the Lambda is executed on. Use this to schedule work with.
107+
/// This is useful when implementing the `EventLoopLambdaHandler` protocol.
108+
///
109+
/// - note: The `EventLoop` is shared with the Lambda runtime engine and should be handled with extra care.
110+
/// Most importantly the `EventLoop` must never be blocked.
111+
public let eventLoop: EventLoop
112+
113+
internal init(logger: Logger, eventLoop: EventLoop) {
114+
self.eventLoop = eventLoop
115+
self.logger = logger
116+
}
117+
}
118+
}

Sources/AWSLambdaRuntimeCore/LambdaHandler.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,13 @@ public protocol ByteBufferLambdaHandler {
170170
///
171171
/// - Note: In case your Lambda fails while creating your LambdaHandler in the `HandlerFactory`, this method
172172
/// **is not invoked**. In this case you must cleanup the created resources immediately in the `HandlerFactory`.
173-
func syncShutdown() throws
173+
func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture<Void>
174174
}
175175

176176
public extension ByteBufferLambdaHandler {
177-
func syncShutdown() throws {}
177+
func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture<Void> {
178+
context.eventLoop.makeSucceededFuture(Void())
179+
}
178180
}
179181

180182
private enum CodecError: Error {

Sources/AWSLambdaRuntimeCore/LambdaLifecycle.swift

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,19 @@ extension Lambda {
8181
let runner = Runner(eventLoop: self.eventLoop, configuration: self.configuration)
8282

8383
let startupFuture = runner.initialize(logger: logger, factory: self.factory)
84-
startupFuture.flatMap { handler in
84+
startupFuture.flatMap { handler -> EventLoopFuture<(ByteBufferLambdaHandler, Result<Int, Error>)> in
8585
// after the startup future has succeeded, we have a handler that we can use
8686
// to `run` the lambda.
8787
let finishedPromise = self.eventLoop.makePromise(of: Int.self)
8888
self.state = .active(runner, handler)
8989
self.run(promise: finishedPromise)
90-
return finishedPromise.futureResult.always { _ in
91-
// If the lambda is terminated (e.g. LocalServer shutdown), we make sure
92-
// developers have the chance to cleanup their resources.
93-
do {
94-
try handler.syncShutdown()
95-
} catch {
96-
logger.error("Error shutting down handler: \(error)")
97-
}
98-
}
90+
return finishedPromise.futureResult.mapResult { (handler, $0) }
91+
}
92+
.flatMap { (handler, result) -> EventLoopFuture<Int> in
93+
let shutdownContext = ShutdownContext(logger: logger, eventLoop: self.eventLoop)
94+
return handler.shutdown(context: shutdownContext).recover { (error) in
95+
logger.error("Error shutting down handler: \(error)")
96+
}.flatMapResult { (_) in result }
9997
}.always { _ in
10098
// triggered when the Lambda has finished its last run or has a startup failure.
10199
self.markShutdown()

Sources/AWSLambdaRuntimeCore/LambdaRunner.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private extension Lambda.Context {
9595
}
9696

9797
// TODO: move to nio?
98-
private extension EventLoopFuture {
98+
extension EventLoopFuture {
9999
// callback does not have side effects, failing with original result
100100
func peekError(_ callback: @escaping (Error) -> Void) -> EventLoopFuture<Value> {
101101
self.flatMapError { error in

Tests/AWSLambdaRuntimeCoreTests/LambdaLifecycleTest.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,19 @@ class LambdaLifecycleTest: XCTestCase {
4747
func testSyncShutdownIsCalledWhenLambdaShutsdown() {
4848
struct CallbackLambdaHandler: ByteBufferLambdaHandler {
4949
let handler: (Lambda.Context, ByteBuffer) -> (EventLoopFuture<ByteBuffer?>)
50-
let shutdown: () throws -> Void
50+
let shutdown: (Lambda.ShutdownContext) -> EventLoopFuture<Void>
5151

52-
init(_ handler: @escaping (Lambda.Context, ByteBuffer) -> (EventLoopFuture<ByteBuffer?>), shutdown: @escaping () throws -> Void) {
52+
init(_ handler: @escaping (Lambda.Context, ByteBuffer) -> (EventLoopFuture<ByteBuffer?>), shutdown: @escaping (Lambda.ShutdownContext) -> EventLoopFuture<Void>) {
5353
self.handler = handler
5454
self.shutdown = shutdown
5555
}
5656

5757
func handle(context: Lambda.Context, event: ByteBuffer) -> EventLoopFuture<ByteBuffer?> {
5858
self.handler(context, event)
5959
}
60-
61-
func syncShutdown() throws {
62-
try self.shutdown()
60+
61+
func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture<Void> {
62+
self.shutdown(context)
6363
}
6464
}
6565

@@ -70,8 +70,9 @@ class LambdaLifecycleTest: XCTestCase {
7070
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
7171

7272
var count = 0
73-
let handler = CallbackLambdaHandler({ XCTFail("Should not be reached"); return $0.eventLoop.makeSucceededFuture($1) }) {
73+
let handler = CallbackLambdaHandler({ XCTFail("Should not be reached"); return $0.eventLoop.makeSucceededFuture($1) }) { context in
7474
count += 1
75+
return context.eventLoop.makeSucceededFuture(Void())
7576
}
7677

7778
let eventLoop = eventLoopGroup.next()

0 commit comments

Comments
 (0)