fix: honour TableThroughputMode and emit TableThroughputModeSummary - #320
Open
yesyayen wants to merge 2 commits into
Open
fix: honour TableThroughputMode and emit TableThroughputModeSummary#320yesyayen wants to merge 2 commits into
yesyayen wants to merge 2 commits into
Conversation
Dual-target coverage for the TableThroughputMode request member on CreateTable and UpdateTable (fallback alias of BillingMode, per-member enum validation, BillingMode precedence when both are present) and for TableThroughputModeSummary mirroring BillingModeSummary in table descriptions. Measured against Amazon DynamoDB; passes there, fails on ExtendDB before the fix.
Amazon DynamoDB accepts TableThroughputMode on CreateTable and UpdateTable as a fallback alias of BillingMode: BillingMode wins when both members are present (a conflict is not refused), enum validation is per member and pre-semantic, and every downstream message phrases the mode as BillingMode. Measured 2026-08-21; the earlier decision to ignore the member was based on the SDK model, which lags the service. Table descriptions now also carry TableThroughputModeSummary, an exact mirror of BillingModeSummary (same mode, identical timestamp, emitted together or not at all), populated centrally in the engine so both members cannot disagree and every backend inherits it.
yesyayen
marked this pull request as ready for review
August 27, 2026 16:40
yesyayen
requested review from
LeeroyHannigan,
amrith,
c33howard,
jcshepherd,
pdf-amzn and
robinnsc
as code owners
August 27, 2026 16:40
| return resp.json() | ||
|
|
||
|
|
||
| def _wait_active_raw(name: str, timeout: float = 120.0) -> None: |
Collaborator
There was a problem hiding this comment.
Should-fix The 120s budget is too short when this suite runs against real DynamoDB: billing mode switches can take several minutes to return to ACTIVE, so the three UpdateTable mode-switch tests time out there. All three pass with a 900s budget. Bump the default, or scale it up when EXTENDDB_TEST_ENDPOINT is unset.
LeeroyHannigan
approved these changes
Aug 28, 2026
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
Amazon DynamoDB accepts
TableThroughputModeas a request member onCreateTableandUpdateTableand emitsTableThroughputModeSummaryin table descriptions. ExtendDB ignored the request member (so aCreateTablecarrying onlyTableThroughputMode: PAY_PER_REQUESTfell through to the PROVISIONED default and failed on missing throughput) and never emitted the response member. Both members are absent from the public SDK models (checked at aws-sdk-dynamodb 1.119.0 and botocore 1.33.13), but the service honours them; this was measured directly against Amazon DynamoDB with raw signed requests (2026-08-21).Request side, implemented exactly as measured:
TableThroughputModeis a fallback alias ofBillingMode: when both members are present,BillingModedecides and the other member is ignored. A conflict is not refused. Resolution happens once inextenddb-core(resolve_table_throughput_mode(), which consumes the member), so all existing billing-mode validation and both storage backends run unchanged and every downstream message phrases the mode asBillingMode, matching the service.tableThroughputModeeven when a validBillingModewould win, before required-member validation and before the table lookup onUpdateTable. Multiple invalid members join underN validation errors detected:withbillingModefirst.UpdateTablegains this pre-lookup enum validation for both members, expressed through the same typed enum-field specs the other handlers use.Response side:
TableDescriptiongainsTableThroughputModeSummary, an exact mirror ofBillingModeSummary(present if and only if the sibling is present, same mode, identicalLastUpdateToPayPerRequestDateTime). The mirror is derivedFrom<&BillingModeSummary>and populated centrally in the engine on every path that emits a description (CreateTable,DescribeTable,UpdateTable,DeleteTable,RestoreTableFromBackup), so the two members cannot disagree and every backend inherits it; backends only gained a mechanicalNonefield.This reverses a deliberate earlier decision: two unit tests asserted the member must be ignored because it is not in the SDK model. The original decision was sound and the SDK model lagged the service. The tests are inverted with the measurement dated in the comment so the member is not re-removed on SDK-model grounds.
Behavior: today vs Amazon DynamoDB
Captured from Amazon DynamoDB via raw SigV4-signed JSON (the SDKs cannot express these requests):
CreateTablewith onlyTableThroughputMode: PAY_PER_REQUESTValidationException: No provisioned throughput specified for the table200, table is PAY_PER_REQUESTCreateTablewithBillingMode: PROVISIONED+ throughput +TableThroughputMode: PAY_PER_REQUEST200, PROVISIONED (member ignored)200, PROVISIONED (BillingModewins; conflict not refused)CreateTablewithTableThroughputMode: PROVISIONED, no throughputValidationException(defaulted-PROVISIONED path)ValidationExceptionphrased asBillingMode:ReadCapacityUnits and WriteCapacityUnits must both be specified when BillingMode is PROVISIONEDCreateTablewithTableThroughputMode: BOGUSValidationException: 1 validation error detected: Value 'BOGUS' at 'tableThroughputMode' failed to satisfy constraint: Member must satisfy enum value set: [PROVISIONED, PAY_PER_REQUEST]CreateTablewith both members invalidbillingModeerror only2 validation errors detected: ... 'billingMode' ...; ... 'tableThroughputMode' ...UpdateTableswitching mode viaTableThroughputModealoneValidationException: At least one of ... must be specified200, mode switchesUpdateTablewithTableThroughputMode: BOGUSon a missing tableValidationExceptionattableThroughputMode(fires before the table lookup)DescribeTableon a PAY_PER_REQUEST tableBillingModeSummaryonlyBillingModeSummaryandTableThroughputModeSummary, same mode, identical timestampWhy
Fix conformance gaps: an accept-direction divergence (the service accepts and honours a request shape ExtendDB refuses) plus a missing response member emitted by the service on every table description.
Testing done
The branch was rebased onto current main after the vector-search backend PRs and the enum-violation-shape work merged; the enum validation was re-expressed through the typed field specs introduced there, with the original wire captures as the oracle, and all gates below were re-run at the rebased tip.
tests/test_table_throughput_mode.py: 18 dual-target tests covering the request matrixChecklist
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 (additive wire members matching Amazon DynamoDB; no trait, auth, on-disk, or CLI change)
By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache License 2.0 and I agree to the Developer Certificate of
Origin (DCO). See CONTRIBUTING.md for details.