-
Notifications
You must be signed in to change notification settings - Fork 113
Allow integration with API Frameworks: Public Lifecycle #58
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
Conversation
a3554bd
to
23c7471
Compare
hey @fabianfett can you elaborate how making the lifecycle public helps? or iow, how you plan to use the lifecycle in order to integrate higher level frameworks like vapor, smoke, etc |
@tomerd sure. This is how I would use the public class LambdaServer: Server {
struct Handler: EventLoopLambdaHandler {
typealias In = APIGateway.Request
typealias Out = APIGateway.Response
private let application: Application
private let responder: Responder
init(application: Application, responder: Responder) {
self.application = application
self.responder = responder
}
public func handle(context: Lambda.Context, payload: APIGateway.Request)
-> EventLoopFuture<APIGateway.Response>
{
context.logger.info("API.GatewayRequest: \(payload)")
let vaporRequest: Vapor.Request
do {
vaporRequest = try Vapor.Request(req: payload, in: context, for: self.application)
}
catch {
return context.eventLoop.makeFailedFuture(error)
}
return self.responder.respond(to: vaporRequest)
.map { APIGateway.Response(response: $0) }
}
}
private let application : Application
private let responder : Responder
// private let configuration : Configuration
private let eventLoopGroup : EventLoopGroup
private var lambdaLifecycle : Lambda.Lifecycle?
private var onShutdownFuture: EventLoopFuture<Void>?
init(application : Application,
responder : Responder,
// configuration : Configuration,
on eventLoopGroup: EventLoopGroup)
{
self.application = application
self.responder = responder
self.eventLoopGroup = eventLoopGroup
}
public func start(hostname: String?, port: Int?) throws {
let handler = Handler(application: self.application, responder: self.responder)
self.lambdaLifecycle = Lambda.Lifecycle(
eventLoop: eventLoopGroup.next(),
logger: self.application.logger) {
$0.makeSucceededFuture(handler)
}
self.onShutdownFuture = self.lambdaLifecycle?.start().map() { _ in Void() }
}
public var onShutdown: EventLoopFuture<Void> {
return self.onShutdownFuture!
}
public func shutdown() {
// ignore
// should never be executed
self.lambdaLifecycle?.shutdown()
self.lambdaLifecycle = nil
}
} Thanks to some syntactic sugar, developers will be able to deploy their code to lambda by just adding two lines of code: import VaporLambdaRuntime
app.servers.use(.lambda) |
hey @fabianfett this is great imo, love the simple and elegant integration. that said, I am a bit concerned about exposing |
/cc @tachyonics to see if we can achieve smoke integration in a similar fashion |
I'm not clear what this would give us over the The current integration I have uses an initialiser protocol very similar to this- With the application started with Lambda.runAsOperationServer(MyApplicationInitializer.init) This is different but is consistent with how SmokeFramework starts up elsewhere (SmokeFramework 2.x has moved to something very like the Lambda startup which is an evolution on how SmokeFramework 1.x started up). |
f67c43d
to
7d1da8c
Compare
7d1da8c
to
19ea341
Compare
hey @fabianfett as discussed, added a commit to make Lifecycle safe as a public API, lmk what you think |
@tachyonics sounds good, was raising awareness in case the helped with smoke integration but looks like you already have that figured out so awesome! |
self.logger.debug("lambda lifecycle stopping") | ||
self.state = .stopping | ||
public func shutdown() { | ||
self.state = .shuttingdown |
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.
I think we should wrap this in an
eventLoop.submit {
self.state = .shuttingdown
}
to ensure needed thread safety.
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.
state is protected with lock, right?
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.
You‘re right. Sorry.
@tomerd I‘m happy with this. Can be merged! |
@tachyonics Since you are planning something like this for smoke as well. Would this fit your needs?
Motivation
A common pattern in Lambda land is to allow apps written with common api frameworks to be run within Lambda behind an APIGateway or Application Load Balancer. An example for this is: awslabs/aws-lambda-go-api-proxy, which is were I used this pattern for myself for the very first time.
Since SiwftNIO doesn't have such a widespread middleware api for handling http requests as go does, there is no way for us to just allow extension of the lambda by merely presenting one middleware hook for developers.
But that doesn't mean we should not allow such a use-case. We need to find a way though that is easy to use for experienced framework developers, but not in the reach of early adopters. After trying out a number of things especially around injecting ELGs into
Lambda.run()
, I came to the conclusion, that the potentially best way is to open up Lambda.Lifecycle.Changes
Lambda.Lifecycle
is now publicstart
,stop
andshutdown
methods are now publicResult
On a private remote I can prepare my vapor shim to be used with our new lambda runtime.