Skip to content

Commit 250f94b

Browse files
committed
Fix headings and run formatter
1 parent c19ae23 commit 250f94b

File tree

1 file changed

+30
-30
lines changed
  • Sources/AWSLambdaRuntimeCore/Documentation.docc/Proposals

1 file changed

+30
-30
lines changed

Sources/AWSLambdaRuntimeCore/Documentation.docc/Proposals/0001-v2-api.md

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# v2 API proposal for swift-aws-lambda-runtime
22

3-
`swift-aws-lambda-runtime` is an important library for the Swift on Server ecosystem. The initial API was written before async/await was introduced to Swift. When async/await was introduced, shims were added to bridge between the underlying SwiftNIO `EventLoop` interfaces and async/await. However, just like ``gRPC-swift`` and `postgres-nio`, we now want to shift to solely using async/await instead of `EventLoop` interfaces. For this, large parts of the current API have to be reconsidered.
3+
`swift-aws-lambda-runtime` is an important library for the Swift on Server ecosystem. The initial API was written before async/await was introduced to Swift. When async/await was introduced, shims were added to bridge between the underlying SwiftNIO `EventLoop` interfaces and async/await. However, just like `gRPC-swift` and `postgres-nio`, we now want to shift to solely using async/await instead of `EventLoop` interfaces. For this, large parts of the current API have to be reconsidered.
44

55
## Motivation
66

@@ -18,13 +18,13 @@ A Lambda function can currently be implemented through conformance to the variou
1818

1919
The `SimpleLambdaHandler` protocol provides a quick and easy way to implement a basic Lambda function. It only requires an implementation of the `handle` function where the business logic of the Lambda function can be written. `SimpleLambdaHandler` is perfectly sufficient for small use-cases as the user does not need to spend much time looking into the library.
2020

21-
However, `SimpleLambdaHandler` cannot be used when services such as a database client need to be initalized before the Lambda runtime starts and then also gracefully shutdown prior to the runtime terminating. This is because the only way to register termination logic is through the `LambdaInitializationContext` (containing a field `terminator: LambdaTerminator`) which is created and used *internally* within `LambdaRuntime` and never exposed through `SimpleLambdaHandler`. For such use-cases, other handler protocols like `LambdaHandler` must be used. `LambdaHandler` exposes a `context` argument of type `LambdaInitializationContext` through its initializer. Within the initializer, required services can be initalized and their graceful shutdown logic can be registered with the `context.terminator.register` function.
21+
However, `SimpleLambdaHandler` cannot be used when services such as a database client need to be initalized before the Lambda runtime starts and then also gracefully shutdown prior to the runtime terminating. This is because the only way to register termination logic is through the `LambdaInitializationContext` (containing a field `terminator: LambdaTerminator`) which is created and used _internally_ within `LambdaRuntime` and never exposed through `SimpleLambdaHandler`. For such use-cases, other handler protocols like `LambdaHandler` must be used. `LambdaHandler` exposes a `context` argument of type `LambdaInitializationContext` through its initializer. Within the initializer, required services can be initalized and their graceful shutdown logic can be registered with the `context.terminator.register` function.
2222

2323
Yet, `LambdaHandler` is quite cumbersome to use in such use-cases as users have to deviate from the established norms of the Swift on Server ecosystem in order to cleanly manage the lifecycle of the services intended to be used. This is because the convenient `swift-service-lifecycle` v2 library — which is commonly used for cleanly managing the lifecycles of required services and widely supported by many libraries — cannot be used in a structured concurrency manner.
2424

2525
#### Does not integrate well with swift-service-lifecycle in a structured concurrency manner
2626

27-
The Lambda runtime can only be started using the **internal** `Lambda.run()` function. This function is called by the `main()` function defined by the `LambdaHandler` protocol, preventing users from injecting initialized services into the runtime *prior* to it starting. As shown below, this forces users to use an **unstructured concurrency** approach and manually initialize services, leading to the issue of the user then perhaps forgetting to gracefully shutdown the initalized services:
27+
The Lambda runtime can only be started using the **internal** `Lambda.run()` function. This function is called by the `main()` function defined by the `LambdaHandler` protocol, preventing users from injecting initialized services into the runtime _prior_ to it starting. As shown below, this forces users to use an **unstructured concurrency** approach and manually initialize services, leading to the issue of the user then perhaps forgetting to gracefully shutdown the initalized services:
2828

2929
```swift
3030
struct MyLambda: LambdaHandler {
@@ -63,7 +63,7 @@ struct MyLambda: LambdaHandler {
6363

6464
#### Verbose Codable support
6565

66-
In the current API, there are extensions and Codable wrapper classes for decoding events and encoding computed responses for *each* different handler protocol and for both `String` and `JSON` formats. This has resulted in a lot of boilerplate code which can very easily be made generic and simplified in v2.
66+
In the current API, there are extensions and Codable wrapper classes for decoding events and encoding computed responses for _each_ different handler protocol and for both `String` and `JSON` formats. This has resulted in a lot of boilerplate code which can very easily be made generic and simplified in v2.
6767

6868
### New features
6969

@@ -77,18 +77,18 @@ In May [AWS described in a blog post that you can run background tasks in Lambda
7777

7878
## Proposed Solution
7979

80-
#### async/await-first API
80+
### async/await-first API
8181

8282
Large parts of `Lambda`, `LambdaHandler`, and `LambdaRuntime` will be re-written to use async/await constructs in place of the `EventLoop` family of interfaces.
8383

84-
#### Providing ownership of main() and support for swift-service-lifecycle
84+
### Providing ownership of main() and support for swift-service-lifecycle
8585

86-
* Instead of conforming to a handler protocol, users can now create a `LambdaRuntime` by passing in a handler closure.
87-
* `LambdaRuntime` conforms to `ServiceLifecycle.Service` by implementing a `run()` method that contains initialization and graceful shutdown logic.
88-
* This allows the lifecycle of the `LambdaRuntime` to be managed with `swift-service-lifecycle` *alongside* and in the same way the lifecycles of the required services are managed, e.g. `try await ServiceGroup(services: [postgresClient, ..., lambdaRuntime], ...).run()`.
89-
* Dependencies can now be injected into `LambdaRuntime``swift-service-lifecycle` guarantees that the services will be initialized *before* the `LambdaRuntime`’s `run()` function is called.
90-
* The required services can then be used within the handler in a structured concurrency manner. `swift-service-lifecycle` takes care of listening for termination signals and terminating the services as well as the `LambdaRuntime` in correct order.
91-
* `LambdaTerminator` can now be eliminated because its role is replaced with `swift-service-lifecycle`. The termination logic of the Lambda function will be implemented in the conforming `run()` function of `LambdaRuntime`.
86+
- Instead of conforming to a handler protocol, users can now create a `LambdaRuntime` by passing in a handler closure.
87+
- `LambdaRuntime` conforms to `ServiceLifecycle.Service` by implementing a `run()` method that contains initialization and graceful shutdown logic.
88+
- This allows the lifecycle of the `LambdaRuntime` to be managed with `swift-service-lifecycle` _alongside_ and in the same way the lifecycles of the required services are managed, e.g. `try await ServiceGroup(services: [postgresClient, ..., lambdaRuntime], ...).run()`.
89+
- Dependencies can now be injected into `LambdaRuntime``swift-service-lifecycle` guarantees that the services will be initialized _before_ the `LambdaRuntime`’s `run()` function is called.
90+
- The required services can then be used within the handler in a structured concurrency manner. `swift-service-lifecycle` takes care of listening for termination signals and terminating the services as well as the `LambdaRuntime` in correct order.
91+
- `LambdaTerminator` can now be eliminated because its role is replaced with `swift-service-lifecycle`. The termination logic of the Lambda function will be implemented in the conforming `run()` function of `LambdaRuntime`.
9292

9393
With this, the earlier code snippet can be replaced with something much easier to read, maintain, and debug:
9494

@@ -112,13 +112,13 @@ let serviceGroup = ServiceGroup(
112112
try await serviceGroup.run()
113113
```
114114

115-
#### Simplifying Codable support
115+
### Simplifying Codable support
116116

117117
A detailed explanation is provided in the Codable Support section. In short, much of the boilerplate code defined for each handler protocol in `Lambda+Codable` and `Lambda+String` will be replaced with a single `LambdaCodableAdapter` class.
118118

119119
This adapter class is generic over (1) any handler conforming to `LambdaHandler`, (2) the user-specified input and output types, and (3) any decoder and encoder conforming to `LambdaEventDecoder` and `LambdaOutputDecoder`. The adapter will wrap the underlying handler with encoding/decoding logic.
120120

121-
#### Simplifying the handler protocols
121+
### Simplifying the handler protocols
122122

123123
There are four different handler protocols in the current API (`SimpleLambdaHandler`, `LambdaHandler`, `EventLoopLambdaHandler`, `ByteBufferLambdaHandler`). As noted in the **Current Limitations** section, the ease-of-use varies for each handler protocol and users may not be able to easily determine which protocol best serves their use-case without spending time digging into the library. To reduce this problem and provide users with clear-cut options, we propose replacing all of the existing handler protocols with just two: `LambdaHandler` and `StreamingLambdaHandler`. Both will be explained in the **Detailed Solution** section.
124124

@@ -262,7 +262,7 @@ public final class LambdaRuntime<Handler>: ServiceLifecycle.Service, Sendable
262262
}
263263
```
264264

265-
The current API allows for a Lambda function to be tested locally through a mock server by requiring an environment variable named `LOCAL_LAMBDA_SERVER_ENABLED` to be set to `true`. If this environment variable is not set, the program immediately crashes as the user will not have the `AWS_LAMBDA_RUNTIME_API` environment variable on their local machine (set automatically when deployed to AWS Lambda). However, making the user set the `LOCAL_LAMBDA_SERVER_ENABLED` environment variable is an unnecessary step that can be avoided. In the v2 API, the `run()` function will automatically start the mock server when the `AWS_LAMBDA_RUNTIME_API` environment variable cannot be found.
265+
The current API allows for a Lambda function to be tested locally through a mock server by requiring an environment variable named `LOCAL_LAMBDA_SERVER_ENABLED` to be set to `true`. If this environment variable is not set, the program immediately crashes as the user will not have the `AWS_LAMBDA_RUNTIME_API` environment variable on their local machine (set automatically when deployed to AWS Lambda). However, making the user set the `LOCAL_LAMBDA_SERVER_ENABLED` environment variable is an unnecessary step that can be avoided. In the v2 API, the `run()` function will automatically start the mock server when the `AWS_LAMBDA_RUNTIME_API` environment variable cannot be found.
266266

267267
### Lambda
268268

@@ -283,15 +283,15 @@ Since the library now provides ownership of the `main()` function and allows use
283283

284284
To retain support for initialization error reporting, the `Lambda.reportStartupError(any Error)` function gives users the option to manually report initialization errors in their closure handler. Although this should ideally happen implicitly like it currently does in v1, we believe this is a small compromise in comparison to the benefits gained in now being able to cleanly manage the lifecycles of required services in a structured concurrency manner.
285285

286-
287-
>Use-case:
286+
> Use-case:
287+
>
288+
> Assume we want to load a secret for the Lambda function from a secret vault first.
289+
> If this fails, we want to report the error to the control plane:
288290
>
289-
>Assume we want to load a secret for the Lambda function from a secret vault first.
290-
>If this fails, we want to report the error to the control plane:
291-
>```swift
292-
>let secretVault = SecretVault()
291+
> ```swift
292+
> let secretVault = SecretVault()
293293
>
294-
>do {
294+
> do {
295295
> /// !!! Error thrown: secret "foo" does not exist !!!
296296
> let secret = try await secretVault.getSecret("foo")
297297
>
@@ -305,11 +305,11 @@ To retain support for initialization error reporting, the `Lambda.reportStartupE
305305
> logger: logger
306306
> )
307307
> try await serviceGroup.run()
308-
>} catch {
308+
> } catch {
309309
> /// Report startup error straight away to the dedicated initialization error endpoint
310310
> try await Lambda.reportStartupError(error)
311-
>}
312-
>```
311+
> }
312+
> ```
313313
314314
### Codable support
315315
@@ -334,7 +334,7 @@ public protocol LambdaHandler {
334334
}
335335
```
336336
337-
2. Accepts *any* encoder and decoder object conforming to the `LambdaEventDecoder` and `LambdaOutputEncoder` protocols:
337+
2. Accepts _any_ encoder and decoder object conforming to the `LambdaEventDecoder` and `LambdaOutputEncoder` protocols:
338338

339339
#### LambdaEventDecoder and LambdaOutputEncoder protocols
340340

@@ -354,13 +354,13 @@ public protocol LambdaOutputEncoder {
354354
We provide conformances for Foundation’s `JSONDecoder` to `LambdaEventDecoder` and `JSONEncoder` to `LambdaOutputEncoder`.
355355

356356
3. Implements its `handle()` method by:
357-
1. Decoding the `ByteBuffer` event into the generic `Event` type.
358-
2. Passing the generic `Event` instance to the underlying handler's `handle()` method.
359-
3. Encoding the generic `Output` returned from the underlying `handle()` into JSON and returning it.
357+
1. Decoding the `ByteBuffer` event into the generic `Event` type.
358+
2. Passing the generic `Event` instance to the underlying handler's `handle()` method.
359+
3. Encoding the generic `Output` returned from the underlying `handle()` into JSON and returning it.
360360

361361
#### LambdaCodableAdapter
362362

363-
`LambdaCodableAdapter` can implement encoding/decoding for *any* handler conforming to `LambdaHandler` if `Event` is `Decodable` and the `Output` is `Encodable`, meaning that the encoding/decoding stubs do not need to be implemented by the user.
363+
`LambdaCodableAdapter` can implement encoding/decoding for _any_ handler conforming to `LambdaHandler` if `Event` is `Decodable` and the `Output` is `Encodable`, meaning that the encoding/decoding stubs do not need to be implemented by the user.
364364

365365
```swift
366366
/// Wraps an underlying handler conforming to `CodableLambdaHandler`

0 commit comments

Comments
 (0)