Skip to content

Bug: event mutations influence idempotency hash used #1093

Closed
@ojongerius

Description

@ojongerius

Expected Behaviour

A function that (inadvertently) updates the event it has received should execute successfully, and have one idempotency document written.

Current Behaviour

A function that (inadvertently) updates the event it has received executes successfully, but has two documents corresponding to it's execution:

  • An initial document with a hashed idempotency key based on the original payload that will be stuck in INPROGRESS and has no return data
  • A second document with a hashed idempotency key based on the mutated payload with status COMPLETED and the correct return value

Code snippet

@pytest.mark.parametrize("idempotency_config", [{"use_local_cache": False}, {"use_local_cache": True}], indirect=True)
def test_idempotent_lambda_first_execution_event_mutation(
    idempotency_config: IdempotencyConfig,
    persistence_store: DynamoDBPersistenceLayer,
    lambda_apigw_event,
    expected_params_update_item,
    expected_params_put_item,
    lambda_response,
    serialized_lambda_response,
    deserialized_lambda_response,
    hashed_idempotency_key,
    lambda_context,
):
    """
    Test idempotent decorator where lambda_handler mutates the event
    """

    stubber = stub.Stubber(persistence_store.table.meta.client)
    ddb_response = {}

    stubber.add_response("put_item", ddb_response, expected_params_put_item)
    stubber.add_response("update_item", ddb_response, expected_params_update_item)
    stubber.activate()

    @idempotent(config=idempotency_config, persistence_store=persistence_store)
    def lambda_handler(event, context):
        event.popitem() # <- 💣
        return lambda_response

    lambda_handler(lambda_apigw_event, lambda_context)

    stubber.assert_no_pending_responses()
    stubber.deactivate()

Possible Solution

A few options:

  • Create the idempotency key on entry and save it (in say self.idempotency_key)
  • Use copy.deepcopy() when saving self.data which forces a pass by value and gets the result of hash function stable

The former solution would be more performant, the latter less code.

Steps to Reproduce

Add the idempotency decorator to any lambda function, update the event data, ie: event.popitem() and observe that 2 DDB documents were written of which one is stuck in INPROGRESS until it's TTLed

AWS Lambda Powertools for Python version

latest

AWS Lambda function runtime

{"label"=>"3.8"}

Packaging format used

{"label"=>"PyPi"}

Debugging logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions