Skip to content

Commit 8fda4d9

Browse files
committed
LambdaRuntimeClient works with Invocation
WIP: This is a change that I would like to talk about. I don't want to make all the changes necessary for this to correctly work before we haven't talked about if this is something we want to pursue. ### Motivation: - We want to store different entities that are needed when executing a handler within the LambdaContext (Logger, EventLoop, ByteBufferAllocator, …) - Currently the LambdaRuntimeClient creates a LambdaContext. Having the LambdaContext though as a wrapper around EventLoopGroup the API becomes messy. - Conceptionally the Lambda control plane api call is “get next Invocation” (API naming) ### Changes: - LambdaContext has now a reference to the EventLoop (needed for changes down the road) - LambdaContext properties `traceId`, `invokedFunctionArn`, `deadline` are not optional anymore since they will be always set when executing a lambda - LambdaRuntimeClient responds with an Invocation and does not use the LambdaContext at all anymore. - Added an LambdaRuntimeClientError.invocationMissingHeader(String) error ### Open ends: - tests fail, since I made some properties that are always set during the actual Lambda invocation non optional - we will need to build some kind of Deadline into the context - Invocation is currently a struct and I'd assume copied four times with six internal Strings. Maybe we can make this faster by using an internal Storage class. (potentially 4 ref counts vs 24). - I don’t know if Context really is a value type? grpc-swift has the context as class: https://github.com/grpc/grpc-swift/blob/nio/Sources/GRPC/ServerCallContexts/ServerCallContext.swift - What do you think about namespacing LambdaContext within Lambda so that we only have Context.
1 parent 07c61f2 commit 8fda4d9

File tree

3 files changed

+64
-39
lines changed

3 files changed

+64
-39
lines changed

Sources/SwiftAwsLambda/Lambda.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,22 +271,22 @@ public protocol InitializableLambdaHandler {
271271
public struct LambdaContext {
272272
// from aws
273273
public let requestId: String
274-
public let traceId: String?
275-
public let invokedFunctionArn: String?
274+
public let traceId: String
275+
public let invokedFunctionArn: String
276+
public let deadline: String
276277
public let cognitoIdentity: String?
277278
public let clientContext: String?
278-
public let deadline: String?
279279
// utliity
280280
public let eventLoop: EventLoop
281281
public let allocator: ByteBufferAllocator
282282
public let logger: Logger
283283

284284
public init(requestId: String,
285-
traceId: String? = nil,
286-
invokedFunctionArn: String? = nil,
285+
traceId: String,
286+
invokedFunctionArn: String,
287+
deadline: String,
287288
cognitoIdentity: String? = nil,
288289
clientContext: String? = nil,
289-
deadline: String? = nil,
290290
eventLoop: EventLoop,
291291
logger: Logger) {
292292
self.requestId = requestId
@@ -301,9 +301,7 @@ public struct LambdaContext {
301301
// mutate logger with context
302302
var logger = logger
303303
logger[metadataKey: "awsRequestId"] = .string(requestId)
304-
if let traceId = traceId {
305-
logger[metadataKey: "awsTraceId"] = .string(traceId)
306-
}
304+
logger[metadataKey: "awsTraceId"] = .string(traceId)
307305
self.logger = logger
308306
}
309307
}

Sources/SwiftAwsLambda/LambdaRunner.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,18 @@ internal struct LambdaRunner {
5656
// 1. request work from lambda runtime engine
5757
return self.runtimeClient.requestWork(logger: logger).peekError { error in
5858
logger.error("could not fetch work from lambda runtime engine: \(error)")
59-
}.flatMap { context, payload in
59+
}.flatMap { invocation, payload in
6060
// 2. send work to handler
61+
let context = LambdaContext(logger: logger, eventLoop: self.eventLoop, invocation: invocation)
6162
logger.debug("sending work to lambda handler \(self.lambdaHandler)")
6263
return self.lambdaHandler.handle(eventLoop: self.eventLoop,
6364
lifecycleId: self.lifecycleId,
6465
offload: self.offload,
6566
context: context,
66-
payload: payload).mapResult { (context, $0) }
67-
}.flatMap { context, result in
67+
payload: payload).mapResult { (invocation, $0) }
68+
}.flatMap { invocation, result in
6869
// 3. report results to runtime engine
69-
self.runtimeClient.reportResults(logger: logger, context: context, result: result).peekError { error in
70+
self.runtimeClient.reportResults(logger: logger, invocation: invocation, result: result).peekError { error in
7071
logger.error("failed reporting results to lambda runtime engine: \(error)")
7172
}
7273
}.always { result in
@@ -117,6 +118,20 @@ private extension LambdaHandler {
117118
}
118119
}
119120

121+
private extension LambdaContext {
122+
init(logger: Logger, eventLoop: EventLoop, invocation: Invocation) {
123+
124+
self = LambdaContext(requestId: invocation.requestId,
125+
traceId: invocation.traceId,
126+
invokedFunctionArn: invocation.invokedFunctionArn,
127+
deadline: invocation.deadlineDate,
128+
cognitoIdentity: invocation.cognitoIdentity,
129+
clientContext: invocation.clientContext,
130+
eventLoop: eventLoop,
131+
logger: logger)
132+
}
133+
}
134+
120135
// TODO: move to nio?
121136
private extension EventLoopFuture {
122137
// callback does not have side effects, failing with original result

Sources/SwiftAwsLambda/LambdaRuntimeClient.swift

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,18 @@ internal struct LambdaRuntimeClient {
3333
}
3434

3535
/// Requests work from the Runtime Engine.
36-
func requestWork(logger: Logger) -> EventLoopFuture<(LambdaContext, ByteBuffer)> {
36+
func requestWork(logger: Logger) -> EventLoopFuture<(Invocation, ByteBuffer)> {
3737
let url = Consts.invocationURLPrefix + Consts.requestWorkURLSuffix
3838
logger.debug("requesting work from lambda runtime engine using \(url)")
3939
return self.httpClient.get(url: url).flatMapThrowing { response in
4040
guard response.status == .ok else {
4141
throw LambdaRuntimeClientError.badStatusCode(response.status)
4242
}
43+
let invocation = try Invocation(headers: response.headers)
4344
guard let payload = response.body else {
4445
throw LambdaRuntimeClientError.noBody
4546
}
46-
guard let context = LambdaContext(eventLoop: self.eventLoop, logger: logger, response: response) else {
47-
throw LambdaRuntimeClientError.noContext
48-
}
49-
return (context, payload)
47+
return (invocation, payload)
5048
}.flatMapErrorThrowing { error in
5149
switch error {
5250
case HTTPClient.Errors.timeout:
@@ -60,8 +58,8 @@ internal struct LambdaRuntimeClient {
6058
}
6159

6260
/// Reports a result to the Runtime Engine.
63-
func reportResults(logger: Logger, context: LambdaContext, result: Result<ByteBuffer, Error>) -> EventLoopFuture<Void> {
64-
var url = Consts.invocationURLPrefix + "/" + context.requestId
61+
func reportResults(logger: Logger, invocation: Invocation, result: Result<ByteBuffer, Error>) -> EventLoopFuture<Void> {
62+
var url = Consts.invocationURLPrefix + "/" + invocation.requestId
6563
var body: ByteBuffer
6664
switch result {
6765
case .success(let data):
@@ -131,6 +129,7 @@ internal struct LambdaRuntimeClient {
131129
internal enum LambdaRuntimeClientError: Error, Equatable {
132130
case badStatusCode(HTTPResponseStatus)
133131
case upstreamError(String)
132+
case invocationMissingHeader(String)
134133
case noBody
135134
case noContext
136135
case json(JsonCodecError)
@@ -151,6 +150,7 @@ internal struct JsonCodecError: Error, Equatable {
151150
internal struct ErrorResponse: Codable {
152151
var errorType: String
153152
var errorMessage: String
153+
154154
}
155155

156156
private extension ErrorResponse {
@@ -181,26 +181,38 @@ private extension HTTPClient.Response {
181181
}
182182
}
183183

184-
private extension LambdaContext {
185-
init?(eventLoop: EventLoop, logger: Logger, response: HTTPClient.Response) {
186-
guard let requestId = response.headerValue(AmazonHeaders.requestID) else {
187-
return nil
184+
internal struct Invocation {
185+
let requestId : String
186+
let deadlineDate : String
187+
let invokedFunctionArn: String
188+
let traceId : String
189+
let clientContext : String?
190+
let cognitoIdentity : String?
191+
192+
init(headers: HTTPHeaders) throws {
193+
194+
guard let requestId = headers["Lambda-Runtime-Aws-Request-Id"].first else {
195+
throw LambdaRuntimeClientError.invocationMissingHeader("Lambda-Runtime-Aws-Request-Id")
188196
}
189-
if requestId.isEmpty {
190-
return nil
197+
198+
guard let unixTimeMilliseconds = headers["Lambda-Runtime-Deadline-Ms"].first else {
199+
throw LambdaRuntimeClientError.invocationMissingHeader("Lambda-Runtime-Deadline-Ms")
191200
}
192-
let traceId = response.headerValue(AmazonHeaders.traceID)
193-
let invokedFunctionArn = response.headerValue(AmazonHeaders.invokedFunctionARN)
194-
let cognitoIdentity = response.headerValue(AmazonHeaders.cognitoIdentity)
195-
let clientContext = response.headerValue(AmazonHeaders.clientContext)
196-
let deadline = response.headerValue(AmazonHeaders.deadline)
197-
self = LambdaContext(requestId: requestId,
198-
traceId: traceId,
199-
invokedFunctionArn: invokedFunctionArn,
200-
cognitoIdentity: cognitoIdentity,
201-
clientContext: clientContext,
202-
deadline: deadline,
203-
eventLoop: eventLoop,
204-
logger: logger)
201+
202+
guard let invokedFunctionArn = headers["Lambda-Runtime-Invoked-Function-Arn"].first else {
203+
throw LambdaRuntimeClientError.invocationMissingHeader("Lambda-Runtime-Invoked-Function-Arn")
204+
}
205+
206+
guard let traceId = headers["Lambda-Runtime-Trace-Id"].first else {
207+
throw LambdaRuntimeClientError.invocationMissingHeader("Lambda-Runtime-Trace-Id")
208+
}
209+
210+
self.requestId = requestId
211+
self.deadlineDate = unixTimeMilliseconds
212+
self.invokedFunctionArn = invokedFunctionArn
213+
self.traceId = traceId
214+
self.clientContext = headers["Lambda-Runtime-Client-Context"].first
215+
self.cognitoIdentity = headers["Lambda-Runtime-Cognito-Identity"].first
205216
}
217+
206218
}

0 commit comments

Comments
 (0)