Skip to content

fix(idempotency): retry Redis inconsistent state errors - #8736

Merged
leandrodamascena merged 2 commits into
aws-powertools:developfrom
coughlmao:fix/idempotency-redis-inconsistent-state-retry
Nov 14, 2026
Merged

leandrodamascena merged 2 commits into
aws-powertools:developfrom
coughlmao:fix/idempotency-redis-inconsistent-state-retry

Conversation

@coughlmao

Copy link
Copy Markdown
Contributor

Issue number: closes #8727

Summary

Fix a race condition in the Redis idempotency persistence layer where an existing record expires between a failed SET NX operation and the subsequent GET.

Previously, this caused an IdempotencyItemNotFoundError to escape and be wrapped as an IdempotencyPersistenceLayerError, failing the Lambda invocation instead of retrying.

Changes

  • Catch IdempotencyItemNotFoundError when retrieving a record after a failed Redis SET NX.
  • Raise IdempotencyInconsistentStateError to signal the recoverable inconsistent state.
  • Ensure the existing idempotency retry mechanism can retry the operation.
  • Add regression tests covering the disappearing-record race condition, exception chaining, and successful retry through the public idempotency decorator.

User experience

Before: If an existing Redis idempotency record expired between SET NX and GET, the invocation failed with a persistence-layer error.

After: The Redis persistence layer raises IdempotencyInconsistentStateError, allowing the existing retry mechanism to retry the operation. The invocation succeeds when the subsequent attempt acquires the lock successfully.

Verification

Tests and checks run locally:

  • uv run --locked pytest tests/functional/idempotency/_redis/test_redis_layer.py -k inconsistent_state -v — 3 passed.
  • uv run --locked pytest tests/functional/idempotency/_redis/test_redis_layer.py -q — 33 passed.
  • Idempotency functional tests — 203 passed.
  • uv run --locked ruff check aws_lambda_powertools/utilities/idempotency/base.py aws_lambda_powertools/utilities/idempotency/persistence/redis.py tests/functional/idempotency/_redis/test_redis_layer.py — passed.
  • uv run --locked mypy aws_lambda_powertools/utilities/idempotency/base.py aws_lambda_powertools/utilities/idempotency/persistence/redis.py — passed.
  • git diff --check — passed.
  • Git pre-commit hooks — all passed.

The regression scenario was verified using a mocked Redis client. Real Redis service validation has not been performed.

This contribution was developed with AI assistance. I reviewed the changes and personally ran the tests and checks listed above.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

@coughlmao
coughlmao requested a review from a team as a code owner November 1, 2026 15:56
@coughlmao
coughlmao requested a review from hjgraca November 1, 2026 15:56
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Nov 1, 2026
@boring-cyborg boring-cyborg Bot added the tests label Nov 1, 2026
@boring-cyborg

boring-cyborg Bot commented Nov 1, 2026

Copy link
Copy Markdown

Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

@leandrodamascena leandrodamascena added this to the 3.41.0 milestone Nov 1, 2026
@leandrodamascena

Copy link
Copy Markdown
Contributor

Thanks for the PR and for filling in the verification section, that helps. Heads up that the review may take a few days: this goes into 3.41.0 and I review every PR against a real service before approving, one at a time. No need to rebase or ping in the meantime, I'll come back here.

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @coughlmao! I agree with the approach, the retry logic was already there, it just never saw this error. I tried to reproduce the race against a real Valkey and couldn't, it's too tight a window, so your test is what covers it. I did run the normal flow against the real server with your change and everything still works. Goes out in 3.41.0, no need to rebase, you'll be in the release notes.

@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.17%. Comparing base (58197b4) to head (df5b08d).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #8736   +/-   ##
========================================
  Coverage    97.17%   97.17%           
========================================
  Files          323      323           
  Lines        16588    16591    +3     
  Branches      1502     1502           
========================================
+ Hits         16119    16122    +3     
  Misses         328      328           
  Partials       141      141           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@leandrodamascena
leandrodamascena merged commit d36ecdd into aws-powertools:develop Nov 14, 2026
14 checks passed
@boring-cyborg

boring-cyborg Bot commented Nov 14, 2026

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

@powertools-for-aws-oss-automation

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Denotes a PR that changes 30-99 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Redis layer fails the invocation instead of retrying when a record expires between SET NX and GET

2 participants