Skip to content

Inline and specialize LambdaHandlers #201

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

Merged
merged 1 commit into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/AWSLambdaRuntime/Lambda+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ internal struct CodableVoidClosureWrapper<In: Decodable>: LambdaHandler {

/// Implementation of a`ByteBuffer` to `In` decoding
extension EventLoopLambdaHandler where In: Decodable {
@inlinable
public func decode(buffer: ByteBuffer) throws -> In {
try self.decoder.decode(In.self, from: buffer)
}
}

/// Implementation of `Out` to `ByteBuffer` encoding
extension EventLoopLambdaHandler where Out: Encodable {
@inlinable
public func encode(allocator: ByteBufferAllocator, value: Out) throws -> ByteBuffer? {
try self.encoder.encode(value, using: allocator)
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/AWSLambdaRuntimeCore/Lambda+String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ internal struct StringVoidClosureWrapper: LambdaHandler {

extension EventLoopLambdaHandler where In == String {
/// Implementation of a `ByteBuffer` to `String` decoding
@inlinable
public func decode(buffer: ByteBuffer) throws -> String {
var buffer = buffer
guard let string = buffer.readString(length: buffer.readableBytes) else {
Expand All @@ -99,6 +100,7 @@ extension EventLoopLambdaHandler where In == String {

extension EventLoopLambdaHandler where Out == String {
/// Implementation of `String` to `ByteBuffer` encoding
@inlinable
public func encode(allocator: ByteBufferAllocator, value: String) throws -> ByteBuffer? {
// FIXME: reusable buffer
var buffer = allocator.buffer(capacity: value.utf8.count)
Expand Down
40 changes: 15 additions & 25 deletions Sources/AWSLambdaRuntimeCore/LambdaHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public protocol LambdaHandler: EventLoopLambdaHandler {
}

extension Lambda {
@usableFromInline
internal static let defaultOffloadQueue = DispatchQueue(label: "LambdaHandler.offload")
}

Expand All @@ -52,6 +53,7 @@ extension LambdaHandler {
/// `LambdaHandler` is offloading the processing to a `DispatchQueue`
/// This is slower but safer, in case the implementation blocks the `EventLoop`
/// Performance sensitive Lambdas should be based on `EventLoopLambdaHandler` which does not offload.
@inlinable
public func handle(context: Lambda.Context, event: In) -> EventLoopFuture<Out> {
let promise = context.eventLoop.makePromise(of: Out.self)
// FIXME: reusable DispatchQueue
Expand Down Expand Up @@ -128,41 +130,28 @@ public protocol EventLoopLambdaHandler: ByteBufferLambdaHandler {

extension EventLoopLambdaHandler {
/// Driver for `ByteBuffer` -> `In` decoding and `Out` -> `ByteBuffer` encoding
@inlinable
public func handle(context: Lambda.Context, event: ByteBuffer) -> EventLoopFuture<ByteBuffer?> {
switch self.decodeIn(buffer: event) {
case .failure(let error):
return context.eventLoop.makeFailedFuture(CodecError.requestDecoding(error))
case .success(let `in`):
return self.handle(context: context, event: `in`).flatMapThrowing { out in
switch self.encodeOut(allocator: context.allocator, value: out) {
case .failure(let error):
throw CodecError.responseEncoding(error)
case .success(let buffer):
return buffer
}
}
}
}

private func decodeIn(buffer: ByteBuffer) -> Result<In, Error> {
let input: In
do {
return .success(try self.decode(buffer: buffer))
input = try self.decode(buffer: event)
} catch {
return .failure(error)
return context.eventLoop.makeFailedFuture(CodecError.requestDecoding(error))
}
}

private func encodeOut(allocator: ByteBufferAllocator, value: Out) -> Result<ByteBuffer?, Error> {
do {
return .success(try self.encode(allocator: allocator, value: value))
} catch {
return .failure(error)
return self.handle(context: context, event: input).flatMapThrowing { output in
do {
return try self.encode(allocator: context.allocator, value: output)
} catch {
throw CodecError.responseEncoding(error)
}
}
}
}

/// Implementation of `ByteBuffer` to `Void` decoding
extension EventLoopLambdaHandler where Out == Void {
@inlinable
public func encode(allocator: ByteBufferAllocator, value: Void) throws -> ByteBuffer? {
nil
}
Expand Down Expand Up @@ -200,7 +189,8 @@ extension ByteBufferLambdaHandler {
}
}

private enum CodecError: Error {
@usableFromInline
enum CodecError: Error {
case requestDecoding(Error)
case responseEncoding(Error)
}