-
Notifications
You must be signed in to change notification settings - Fork 113
Improves Developer Ergonomics for LambdaHandler Conformances #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
99bb4e2
f7243ee
ca1a5b3
52f6dd9
6d4eed6
d5a0098
9681ae4
fcd397e
39ea79c
ad0d0cf
fef9be8
cd2c29f
5f33fb3
0fda0bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,60 +33,61 @@ public enum Lambda { | |
/// - handlerType: The Handler to create and invoke. | ||
/// | ||
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. | ||
@discardableResult | ||
internal static func run<Handler: ByteBufferLambdaHandler>( | ||
configuration: LambdaConfiguration = .init(), | ||
handlerType: Handler.Type | ||
) -> Result<Int, Error> { | ||
let _run = { (configuration: LambdaConfiguration) -> Result<Int, Error> in | ||
Backtrace.install() | ||
var logger = Logger(label: "Lambda") | ||
logger.logLevel = configuration.general.logLevel | ||
) throws -> Int { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The changes here is just code cleanup that should make the binary a little smaller. The logic here is exactly the same and removes the implicitly unwrapped optional we had for |
||
var result: Result<Int, Error> = .success(0) | ||
|
||
// start local server for debugging in DEBUG mode only | ||
#if DEBUG | ||
var localServer: LocalLambda.Server? = nil | ||
if Handler.isLocalServer { | ||
localServer = try Lambda.startLocalServer() | ||
} | ||
#endif | ||
|
||
var result: Result<Int, Error>! | ||
MultiThreadedEventLoopGroup.withCurrentThreadAsEventLoop { eventLoop in | ||
let runtime = LambdaRuntime<Handler>(eventLoop: eventLoop, logger: logger, configuration: configuration) | ||
Backtrace.install() | ||
var logger = Logger(label: "Lambda") | ||
logger.logLevel = configuration.general.logLevel | ||
|
||
MultiThreadedEventLoopGroup.withCurrentThreadAsEventLoop { eventLoop in | ||
let runtime = LambdaRuntime<Handler>(eventLoop: eventLoop, logger: logger, configuration: configuration) | ||
#if DEBUG | ||
let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in | ||
logger.info("intercepted signal: \(signal)") | ||
runtime.shutdown() | ||
} | ||
#endif | ||
|
||
runtime.start().flatMap { | ||
runtime.shutdownFuture | ||
}.whenComplete { lifecycleResult in | ||
#if DEBUG | ||
let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in | ||
logger.info("intercepted signal: \(signal)") | ||
runtime.shutdown() | ||
} | ||
signalSource.cancel() | ||
#endif | ||
|
||
runtime.start().flatMap { | ||
runtime.shutdownFuture | ||
}.whenComplete { lifecycleResult in | ||
#if DEBUG | ||
signalSource.cancel() | ||
#endif | ||
eventLoop.shutdownGracefully { error in | ||
if let error = error { | ||
preconditionFailure("Failed to shutdown eventloop: \(error)") | ||
} | ||
eventLoop.shutdownGracefully { error in | ||
if let error = error { | ||
preconditionFailure("Failed to shutdown eventloop: \(error)") | ||
} | ||
result = lifecycleResult | ||
} | ||
result = lifecycleResult | ||
} | ||
|
||
logger.info("shutdown completed") | ||
return result | ||
} | ||
|
||
// start local server for debugging in DEBUG mode only | ||
logger.info("shutdown completed") | ||
|
||
#if DEBUG | ||
if Lambda.env("LOCAL_LAMBDA_SERVER_ENABLED").flatMap(Bool.init) ?? false { | ||
do { | ||
return try Lambda.withLocalServer { | ||
_run(configuration) | ||
} | ||
} catch { | ||
return .failure(error) | ||
} | ||
} else { | ||
return _run(configuration) | ||
} | ||
#else | ||
return _run(configuration) | ||
try localServer?.stop() | ||
#endif | ||
|
||
switch result { | ||
case .success(let count): | ||
return count | ||
case .failure(let error): | ||
throw error | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,42 +17,23 @@ import Logging | |
import NIOCore | ||
|
||
internal struct LambdaConfiguration: CustomStringConvertible { | ||
let general: General | ||
let lifecycle: Lifecycle | ||
let runtimeEngine: RuntimeEngine | ||
|
||
init() { | ||
self.init(general: .init(), lifecycle: .init(), runtimeEngine: .init()) | ||
} | ||
|
||
init(general: General? = nil, lifecycle: Lifecycle? = nil, runtimeEngine: RuntimeEngine? = nil) { | ||
self.general = general ?? General() | ||
self.lifecycle = lifecycle ?? Lifecycle() | ||
self.runtimeEngine = runtimeEngine ?? RuntimeEngine() | ||
} | ||
var general: General = .init() | ||
var lifecycle: Lifecycle = .init() | ||
var runtimeEngine: RuntimeEngine = .init() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably unnecessary if you don't like it but I updated this to be easier to read. You don't necessarily need the init functions you had before if these are just structs. Updating to vars for these internal structs still keeps the same ability to initialize these structs with the default values you had defined in your previous init functions. Let me know if you don't want this! |
||
|
||
struct General: CustomStringConvertible { | ||
let logLevel: Logger.Level | ||
|
||
init(logLevel: Logger.Level? = nil) { | ||
self.logLevel = logLevel ?? Lambda.env("LOG_LEVEL").flatMap(Logger.Level.init) ?? .info | ||
} | ||
var logLevel = Lambda.env("LOG_LEVEL").flatMap(Logger.Level.init) ?? .info | ||
|
||
var description: String { | ||
"\(General.self)(logLevel: \(self.logLevel))" | ||
} | ||
} | ||
|
||
struct Lifecycle: CustomStringConvertible { | ||
let id: String | ||
let maxTimes: Int | ||
let stopSignal: Signal | ||
|
||
init(id: String? = nil, maxTimes: Int? = nil, stopSignal: Signal? = nil) { | ||
self.id = id ?? "\(DispatchTime.now().uptimeNanoseconds)" | ||
self.maxTimes = maxTimes ?? Lambda.env("MAX_REQUESTS").flatMap(Int.init) ?? 0 | ||
self.stopSignal = stopSignal ?? Lambda.env("STOP_SIGNAL").flatMap(Int32.init).flatMap(Signal.init) ?? Signal.TERM | ||
precondition(self.maxTimes >= 0, "maxTimes must be equal or larger than 0") | ||
var id: String = "\(DispatchTime.now().uptimeNanoseconds)" | ||
var stopSignal: Signal = Lambda.env("STOP_SIGNAL").flatMap(Int32.init).flatMap(Signal.init) ?? Signal.TERM | ||
var maxTimes: Int = Lambda.env("MAX_REQUESTS").flatMap(Int.init) ?? 0 { | ||
didSet { precondition(self.maxTimes >= 0, "maxTimes must be equal or larger than 0") } | ||
} | ||
|
||
var description: String { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,13 +14,12 @@ | |
|
||
#if compiler(>=5.6) | ||
@preconcurrency import Dispatch | ||
@preconcurrency import Logging | ||
@preconcurrency import NIOCore | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Xcode 14 complains about these. I don't think they're needed anymore? If this breaks something before Xcode 14 I may have to remove this |
||
#else | ||
import Dispatch | ||
#endif | ||
|
||
import Logging | ||
import NIOCore | ||
#endif | ||
|
||
// MARK: - InitializationContext | ||
|
||
|
@@ -45,14 +44,14 @@ public struct LambdaInitializationContext: _AWSLambdaSendable { | |
|
||
/// ``LambdaTerminator`` to register shutdown operations. | ||
public let terminator: LambdaTerminator | ||
|
||
init(logger: Logger, eventLoop: EventLoop, allocator: ByteBufferAllocator, terminator: LambdaTerminator) { | ||
self.eventLoop = eventLoop | ||
self.logger = logger | ||
self.allocator = allocator | ||
self.terminator = terminator | ||
} | ||
|
||
/// This interface is not part of the public API and must not be used by adopters. This API is not part of semver versioning. | ||
public static func __forTestsOnly( | ||
logger: Logger, | ||
|
@@ -193,24 +192,4 @@ public struct LambdaContext: CustomDebugStringConvertible, _AWSLambdaSendable { | |
public var debugDescription: String { | ||
"\(Self.self)(requestID: \(self.requestID), traceID: \(self.traceID), invokedFunctionARN: \(self.invokedFunctionARN), cognitoIdentity: \(self.cognitoIdentity ?? "nil"), clientContext: \(self.clientContext ?? "nil"), deadline: \(self.deadline))" | ||
} | ||
|
||
/// This interface is not part of the public API and must not be used by adopters. This API is not part of semver versioning. | ||
public static func __forTestsOnly( | ||
requestID: String, | ||
traceID: String, | ||
invokedFunctionARN: String, | ||
timeout: DispatchTimeInterval, | ||
logger: Logger, | ||
eventLoop: EventLoop | ||
) -> LambdaContext { | ||
LambdaContext( | ||
requestID: requestID, | ||
traceID: traceID, | ||
invokedFunctionARN: invokedFunctionARN, | ||
deadline: .now() + timeout, | ||
logger: logger, | ||
eventLoop: eventLoop, | ||
allocator: ByteBufferAllocator() | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes help to solve ambiguities in the swift compiler across default implementations of
EventLoopLambdaHandler
when importingAWSLambdaRuntime
or running tests for the package in Xcode