Skip to content

Commit 92f3686

Browse files
author
Alexander Schueren
authored
docs(idempotency: improve docs & snippets (#1655)
1 parent 9668a0f commit 92f3686

File tree

2 files changed

+46
-32
lines changed

2 files changed

+46
-32
lines changed

Diff for: docs/snippets/idempotency/makeIdempotentJmes.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const createSubscriptionPayment = async (
2424

2525
// Extract the idempotency key from the request headers
2626
const config = new IdempotencyConfig({
27-
eventKeyJmesPath: 'headers."X-Idempotency-Key"',
27+
eventKeyJmesPath: 'body',
2828
});
2929

3030
export const handler = makeIdempotent(

Diff for: docs/utilities/idempotency.md

+45-31
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,22 @@ classDiagram
5252

5353
## Getting started
5454

55-
### IAM Permissions
55+
### Installation
56+
Install the library in your project
57+
```shell
58+
npm i @aws-lambda-powertools/idempotency @aws-sdk/client-dynamodb
59+
```
60+
61+
While we support Amazon DynamoDB as a persistance layer out of the box, you need to bring your own AWS SDK for JavaScript v3 DynamoDB client.
5662

57-
Your Lambda function IAM Role must have `dynamodb:GetItem`, `dynamodb:PutItem`, `dynamodb:UpdateItem` and `dynamodb:DeleteItem` IAM permissions before using this feature.
5863

5964
???+ note
60-
If you're using one of our examples: [AWS Serverless Application Model (SAM)](#required-resources) or [Terraform](#required-resources) the required permissions are already included.
65+
This utility supports **[AWS SDK for JavaScript v3](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/){target="_blank"} only**. If you are using the `nodejs18.x` runtime, the AWS SDK for JavaScript v3 is already installed and you can install only the utility.
66+
67+
68+
### IAM Permissions
69+
70+
Your Lambda function IAM Role must have `dynamodb:GetItem`, `dynamodb:PutItem`, `dynamodb:UpdateItem` and `dynamodb:DeleteItem` IAM permissions before using this feature. If you're using one of our examples: [AWS Serverless Application Model (SAM)](#required-resources) or [Terraform](#required-resources) the required permissions are already included.
6171

6272
### Required resources
6373

@@ -69,10 +79,10 @@ As of now, Amazon DynamoDB is the only supported persistent storage layer, so yo
6979

7080
If you're not [changing the default configuration for the DynamoDB persistence layer](#dynamodbpersistencelayer), this is the expected default configuration:
7181

72-
| Configuration | Value | Notes |
73-
| ------------------ | ------------ | ----------------------------------------------------------------------------------- |
74-
| Partition key | `id` |
75-
| TTL attribute name | `expiration` | This can only be configured after your table is created if you're using AWS Console |
82+
| Configuration | Default value | Notes |
83+
| ------------------ |:--------------|-----------------------------------------------------------------------------------------|
84+
| Partition key | `id` | The id of each idempotency record which a combination of `functionName#hashOfPayload`. |
85+
| TTL attribute name | `expiration` | This can only be configured after your table is created if you're using AWS Console. |
7686

7787
???+ tip "Tip: You can share a single state table for all functions"
7888
You can reuse the same DynamoDB table to store idempotency state. We add the Lambda function name in addition to the idempotency key as a hash key.
@@ -212,26 +222,26 @@ If you're not [changing the default configuration for the DynamoDB persistence l
212222

213223
You can quickly start by initializing the `DynamoDBPersistenceLayer` class and using it with the `makeIdempotent` function wrapper on your Lambda handler.
214224

215-
???+ note
216-
In this example, the entire Lambda handler is treated as a single idempotent operation. If your Lambda handler can cause multiple side effects, or you're only interested in making a specific logic idempotent, use the `makeIdempotent` high-order function only on the function that needs to be idempotent.
217-
218-
!!! tip "See [Choosing a payload subset for idempotency](#choosing-a-payload-subset-for-idempotency) for more elaborate use cases."
219-
220225
=== "index.ts"
221226

222227
```typescript hl_lines="2-3 21 35-38"
223228
--8<-- "docs/snippets/idempotency/makeIdempotentBase.ts"
224229
```
225230

226-
=== "Types"
231+
=== "types.ts"
227232

228233
```typescript
229234
--8<-- "docs/snippets/idempotency/types.ts::13"
230235
```
231236

232237
After processing this request successfully, a second request containing the exact same payload above will now return the same response, ensuring our customer isn't charged twice.
233238

234-
!!! question "New to idempotency concept? Please review our [Terminology](#terminology) section if you haven't yet."
239+
240+
???+ note
241+
In this example, the entire Lambda handler is treated as a single idempotent operation. If your Lambda handler can cause multiple side effects, or you're only interested in making a specific logic idempotent, use the `makeIdempotent` high-order function only on the function that needs to be idempotent.
242+
243+
See [Choosing a payload subset for idempotency](#choosing-a-payload-subset-for-idempotency) for more elaborate use cases.
244+
235245

236246
You can also use the `makeIdempotent` function wrapper on any function that returns a response to make it idempotent. This is useful when you want to make a specific logic idempotent, for example when your Lambda handler performs multiple side effects and you only want to make a specific one idempotent.
237247

@@ -240,21 +250,21 @@ You can also use the `makeIdempotent` function wrapper on any function that retu
240250

241251
When using `makeIdempotent` on arbitrary functions, you can tell us which argument in your function signature has the data we should use via **`dataIndexArgument`**. If you don't specify this argument, we'll use the first argument in the function signature.
242252

243-
???+ note
244-
The function in the example below has two arguments, note that while wrapping it with the `makeIdempotent` high-order function, we specify the `dataIndexArgument` as `1` to tell the decorator that the second argument is the one that contains the data we should use to make the function idempotent. Remember that arguments are zero-indexed, so the first argument is `0`, the second is `1`, and so on.
245-
246253
=== "index.ts"
247254

248255
```typescript hl_lines="22 34-38"
249256
--8<-- "docs/snippets/idempotency/makeIdempotentAnyFunction.ts"
250257
```
251258

252-
=== "Types"
259+
=== "types.ts"
253260

254261
```typescript
255262
--8<-- "docs/snippets/idempotency/types.ts::13"
256263
```
257264

265+
The function this example has two arguments, note that while wrapping it with the `makeIdempotent` high-order function, we specify the `dataIndexArgument` as `1` to tell the decorator that the second argument is the one that contains the data we should use to make the function idempotent. Remember that arguments are zero-indexed, so the first argument is `0`, the second is `1`, and so on.
266+
267+
258268
### MakeHandlerIdempotent Middy middleware
259269

260270
!!! tip "A note about Middy"
@@ -269,28 +279,25 @@ If you are using [Middy](https://middy.js.org){target="_blank"} as your middlewa
269279
--8<-- "docs/snippets/idempotency/makeHandlerIdempotent.ts"
270280
```
271281

272-
=== "Types"
282+
=== "types.ts"
273283

274284
```typescript
275285
--8<-- "docs/snippets/idempotency/types.ts::13"
276286
```
277287

278288
### Choosing a payload subset for idempotency
279289

280-
???+ tip "Tip: Dealing with always changing payloads"
281-
When dealing with a more elaborate payload, where parts of the payload always change, you should use the **`eventKeyJmesPath`** parameter.
282-
283-
Use [`IdempotencyConfig`](#customizing-the-default-behavior) to instruct the idempotent decorator to only use a portion of your payload to verify whether a request is idempotent, and therefore it should not be retried.
290+
Use [`IdempotencyConfig`](#customizing-the-default-behavior) to instruct the idempotent decorator to only use a portion of your payload to verify whether a request is idempotent, and therefore it should not be retried. When dealing with a more elaborate payload, where parts of the payload always change, you should use the **`eventKeyJmesPath`** parameter.
284291

285-
> **Payment scenario**
292+
**Payment scenario**
286293

287294
In this example, we have a Lambda handler that creates a payment for a user subscribing to a product. We want to ensure that we don't accidentally charge our customer by subscribing them more than once.
288295

289296
Imagine the function executes successfully, but the client never receives the response due to a connection issue. It is safe to retry in this instance, as the idempotent decorator will return a previously saved response.
290297

291298
**What we want here** is to instruct Idempotency to use the `user` and `productId` fields from our incoming payload as our idempotency key. If we were to treat the entire request as our idempotency key, a simple HTTP header or timestamp change would cause our customer to be charged twice.
292299

293-
???+ tip "Deserializing JSON strings in payloads for increased accuracy."
300+
???+ warning "Deserializing JSON strings in payloads for increased accuracy."
294301
The payload extracted by the `eventKeyJmesPath` is treated as a string by default. This means there could be differences in whitespace even when the JSON payload itself is identical.
295302

296303
=== "index.ts"
@@ -334,26 +341,26 @@ Imagine the function executes successfully, but the client never receives the re
334341
}
335342
```
336343

337-
=== "Types"
344+
=== "types.ts"
338345

339346
```typescript
340347
--8<-- "docs/snippets/idempotency/types.ts::13"
341348
```
342349

343350
### Lambda timeouts
344351

345-
???+ note
346-
This is automatically done when you wrap your Lambda handler with the [makeIdempotent](#makeIdempotent-function-wrapper) function wrapper, or use the [`makeHandlerIdempotent`](#makeHandlerIdempotent-middy-middleware) Middy middleware.
347352

348-
To prevent against extended failed retries when a [Lambda function times out](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-verify-invocation-timeouts/), Powertools for AWS calculates and includes the remaining invocation available time as part of the idempotency record.
353+
354+
To prevent against extended failed retries when a [Lambda function times out](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-verify-invocation-timeouts/), Powertools for AWS Lambda calculates and includes the remaining invocation available time as part of the idempotency record.
355+
This is automatically done when you wrap your Lambda handler with the [makeIdempotent](#makeIdempotent-function-wrapper) function wrapper, or use the [`makeHandlerIdempotent`](#makeHandlerIdempotent-middy-middleware) Middy middleware.
349356

350357
???+ example
351358
If a second invocation happens **after** this timestamp, and the record is marked as `INPROGRESS`, we will execute the invocation again as if it was in the `EXPIRED` state (e.g, `expire_seconds` field elapsed).
352359

353360
This means that if an invocation expired during execution, it will be quickly executed again on the next retry.
354361

355362
???+ important
356-
If you are only using the [makeIdempotent function wrapper](#makeIdempotent-function-wrapper) to guard isolated parts of your code, you must use `registerLambdaContext` available in the [idempotency config object](#customizing-the-default-behavior) to benefit from this protection.
363+
If you are only using the [makeIdempotent function wrapper](#makeIdempotent-function-wrapper) to guard isolated parts of your code outside of your handler, you must use `registerLambdaContext` available in the [idempotency config object](#customizing-the-default-behavior) to benefit from this protection.
357364

358365
Here is an example on how you register the Lambda context in your handler:
359366

@@ -371,6 +378,7 @@ This means that new invocations will execute your code again despite having the
371378
<center>
372379
```mermaid
373380
sequenceDiagram
381+
autonumber
374382
participant Client
375383
participant Lambda
376384
participant Persistence Layer
@@ -410,6 +418,7 @@ The following sequence diagrams explain how the Idempotency feature behaves unde
410418
<center>
411419
```mermaid
412420
sequenceDiagram
421+
autonumber
413422
participant Client
414423
participant Lambda
415424
participant Persistence Layer
@@ -444,6 +453,7 @@ sequenceDiagram
444453
<center>
445454
```mermaid
446455
sequenceDiagram
456+
autonumber
447457
participant Client
448458
participant Lambda
449459
participant Persistence Layer
@@ -474,6 +484,7 @@ sequenceDiagram
474484
<center>
475485
```mermaid
476486
sequenceDiagram
487+
autonumber
477488
participant Client
478489
participant Lambda
479490
participant Persistence Layer
@@ -509,6 +520,7 @@ sequenceDiagram
509520
<center>
510521
```mermaid
511522
sequenceDiagram
523+
autonumber
512524
participant Client
513525
participant Lambda
514526
participant Persistence Layer
@@ -537,6 +549,7 @@ sequenceDiagram
537549
<center>
538550
```mermaid
539551
sequenceDiagram
552+
autonumber
540553
participant Client
541554
participant Lambda
542555
participant Persistence Layer
@@ -570,6 +583,7 @@ sequenceDiagram
570583
<center>
571584
```mermaid
572585
sequenceDiagram
586+
autonumber
573587
participant Client
574588
participant Lambda
575589
participant Persistence Layer
@@ -794,4 +808,4 @@ The example function above would cause data to be stored in DynamoDB like this:
794808
## Extra resources
795809

796810
If you're interested in a deep dive on how Amazon uses idempotency when building our APIs, check out
797-
[this article](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/){target="_blank"}.
811+
[this article](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/){target="_blank"}.

0 commit comments

Comments
 (0)