fix(idempotency): resolve tech debt issues with falsy responses and Redis persistency#8176
Conversation
leandrodamascena
left a comment
There was a problem hiding this comment.
Hey @hirenkumar-n-dholariya, thanks for working on this and submitting the PR!
The Redis str(None) fix is good. Removing the str() wrapper is the right fix.
However, I have some concerns with the rest of the PR, and there are missing items from the issue.
Missing items from the issue
In your comment on #8090 you mentioned that items 1 and 2 "appear already resolved", but they are still present in the codebase:
Item 1 - Falsy response handling is still in base.py:299:
serialized_response: dict = self.output_serializer.to_dict(response) if response else NoneThis silently discards valid falsy return values like 0, False, "", [], {}. It should be if response is not None. This is a real bug that affects customers.
Item 2 - "INPROGRESS" string literal is still in base.py:170:
if is_replay and record is not None and record.status == "INPROGRESS":Should use STATUS_CONSTANTS["INPROGRESS"] for consistency with the rest of the code.
Helper method
The helper _get_idempotency_key_or_return_none() doesn't actually reduce the duplication. After the refactor, each caller still does:
idempotency_key = self._get_idempotency_key_or_return_none(data=data)
if idempotency_key is None:
return NoneWe moved one line into a wrapper but the if None: return is still in all 4 methods. The helper is a passthrough that adds indirection without simplifying anything. Let's drop this part and keep the original inline pattern, it's 3 lines and every Python developer reads it instantly.
Tests
The tests need a full rework. Please don't create a new test file for this.
We already have test_item_to_datarecord_conversion in tests/functional/idempotency/_redis/test_redis_layer.py (line 318) that tests exactly _item_to_data_record(). Add your test case there, next to the existing one. That's our pattern: tests go in the existing test files, close to related tests.
What's wrong with the current tests:
- Don't test Python builtins.
test_str_none_produces_wrong_stringasserts thatstr(None) == "None"andtest_redis_missing_data_attr_returns_none_not_stringasserts thatdict.get()returnsNone. These are testing the Python language, not our code. - Don't mock internal methods with MagicMock. We don't do that in idempotency tests. Look at the existing tests in
_redis/test_redis_layer.pyand_boto3/test_idempotency.pyfor the patterns we use. We test through the actual classes with fixtures, not by patching_get_hashed_idempotency_keywith a MagicMock. ConcretePersistenceLayeris defined 5 times instead of once.- 208 lines of tests for a 2-line fix is too much surface area. A single test in the existing file that calls
_item_to_data_record()with missing attributes and assertsresponse_data is None(not"None") would cover the Redis fix properly.
Suggestion
Let's scope this PR to fix all 4 items from the issue:
- Fix
if response else Nonetoif response is not None(item 1 - bug fix) - Fix
"INPROGRESS"string literal to useSTATUS_CONSTANTS(item 2 - consistency) - Fix
str(None)in Redis (item 3 - already done, good) - Drop the helper method extraction (item 4 - the current approach doesn't improve things)
Add tests in the existing test files following our current patterns. Also pls fix the issues in SonarCloud.
Thanks again for contributing!
Hey @leandrodamascena, I have addressed all the feedback:
Thanks again for taking the time to explain the patterns and reasoning behind each point. It really helps understand the project's conventions better. Please let me know if anything else needs adjusting. |
…istence Replace str(item.get(...)) with item.get(...) to avoid storing the string "None" when a value is missing from Redis hash map. When data_attr or validation_key_attr is missing from Redis, item.get() returns None. Wrapping with str() converts it to the string "None" which is incorrect. Now correctly returns None. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
…into helper method Replace 4 identical null-check blocks across save_success, save_inprogress, delete_record, and get_record with a single helper method _get_idempotency_key_or_return_none() to reduce code duplication. The helper encapsulates the pattern of calling _get_hashed_idempotency_key() and returning None early if the key is None, keeping each method cleaner and easier to read. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
…wertools#8090 Fix 1 - str(None) in Redis _item_to_data_record: - Missing data_attr returns None not string "None" - Existing data_attr returns value correctly - Demonstrates old bug vs new correct behavior Fix 2 - _get_idempotency_key_or_return_none helper: - Returns None when key is None - Returns key string when key exists - Correctly used in save_success, save_inprogress, delete_record, and get_record (all return None early) Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
… constant
Fix 1 - Falsy response handling in _get_function_response(): Replace `if response else None` with `if response is not None else None`. So valid falsy return values (0, False, {}, [], "") are correctly serialized and stored instead of being silently discarded.
Fix 2 - Inconsistent status constant in _process_idempotency():
Replace string literal "INPROGRESS" with STATUS_CONSTANTS["INPROGRESS"] for consistency with the rest of the codebase.
Part of aws-powertools#8090
Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
… null-check pattern Per Leandro Damascena's feedback: _get_idempotency_key_or_return_none() helper added indirection without reducing duplication since the if None: return None check remained in all 4 callers anyway. Restored original inline 3-line pattern in save_success, save_inprogress, delete_record, and get_record which is clearer and instantly readable. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
Tests should be added to existing test files following established patterns, not in new standalone files. The Redis fix will be tested in _redis/test_redis_layer.py next to the existing test_item_to_datarecord_conversion. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
…data_record Added single test next to existing test_item_to_datarecord_conversion to verify missing Redis attributes return None instead of string "None". Follows existing test patterns using fixtures instead of MagicMock. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
e78eeb2 to
01f565a
Compare
… base.py Remove trailing whitespace on blank line between is_missing_idempotency_key and _get_hashed_payload methods to pass ruff format check. Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
…st_redis_layer.py - Fix fixture name from persistence_store_redis to persistence_store_standalone_redis to match existing fixture defined in the test file - Remove trailing whitespace on blank line after test function to pass ruff format check Part of aws-powertools#8090 Signed-off-by: hirenkumar-n-dholariya <hirenkumarnd@gmail.com>
|
Hey @leandrodamascena, I've addressed all the feedback from your review:
All SonarCloud checks are passing. Could you please approve the workflows and take another look when you get a chance? Thank you! |
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #8176 +/- ##
========================================
Coverage 96.72% 96.72%
========================================
Files 286 286
Lines 14341 14341
Branches 1200 1200
========================================
Hits 13871 13871
Misses 341 341
Partials 129 129 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
leandrodamascena
left a comment
There was a problem hiding this comment.
Thanks for working on this @hirenkumar-n-dholariya! Approved.
|
Thank you @leandrodamascena; sincerely appreciate your suggestions, feedback and help to merge this! |



Issue number: fixes #8090
Summary
Fixes all 4 tech debt issues found during code review of the idempotency utility.
Changes
aws_lambda_powertools/utilities/idempotency/base.pyif response else None→if response is not None else Noneso valid falsy return values (
0,False,{},[],"") are correctly serialized and stored instead of being silently discarded on replay"INPROGRESS"→STATUS_CONSTANTS["INPROGRESS"]for consistency with the rest of the codebaseaws_lambda_powertools/utilities/idempotency/persistence/redis.pystr(None)producing the string"None"in_item_to_data_record()whendata_attrorvalidation_key_attris missing from RedisUser experience
Before: Functions returning falsy values (
0,False,{}) would have their response silently discarded and re-executed on replay instead of returning the cached value.After: All non-
Nonereturn values including falsy ones are correctly cached and returned on replay.Tests
Added one regression test in
tests/functional/idempotency/_redis/test_redis_layer.pynext to existingtest_item_to_datarecord_conversion, verifying missing Redis attributes returnNoneinstead of the string"None".Acknowledgment
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.