Repository navigation
Description
Expected Behaviour
When making a function idempotent via the @idempotent decorator and enabling payload validation the Idempotency utility should raise a payload validation exception when the payload in subsequent requests differs from the one stored.
With this configuration:
persistence_layer = DynamoDBPersistenceLayer(table_name=os.environ["TABLE_NAME"])
config = IdempotencyConfig(
event_key_jmespath='["rcn", "subscriptionType"]',
payload_validation_jmespath="customerAccounts",
)we are using two fields from the payload to generate the idempotency hash (rcn and subscriptionType) but we are using a different key (customerAccounts) for payload validation.
If we send the following two requests:
{
"rcn": "RCN-000-000-000-000",
"customerAccounts": {
"customerAccount": [
{
"accountId": "12345",
"accountType": "someaccounttype"
}
]
},
"subscriptionType": "somesubscriptiontype"
}and
{
"rcn": "RCN-000-000-000-000",
"customerAccounts": {
"customerAccount": [
{
"accountId": "54321",
"accountType": "someaccounttype"
}
]
},
"subscriptionType": "somesubscriptiontype"
}These two requests should be considered idempotent because the compound value of rcn and subscriptionType is the same across requests.
At the same time, the values under customerAccounts have changed between requests (note customerAccounts.customerAccount[0].accountId being different). This means the payload validation should fail in the second request.
Current Behaviour
The function does not throw a validation error even though payload validation is enabled and parts of the payload differ from the stored one.
See screenshot below, where I send both requests and both are successful:

Code snippet
import os
from aws_lambda_powertools.utilities.idempotency import (
DynamoDBPersistenceLayer,
IdempotencyConfig,
idempotent,
)
from aws_lambda_powertools.utilities.typing import LambdaContext
persistence_layer = DynamoDBPersistenceLayer(table_name=os.environ["TABLE_NAME"])
config = IdempotencyConfig(
event_key_jmespath='["rcn", "subscriptionType"]',
payload_validation_jmespath="customerAccounts",
)
@idempotent(persistence_store=persistence_layer, config=config)
def handler(event: dict, context: LambdaContext):
return TruePossible Solution
As part of a feature added in #3446 we are using DynamoDB ReturnValuesOnConditionCheckFailure whenever available. This feature allows the DynamoDB to return an item that caused a conditional operation to fail, which in turn allows the Idempotency utility to get the IdempotencyRecord of a request without having to do an additional read.
In the base idempotency class (here), we have added an if/else statement that uses the returned item when available, or otherwise it calls the _get_idemptoency_record() method when one is not available - this last case can happen when customers are using older versions of boto3.
With the current implementation, the logic that validates the payload is executed only in the else clause, which causes payload validation to be skipped.
A potential fix would be to make the _validate_payload() method in the BasePersistenceLayer class and then call it on the exc.old_data_record, similar to this:
record = exc.old_data_record
if exc.old_data_record is not None:
self.persistence_store.validate_payload(
self.data,
record
)
else:
record = self._get_idempotency_record()Steps to Reproduce
See steps above.
Powertools for AWS Lambda (Python) version
latest
AWS Lambda function runtime
3.12
Packaging format used
PyPi
Debugging logs
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsShipped
Activity
dreamorosi commentedon Feb 16, 2024
I have tried implementing the fix I was suggesting in the OP but I wasn't able to make the tests pass :/
rubenfonseca commentedon Feb 19, 2024
Thank you for opening this detailed issue Andrea! I'll try to finish the PR
rubenfonseca commentedon Feb 20, 2024
So acording to
powertools-lambda-python/aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py
Lines 251 to 267 in c135cec
heitorlessa commentedon Feb 20, 2024
couldn't find the test for this enhancement (that potentially triggered this regression) -- looking at the history now
heitorlessa commentedon Feb 20, 2024
found the test that was modified with this feature but seems to be working as expected, will look at E2E and create one if there isn't one to be absolutely sure.
Tasks
heitorlessa commentedon Feb 20, 2024
okay managed to reproduce - validation at nested fields aren't working, that's why tests worked but @dreamorosi payload didn't. Docs also use a single key/value, compound would also work, but nested fields didn't.
NOTE. This is not related to
ReturnValueOnConditionCheckFailure. Our tests caught the change before, and that's why @rubenfonseca wasn't able to validate.I've created a functional test along w/ some basic tooling (we sorely need testing tooling) to make it easier to identify the root cause. I suspect it might be down to ordering as hashing it's occurring.
heitorlessa commentedon Feb 20, 2024
Turns out I only managed to reproduce because of fat finger ... I used the wrong validation key after editing the wrong file (same name but local cache for E2E).
I can't reproduce with both functional and E2E tests. I'm gonna have another look at the original snippet see if I can spot anything, and push a PR to improve our tests regardless.
That said, TS has another issue I thought it affected us... ordering. We deep sort payloads both incoming and for validation before generating a hash.
Phew!
heitorlessa commentedon Feb 20, 2024
Waiting for E2E to run with this new case (functional tests are passing) -- I can't for the love of me reproduce it. Added exact same nesting and change in functional and E2E test above in the PR -- I'll try one last time with a manual Lambda tomorrow.
https://github.com/aws-powertools/powertools-lambda-python/actions/runs/7976558714
dreamorosi commentedon Feb 20, 2024
No need.
I have deployed the manual stack from scratch & ran the two payloads again and the second request is throwing as expected.
Given this I can only assume that I must have made a mistake. Apologies for making you run in circles and wasting your time @rubenfonseca & @heitorlessa.
I'll be more thorough in the future.
heitorlessa commentedon Feb 21, 2024
not at all. First, better safe than sorry. Second, this was an useful exercise to improve our test coverage on payload validation as it was overly simplistic :)
If anything, keep it coming @dreamorosi. It was also useful exercise to discover the ordering issue in TS nevertheless.
In case I haven't said it this month... it's a pleasure working with you :)
github-actions commentedon Feb 21, 2024
This issue is now closed. Please be mindful that future comments are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.