fix: TWI API idempotency account scope - #208
Merged
Merged
Conversation
ClientRequestToken uniqueness is per account in Amazon DynamoDB, but the Postgres idempotency_tokens table (a single shared data database) keyed on token alone, so tokens from different accounts collided: an identical token was treated as an idempotent replay (dropping another account's write) or a parameter mismatch (rejecting it). Re-key on (account_id, token) via a new data migration and thread the account through the storage trait, matching every other account-scoped table. Global TTL cleanup and the payload fingerprint are unchanged.
Single-account replay/mismatch/independence dual-targeted against Amazon DynamoDB and ExtendDB; cross-account isolation (same token from two accounts) asserted ExtendDB-only, since one AWS account cannot exercise a collision.
Backend-implementer contract now mandates keying idempotency tokens on (account_id, token); storage design doc reflects the real account-scoped schema and fingerprint flow.
yesyayen
requested review from
LeeroyHannigan,
amrith,
c33howard,
jcshepherd and
pdf-amzn
as code owners
July 9, 2026 17:26
pdf-amzn
approved these changes
Jul 15, 2026
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
TransactWriteItemsidempotency tokens were stored keyed on the token alone (PRIMARY KEY (token)). AClientRequestTokenis unique per account in Amazon DynamoDB, but the Postgres data database is shared, so tokens from different accounts collided.Re-key
idempotency_tokenson(account_id, token)(new data migration003) and thread the caller's account from theTransactWriteItemshandler into the token check.Why
Two accounts sharing one token keyspace violates account isolation:
IdempotentParameterMismatchExceptionSingle-account behavior (replay-idempotent, mismatch, independence) is unchanged.
Closes # n/a.
Testing done
tests/test_idempotency_account_scope.py: 3 single-account cases pass against Amazon DynamoDB and ExtendDB; 2 cross-account isolation cases (ExtendDB-only, two accounts) red before, green after.cargo test --workspace,cargo fmt --check,cargo clippy --workspace -- -D warnings: clean. No regressions.Checklist
cargo test --workspace)cargo fmt --check)cargo clippy -- -W clippy::pedantic)Storagetrait, auth model, on-diskformat, or public CLI surface, an RFC has been accepted or is linked
below. Otherwise, an ADR captures the decision (link below).
ADR / RFC: n/a. Refines the internal
DataEngine::transact_write_itemssignature (see below).Breaking changes
DataEngine::transact_write_itemsnow takesOption<IdempotencyKey<'_>>instead ofOption<(&str, &str)>. Backends must scope their idempotency store on(account_id, token). The SQLite backend (feat: SQLite storage backend (in-tree) with developer mode #182) has the same defect and is handled in that PR.003recreatesidempotency_tokens. Apply during stop / migrate / restart; a client retry straddling the migration loses dedup for that short-lived cache.