Skip to content

Commit 0ce0bb2

Browse files
committed
fix(idempotency): capture the lambda context automatically
1 parent 1a72214 commit 0ce0bb2

File tree

6 files changed

+39
-44
lines changed

6 files changed

+39
-44
lines changed

aws_lambda_powertools/utilities/idempotency/base.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,8 @@ def _get_remaining_time_in_millis(self) -> Optional[int]:
130130
Remaining time in millis, or None if the remaining time cannot be determined.
131131
"""
132132

133-
# Look to see if we have stored a Lambda Context
134133
if self.config.lambda_context is not None:
135-
self.config.lambda_context.get_remaining_time_in_millis()
136-
137-
# Look into fn_args to see if we have a lambda context
138-
if self.fn_args and len(self.fn_args) == 2 and getattr(self.fn_args[1], "get_remaining_time_in_millis", None):
139-
return self.fn_args[1].get_remaining_time_in_millis()
134+
return self.config.lambda_context.get_remaining_time_in_millis()
140135

141136
return None
142137

aws_lambda_powertools/utilities/idempotency/idempotency.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def idempotent(
6262
return handler(event, context)
6363

6464
config = config or IdempotencyConfig()
65+
config.register_lambda_context(context)
66+
6567
args = event, context
6668
idempotency_handler = IdempotencyHandler(
6769
function=handler,

docs/utilities/idempotency.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,11 @@ In cases where the [Lambda invocation
392392
expires](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-verify-invocation-timeouts/),
393393
Powertools doesn't have the chance to set the idempotency record to `EXPIRED`.
394394
This means that the record would normally have been locked until `expire_seconds` have
395-
passsed.
395+
passed.
396396

397397
However, when Powertools has access to the Lambda invocation context, we are able to calculate the remaining
398398
available time for the invocation, and save it on the idempotency record. This way, if a second invocation happens
399-
after this timestamp, and the record is still marked `INPROGRESS`, we execute the inovcation again as if it was
399+
after this timestamp, and the record is still marked `INPROGRESS`, we execute the invocation again as if it was
400400
already expired. This means that if an invocation expired during execution, it will be quickly executed again on the
401401
next retry.
402402

@@ -435,9 +435,9 @@ sequenceDiagram
435435
</center>
436436

437437
???+ info "Info: Calculating the remaining available time"
438-
When using the `idempotent` decorator, we captura and calculate the remaining available time for you.
438+
When using the `idempotent` decorator, we capture and calculate the remaining available time for you.
439439
However, when using the `idempotent_function`, the functionality doesn't work out of the box. You'll
440-
need to register the Lambda context on your handler:
440+
need to register the Lambda context in your handler:
441441

442442
```python hl_lines="8 16" title="Registering the Lambda context"
443443
from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord

tests/functional/idempotency/conftest.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import datetime
22
import json
3-
from collections import namedtuple
43
from decimal import Decimal
54
from unittest import mock
65

@@ -32,14 +31,17 @@ def lambda_apigw_event():
3231

3332
@pytest.fixture
3433
def lambda_context():
35-
lambda_context = {
36-
"function_name": "test-func",
37-
"memory_limit_in_mb": 128,
38-
"invoked_function_arn": "arn:aws:lambda:eu-west-1:809313241234:function:test-func",
39-
"aws_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
40-
}
34+
class LambdaContext:
35+
def __init__(self):
36+
self.function_name = "test-func"
37+
self.memory_limit_in_mb = 128
38+
self.invoked_function_arn = "arn:aws:lambda:eu-west-1:809313241234:function:test-func"
39+
self.aws_request_id = "52fdfc07-2182-154f-163f-5f0f9a621d72"
40+
41+
def get_remaining_time_in_millis(self) -> int:
42+
return 1000
4143

42-
return namedtuple("LambdaContext", lambda_context.keys())(*lambda_context.values())
44+
return LambdaContext()
4345

4446

4547
@pytest.fixture
@@ -142,7 +144,12 @@ def expected_params_put_item(hashed_idempotency_key):
142144
"#in_progress_expiry": "in_progress_expiration",
143145
},
144146
"ExpressionAttributeValues": {":now": stub.ANY, ":inprogress": "INPROGRESS"},
145-
"Item": {"expiration": stub.ANY, "id": hashed_idempotency_key, "status": "INPROGRESS"},
147+
"Item": {
148+
"expiration": stub.ANY,
149+
"id": hashed_idempotency_key,
150+
"status": "INPROGRESS",
151+
"in_progress_expiration": stub.ANY,
152+
},
146153
"TableName": "TEST_TABLE",
147154
}
148155

@@ -163,6 +170,7 @@ def expected_params_put_item_with_validation(hashed_idempotency_key, hashed_vali
163170
"ExpressionAttributeValues": {":now": stub.ANY, ":inprogress": "INPROGRESS"},
164171
"Item": {
165172
"expiration": stub.ANY,
173+
"in_progress_expiration": stub.ANY,
166174
"id": hashed_idempotency_key,
167175
"status": "INPROGRESS",
168176
"validation": hashed_validation_key,

tests/functional/idempotency/test_idempotency.py

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -768,37 +768,22 @@ def lambda_handler(event, context):
768768
stubber.deactivate()
769769

770770

771-
@pytest.mark.parametrize("idempotency_config", [{"use_local_cache": False}, {"use_local_cache": True}], indirect=True)
772-
def test_idempotent_lambda_expires_in_progress_unavailable_remaining_time(
773-
idempotency_config: IdempotencyConfig,
774-
persistence_store: DynamoDBPersistenceLayer,
775-
lambda_apigw_event,
776-
lambda_response,
777-
lambda_context,
778-
expected_params_put_item,
779-
expected_params_update_item,
780-
):
781-
stubber = stub.Stubber(persistence_store.table.meta.client)
782-
783-
ddb_response = {}
784-
stubber.add_response("put_item", ddb_response, expected_params_put_item)
785-
stubber.add_response("update_item", ddb_response, expected_params_update_item)
786-
787-
stubber.activate()
771+
def test_idempotent_lambda_expires_in_progress_unavailable_remaining_time():
772+
mock_event = {"data": "value"}
773+
idempotency_key = "test-func.function#" + hash_idempotency_key(mock_event)
774+
persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
775+
expected_result = {"message": "Foo"}
788776

789-
@idempotent(config=idempotency_config, persistence_store=persistence_store)
790-
def lambda_handler(event, context):
791-
return lambda_response
777+
@idempotent_function(persistence_store=persistence_layer, data_keyword_argument="record")
778+
def function(record):
779+
return expected_result
792780

793781
with warnings.catch_warnings(record=True) as w:
794782
warnings.simplefilter("default")
795-
lambda_handler(lambda_apigw_event, lambda_context)
783+
function(record=mock_event)
796784
assert len(w) == 1
797785
assert str(w[-1].message) == "Expires in progress is enabled but we couldn't determine the remaining time left"
798786

799-
stubber.assert_no_pending_responses()
800-
stubber.deactivate()
801-
802787

803788
def test_data_record_invalid_status_value():
804789
data_record = DataRecord("key", status="UNSUPPORTED_STATUS")

tests/functional/idempotency/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ def build_idempotency_put_item_stub(
2929
"#in_progress_expiry": "in_progress_expiration",
3030
},
3131
"ExpressionAttributeValues": {":now": stub.ANY, ":inprogress": "INPROGRESS"},
32-
"Item": {"expiration": stub.ANY, "id": idempotency_key_hash, "status": "INPROGRESS"},
32+
"Item": {
33+
"expiration": stub.ANY,
34+
"id": idempotency_key_hash,
35+
"status": "INPROGRESS",
36+
"in_progress_expiration": stub.ANY,
37+
},
3338
"TableName": "TEST_TABLE",
3439
}
3540

0 commit comments

Comments
 (0)