@@ -34,6 +34,7 @@ storage layer, so you'll need to create a table first.
34
34
> Example using AWS Serverless Application Model (SAM)
35
35
36
36
=== "template.yml"
37
+
37
38
```yaml
38
39
Resources:
39
40
HelloWorldFunction:
@@ -76,32 +77,32 @@ You can quickly start by initializing the `DynamoDBPersistenceLayer` class outsi
76
77
with the ` idempotent ` decorator on your lambda handler. The only required parameter is ` table_name ` , but you likely
77
78
want to specify ` event_key_jmespath ` as well.
78
79
79
- ` event_key_jmespath ` : A JMESpath expression which will be used to extract the payload from the event your Lambda hander
80
+ ` event_key_jmespath ` : A JMESpath expression which will be used to extract the payload from the event your Lambda handler
80
81
is called with. This payload will be used as the key to decide if future invocations are duplicates. If you don't pass
81
82
this parameter, the entire event will be used as the key.
82
83
83
84
=== "app.py"
84
85
85
86
```python hl_lines="2 6-9 11"
86
- import json
87
- from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
88
-
89
- # Treat everything under the "body" key in
90
- # the event json object as our payload
91
- persistence_layer = DynamoDBPersistenceLayer(
92
- event_key_jmespath="body ",
93
- table_name="IdempotencyTable"
94
- )
87
+ import json
88
+ from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
89
+
90
+ # Treat everything under the "body" key in
91
+ # the event json object as our payload
92
+ persistence_layer = DynamoDBPersistenceLayer(
93
+ table_name="IdempotencyTable ",
94
+ event_key_jmespath="body",
95
+ )
95
96
96
- @idempotent(persistence_store=persistence_layer)
97
- def handler(event, context):
98
- body = json.loads(event['body'])
99
- payment = create_subscription_payment(
100
- user=body['user'],
101
- product=body['product_id']
102
- )
103
- ...
104
- return {"message": "success", "statusCode": 200, "payment_id": payment.id}
97
+ @idempotent(persistence_store=persistence_layer)
98
+ def handler(event, context):
99
+ body = json.loads(event['body'])
100
+ payment = create_subscription_payment(
101
+ user=body['user'],
102
+ product=body['product_id']
103
+ )
104
+ ...
105
+ return {"message": "success", "statusCode": 200, "payment_id": payment.id}
105
106
```
106
107
=== "Example event"
107
108
@@ -171,17 +172,19 @@ In most cases, it is not desirable to store the idempotency records forever. Rat
171
172
same payload won't be executed within a period of time. By default, the period is set to 1 hour (3600 seconds). You can
172
173
change this window with the ` expires_after_seconds ` parameter:
173
174
174
- ``` python hl_lines="4"
175
- DynamoDBPersistenceLayer(
176
- event_key_jmespath = " body" ,
177
- table_name = " IdempotencyTable" ,
178
- expires_after_seconds = 5 * 60 # 5 minutes
175
+ === "app.py"
176
+
177
+ ```python hl_lines="4"
178
+ DynamoDBPersistenceLayer(
179
+ table_name="IdempotencyTable",
180
+ event_key_jmespath="body",
181
+ expires_after_seconds=5*60, # 5 minutes
179
182
)
183
+ ```
180
184
181
- ```
182
185
This will mark any records older than 5 minutes as expired, and the lambda handler will be executed as normal if it is
183
186
invoked with a matching payload. If you have set the TTL field in DynamoDB like in the SAM example above, the record
184
- will be automatically deleted from the table after a period of itme .
187
+ will be automatically deleted from the table after a period of time .
185
188
186
189
187
190
### Handling concurrent executions
@@ -195,17 +198,19 @@ concurrent execution. If you receive this error, you can safely retry the operat
195
198
To reduce the number of lookups to the persistence storage layer, you can enable in memory caching with the
196
199
` use_local_cache ` parameter, which is disabled by default. This cache is local to each Lambda execution environment.
197
200
This means it will be effective in cases where your function's concurrency is low in comparison to the number of
198
- "retry" invocations with the same payload. When enabled, the default is to cache a maxmum of 256 records in each Lambda
201
+ "retry" invocations with the same payload. When enabled, the default is to cache a maximum of 256 records in each Lambda
199
202
execution environment. You can change this with the ` local_cache_max_items ` parameter.
200
203
201
- ``` python hl_lines="4 5"
202
- DynamoDBPersistenceLayer(
203
- event_key_jmespath = " body" ,
204
- table_name = " IdempotencyTable" ,
205
- use_local_cache = True ,
206
- local_cache_max_items = 1000
204
+ === "app.py"
205
+
206
+ ```python hl_lines="4 5"
207
+ DynamoDBPersistenceLayer(
208
+ table_name="IdempotencyTable",
209
+ event_key_jmespath="body",
210
+ use_local_cache=True,
211
+ local_cache_max_items=1000
207
212
)
208
- ```
213
+ ```
209
214
210
215
211
216
## Advanced
@@ -218,35 +223,37 @@ with the `payload_validation_jmespath` to specify which part of the event body s
218
223
idempotent invocations.
219
224
220
225
=== "app.py"
226
+
221
227
```python hl_lines="6"
222
228
from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
223
229
224
- persistence_layer = DynamoDBPersistenceLayer(
225
- event_key_jmespath="[userDetail, productId] ",
226
- table_name="IdempotencyTable",)
227
- payload_validation_jmespath="amount"
228
- )
230
+ persistence_layer = DynamoDBPersistenceLayer(
231
+ table_name="IdempotencyTable ",
232
+ event_key_jmespath="[userDetail, productId]",
233
+ payload_validation_jmespath="amount"
234
+ )
229
235
230
- @idempotent(persistence_store=persistence_layer)
231
- def handler(event, context):
232
- # Creating a subscription payment is a side
233
- # effect of calling this function!
234
- payment = create_subscription_payment(
235
- user=event['userDetail']['username'],
236
- product=event['product_id'],
237
- amount=event['amount']
238
- )
239
- ...
240
- return {"message": "success", "statusCode": 200,
241
- "payment_id": payment.id, "amount": payment.amount}
236
+ @idempotent(persistence_store=persistence_layer)
237
+ def handler(event, context):
238
+ # Creating a subscription payment is a side
239
+ # effect of calling this function!
240
+ payment = create_subscription_payment(
241
+ user=event['userDetail']['username'],
242
+ product=event['product_id'],
243
+ amount=event['amount']
244
+ )
245
+ ...
246
+ return {"message": "success", "statusCode": 200,
247
+ "payment_id": payment.id, "amount": payment.amount}
242
248
```
243
- === "Event"
249
+ === "Example Event"
250
+
244
251
```json
245
252
{
246
253
"userDetail": {
247
254
"username": "User1",
248
255
249
- },
256
+ },
250
257
"productId": 1500,
251
258
"charge_type": "subscription",
252
259
"amount": 500
@@ -264,13 +271,15 @@ amount field, we prevent this potentially confusing behaviour and instead raise
264
271
If you want to enforce that an idempotency key is required, you can set ` raise_on_no_idempotency_key ` to ` True ` ,
265
272
and we will raise ` IdempotencyKeyError ` if none was found.
266
273
267
- ``` python hl_lines="4"
268
- DynamoDBPersistenceLayer(
269
- event_key_jmespath = " body" ,
270
- table_name = " IdempotencyTable" ,
271
- raise_on_no_idempotency_key = True
274
+ === "app.py"
275
+
276
+ ```python hl_lines="4"
277
+ DynamoDBPersistenceLayer(
278
+ table_name="IdempotencyTable",
279
+ event_key_jmespath="body",
280
+ raise_on_no_idempotency_key=True,
272
281
)
273
- ```
282
+ ```
274
283
275
284
### Changing dynamoDB attribute names
276
285
If you want to use an existing DynamoDB table, or wish to change the name of the attributes used to store items in the
@@ -288,53 +297,56 @@ validation_key_attr | "validation" | Hashed representation of the parts of the
288
297
This example demonstrates changing the attribute names to custom values:
289
298
290
299
=== "app.py"
291
- ```python hl_lines="5-10"
300
+
301
+ ```python hl_lines="4-8"
292
302
persistence_layer = DynamoDBPersistenceLayer(
293
- event_key_jmespath="[ userDetail, productId] ",
294
303
table_name="IdempotencyTable",
304
+ event_key_jmespath="[userDetail, productId]",
295
305
key_attr="idempotency_key",
296
306
expiry_attr="expires_at",
297
307
status_attr="current_status",
298
308
data_attr="result_data",
299
309
validation_key_attr="validation_key"
300
- )
310
+ )
301
311
```
302
312
303
313
### Customizing boto configuration
304
314
You can provide custom boto configuration or event bring your own boto3 session if required by using the ` boto_config `
305
315
or ` boto3_session ` parameters when constructing the persistence store.
306
316
307
317
=== "Custom session"
318
+
308
319
```python hl_lines="1 4 8"
309
- import boto3
310
- from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
311
-
312
- boto3_session = boto3.session.Session()
313
- persistence_layer = DynamoDBPersistenceLayer(
314
- event_key_jmespath="body ",
315
- table_name="IdempotencyTable ",
316
- boto3_session=boto3_session
317
- )
320
+ import boto3
321
+ from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
322
+
323
+ boto3_session = boto3.session.Session()
324
+ persistence_layer = DynamoDBPersistenceLayer(
325
+ table_name="IdempotencyTable ",
326
+ event_key_jmespath="body ",
327
+ boto3_session=boto3_session
328
+ )
318
329
319
- @idempotent(persistence_store=persistence_layer)
320
- def handler(event, context):
321
- ...
330
+ @idempotent(persistence_store=persistence_layer)
331
+ def handler(event, context):
332
+ ...
322
333
```
323
334
=== "Custom config"
335
+
324
336
```python hl_lines="1 4 8"
325
- from botocore.config import Config
326
- from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
327
-
328
- boto_config = Config()
329
- persistence_layer = DynamoDBPersistenceLayer(
330
- event_key_jmespath="body ",
331
- table_name="IdempotencyTable ",
332
- boto_config=boto_config
333
- )
334
-
335
- @idempotent(persistence_store=persistence_layer)
336
- def handler(event, context):
337
- ...
337
+ from botocore.config import Config
338
+ from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, idempotent
339
+
340
+ boto_config = Config()
341
+ persistence_layer = DynamoDBPersistenceLayer(
342
+ table_name="IdempotencyTable ",
343
+ event_key_jmespath="body ",
344
+ boto_config=boto_config
345
+ )
346
+
347
+ @idempotent(persistence_store=persistence_layer)
348
+ def handler(event, context):
349
+ ...
338
350
```
339
351
340
352
### Bring your own persistent store
@@ -357,14 +369,15 @@ The idempotency utility can be used with the `validator` decorator. Ensure that
357
369
` event_key_jmespath ` .
358
370
359
371
=== "app.py"
372
+
360
373
```python hl_lines="9 10"
361
374
from aws_lambda_powertools.utilities.validation import validator, envelopes
362
375
from aws_lambda_powertools.utilities.idempotency.idempotency import idempotent
363
376
364
377
persistence_layer = DynamoDBPersistenceLayer(
365
- event_key_jmespath="[message, username]",
366
378
table_name="IdempotencyTable",
367
- )
379
+ event_key_jmespath="[message, username]",
380
+ )
368
381
369
382
@validator(envelope=envelopes.API_GATEWAY_HTTP)
370
383
@idempotent(persistence_store=persistence_layer)
0 commit comments