From 23c7471c342a13b4a9423a3a4a0b5d6ce413bd5c Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Wed, 15 Apr 2020 21:31:27 +0200 Subject: [PATCH 1/3] Public Lifecycle --- Sources/AWSLambdaRuntime/LambdaLifecycle.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/AWSLambdaRuntime/LambdaLifecycle.swift b/Sources/AWSLambdaRuntime/LambdaLifecycle.swift index b96063d6..1f3e2460 100644 --- a/Sources/AWSLambdaRuntime/LambdaLifecycle.swift +++ b/Sources/AWSLambdaRuntime/LambdaLifecycle.swift @@ -17,7 +17,7 @@ import NIO import NIOConcurrencyHelpers extension Lambda { - internal final class Lifecycle { + public final class Lifecycle { private let eventLoop: EventLoop private let logger: Logger private let configuration: Configuration @@ -26,6 +26,10 @@ extension Lambda { private var _state = State.idle private let stateLock = Lock() + public convenience init(eventLoop: EventLoop, logger: Logger, factory: @escaping LambdaHandlerFactory) { + self.init(eventLoop: eventLoop, logger: logger, configuration: .init(), factory: factory) + } + init(eventLoop: EventLoop, logger: Logger, configuration: Configuration, factory: @escaping LambdaHandlerFactory) { self.eventLoop = eventLoop self.logger = logger @@ -53,7 +57,7 @@ extension Lambda { } } - func start() -> EventLoopFuture { + public func start() -> EventLoopFuture { logger.info("lambda lifecycle starting with \(self.configuration)") self.state = .initializing var logger = self.logger @@ -65,12 +69,12 @@ extension Lambda { } } - func stop() { + public func stop() { self.logger.debug("lambda lifecycle stopping") self.state = .stopping } - func shutdown() { + public func shutdown() { self.logger.debug("lambda lifecycle shutdown") self.state = .shutdown } From 19ea3412d4ed2cfa899ff6e7a071868b7653252e Mon Sep 17 00:00:00 2001 From: tom doron Date: Wed, 22 Apr 2020 13:05:43 -0700 Subject: [PATCH 2/3] make lifecycle safer to user as a public api --- Sources/AWSLambdaRuntime/Lambda.swift | 9 +++-- .../AWSLambdaRuntime/LambdaLifecycle.swift | 39 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Sources/AWSLambdaRuntime/Lambda.swift b/Sources/AWSLambdaRuntime/Lambda.swift index 524e6c76..b46674d6 100644 --- a/Sources/AWSLambdaRuntime/Lambda.swift +++ b/Sources/AWSLambdaRuntime/Lambda.swift @@ -83,11 +83,12 @@ public enum Lambda { let lifecycle = Lifecycle(eventLoop: eventLoopGroup.next(), logger: logger, configuration: configuration, factory: factory) let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in logger.info("intercepted signal: \(signal)") - lifecycle.stop() - } - return lifecycle.start().always { _ in lifecycle.shutdown() - signalSource.cancel() + } + return lifecycle.start().flatMap { + return lifecycle.shutdownFuture.always { _ in + signalSource.cancel() + } } } } diff --git a/Sources/AWSLambdaRuntime/LambdaLifecycle.swift b/Sources/AWSLambdaRuntime/LambdaLifecycle.swift index 1f3e2460..3321c724 100644 --- a/Sources/AWSLambdaRuntime/LambdaLifecycle.swift +++ b/Sources/AWSLambdaRuntime/LambdaLifecycle.swift @@ -19,6 +19,7 @@ import NIOConcurrencyHelpers extension Lambda { public final class Lifecycle { private let eventLoop: EventLoop + private let shutdownPromise: EventLoopPromise private let logger: Logger private let configuration: Configuration private let factory: LambdaHandlerFactory @@ -32,6 +33,7 @@ extension Lambda { init(eventLoop: EventLoop, logger: Logger, configuration: Configuration, factory: @escaping LambdaHandlerFactory) { self.eventLoop = eventLoop + self.shutdownPromise = eventLoop.makePromise(of: Int.self) self.logger = logger self.configuration = configuration self.factory = factory @@ -51,38 +53,43 @@ extension Lambda { } set { self.stateLock.withLockVoid { - precondition(newValue.order > _state.order, "invalid state \(newValue) after \(self._state)") + precondition(newValue.order > self._state.order, "invalid state \(newValue) after \(self._state)") self._state = newValue } + self.logger.debug("lambda lifecycle state: \(newValue)") } } - public func start() -> EventLoopFuture { + public var shutdownFuture: EventLoopFuture { + self.shutdownPromise.futureResult + } + + public func start() -> EventLoopFuture { logger.info("lambda lifecycle starting with \(self.configuration)") self.state = .initializing + let promise = self.eventLoop.makePromise(of: Int.self) + promise.futureResult.always { _ in + self.markShutdown() + }.cascade(to: self.shutdownPromise) var logger = self.logger logger[metadataKey: "lifecycleId"] = .string(self.configuration.lifecycle.id) let runner = Runner(eventLoop: self.eventLoop, configuration: self.configuration) - return runner.initialize(logger: logger, factory: self.factory).flatMap { handler in + return runner.initialize(logger: logger, factory: self.factory).map { handler in self.state = .active(runner, handler) - return self.run() + self.run(promise: promise) } } - public func stop() { - self.logger.debug("lambda lifecycle stopping") - self.state = .stopping + public func shutdown() { + self.state = .shuttingdown } - public func shutdown() { - self.logger.debug("lambda lifecycle shutdown") + private func markShutdown() { self.state = .shutdown } @inline(__always) - private func run() -> EventLoopFuture { - let promise = self.eventLoop.makePromise(of: Int.self) - + private func run(promise: EventLoopPromise) { func _run(_ count: Int) { switch self.state { case .active(let runner, let handler): @@ -100,7 +107,7 @@ extension Lambda { promise.fail(error) } } - case .stopping, .shutdown: + case .shuttingdown: promise.succeed(count) default: preconditionFailure("invalid run state: \(self.state)") @@ -108,15 +115,13 @@ extension Lambda { } _run(0) - - return promise.futureResult } private enum State { case idle case initializing case active(Runner, ByteBufferLambdaHandler) - case stopping + case shuttingdown case shutdown internal var order: Int { @@ -127,7 +132,7 @@ extension Lambda { return 1 case .active: return 2 - case .stopping: + case .shuttingdown: return 3 case .shutdown: return 4 From 0cff3cbc58d679789f7480667d934d8d14c8ea50 Mon Sep 17 00:00:00 2001 From: tom doron Date: Wed, 22 Apr 2020 15:40:15 -0700 Subject: [PATCH 3/3] api docs --- .../AWSLambdaRuntime/LambdaLifecycle.swift | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/Sources/AWSLambdaRuntime/LambdaLifecycle.swift b/Sources/AWSLambdaRuntime/LambdaLifecycle.swift index 3321c724..8ed62d9e 100644 --- a/Sources/AWSLambdaRuntime/LambdaLifecycle.swift +++ b/Sources/AWSLambdaRuntime/LambdaLifecycle.swift @@ -17,6 +17,7 @@ import NIO import NIOConcurrencyHelpers extension Lambda { + /// `Lifecycle` manages the Lambda process lifecycle. public final class Lifecycle { private let eventLoop: EventLoop private let shutdownPromise: EventLoopPromise @@ -27,6 +28,12 @@ extension Lambda { private var _state = State.idle private let stateLock = Lock() + /// Create a new `Lifecycle`. + /// + /// - parameters: + /// - eventLoop: An `EventLoop` to run the Lambda on. + /// - logger: A `Logger` to log the Lambda events. + /// - factory: A `LambdaHandlerFactory` to create the concrete Lambda handler. public convenience init(eventLoop: EventLoop, logger: Logger, factory: @escaping LambdaHandlerFactory) { self.init(eventLoop: eventLoop, logger: logger, configuration: .init(), factory: factory) } @@ -45,30 +52,22 @@ extension Lambda { } } - private var state: State { - get { - self.stateLock.withLock { - self._state - } - } - set { - self.stateLock.withLockVoid { - precondition(newValue.order > self._state.order, "invalid state \(newValue) after \(self._state)") - self._state = newValue - } - self.logger.debug("lambda lifecycle state: \(newValue)") - } - } - + /// The `Lifecycle` shutdown future. + /// + /// - Returns: An `EventLoopFuture` that is fulfilled after the Lambda lifecycle has fully shutdown. public var shutdownFuture: EventLoopFuture { self.shutdownPromise.futureResult } + /// Start the `Lifecycle`. + /// + /// - Returns: An `EventLoopFuture` that is fulfilled after the Lambda hander has been created and initiliazed, and a first run has been schduled. public func start() -> EventLoopFuture { logger.info("lambda lifecycle starting with \(self.configuration)") self.state = .initializing - let promise = self.eventLoop.makePromise(of: Int.self) - promise.futureResult.always { _ in + // triggered when the Lambda has finished its last run + let finishedPromise = self.eventLoop.makePromise(of: Int.self) + finishedPromise.futureResult.always { _ in self.markShutdown() }.cascade(to: self.shutdownPromise) var logger = self.logger @@ -76,10 +75,13 @@ extension Lambda { let runner = Runner(eventLoop: self.eventLoop, configuration: self.configuration) return runner.initialize(logger: logger, factory: self.factory).map { handler in self.state = .active(runner, handler) - self.run(promise: promise) + self.run(promise: finishedPromise) } } + // MARK: - Private + + /// Begin the `Lifecycle` shutdown. public func shutdown() { self.state = .shuttingdown } @@ -117,6 +119,21 @@ extension Lambda { _run(0) } + private var state: State { + get { + self.stateLock.withLock { + self._state + } + } + set { + self.stateLock.withLockVoid { + precondition(newValue.order > self._state.order, "invalid state \(newValue) after \(self._state)") + self._state = newValue + } + self.logger.debug("lambda lifecycle state: \(newValue)") + } + } + private enum State { case idle case initializing