fix(dynamodb): reset a removed per-GSI on-demand limit instead of silently no-oping - #1433
Merged
Merged
Conversation
…ently no-oping Closes #1423 Removing maxReadRequestUnits / maxWriteRequestUnits from a GlobalTable GSI emitted nothing, so the old ceiling stayed live in AWS forever while cdkd reported success. CloudFormation resets it. This is the absent-field-reset silent-drop class (#1160) one level down inside a nested block (#1225). The blocking unknown was whether -1 is the reset sentinel for the per-GSI Update action: AWS documents it for the TABLE-level OnDemandThroughput only, and reset semantics are field-specific. Settled by a live probe rather than inferred - a real UpdateTable with {-1, -1} on GlobalSecondaryIndexUpdates[].Update was ACCEPTED and DescribeTable then reported the member ABSENT, i.e. genuinely cleared rather than stored as -1. The probe transcript is recorded on the issue. Only the members that were actually SET before are reset; a blanket {-1, -1} would clear a sibling limit the template still declares. Skipped on a billing flip, where absent on-demand fields are just the translation of a PROVISIONED side rather than a template removal. 3 unit tests (2 fail against origin/main; the third is the negative that must NOT emit a reset) plus a new drop-gsi-ondemand-limits integ phase asserting the live DescribeTable readback is ABSENT. Integ PASS, 3 deleted / 0 errors / 0 orphans.
…still dropped Review blocker on the first version: the reset lived in an else-if, so it only fired when the new side had NO OnDemandThroughput at all. The read limit comes from Replicas[local].GlobalSecondaryIndexes[].ReadOnDemandThroughputSettings and the write limit from GSI.WriteOnDemandThroughputSettings - two INDEPENDENT CDK props - so removing just one left the other live in AWS forever. That is the #1423 bug itself, in the likelier user edit, shipped as fixed. Now merges: start from the desired side and fill -1 for every member the template dropped. Live-probed the MIXED payload before adopting it, since only {-1,-1} had been verified: UpdateTable accepted { MaxReadRequestUnits: 50, MaxWriteRequestUnits: -1 } and DescribeTable read back { MaxReadRequestUnits: 50 } - dropped member cleared, kept member preserved. The integ fixture now drops ONLY the write limit (the harder case) and verify.sh asserts write is absent while read is still 50. It also asserts the index EXISTS first: a '| [0]' query against a missing index also answers None, so the absence check alone would pass if byOwner had vanished. The first integ run of that assertion FAILED because the rewritten queries lost the Table. prefix and length() received null - caught by the integ, fixed, re-run clean (182s, 3 deleted / 0 errors / 0 orphans). Table-level and cross-region-replica on-demand ceilings have the same class and are filed as #1434; each needs its own live probe and the table-level one touches a different code path.
github-actions Bot
pushed a commit
that referenced
this pull request
Aug 9, 2026
## [0.278.15](v0.278.14...v0.278.15) (2026-08-09) ### Bug Fixes * **dynamodb:** reset a removed per-GSI on-demand limit instead of silently no-oping ([#1433](#1433)) ([0e10091](0e10091))
|
🎉 This PR is included in version 0.278.15 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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.
Closes #1423
Summary
Removing
maxReadRequestUnits/maxWriteRequestUnitsfrom aAWS::DynamoDB::GlobalTableGSI emitted nothing at all, so the old ceilingstayed live in AWS forever while cdkd reported success. CloudFormation resets
it. This is the absent-field-reset silent-drop class (#1160), one level down
inside a nested block (#1225).
The blocking unknown, settled by a live probe
AWS documents
-1as "reset to default" for the table-levelOnDemandThroughput, and says nothing about the per-GSIGlobalSecondaryIndexUpdates[].Update.OnDemandThroughput. Reset semantics arefield-specific, so this was probed rather than inferred (throwaway table,
deleted afterwards, no orphans):
So
-1is accepted and the limit is genuinely cleared — it reads back asabsence, never as
-1. The full transcript is recorded on the issue foranyone auditing the choice later.
Review fix-back (blocker)
The first version put the reset in an
else if, so it only fired when the newside had no
OnDemandThroughputat all. The read limit comes fromReplicas[local].GlobalSecondaryIndexes[].ReadOnDemandThroughputSettingsandthe write limit from
GSI.WriteOnDemandThroughputSettings— two independentCDK props — so removing just one left the other live in AWS forever. That is the
#1423 bug itself, in the likelier user edit, shipped as fixed. My unit test
missed it because its
previousonly had one member set.Now it merges: start from the desired side, fill
-1for every member thetemplate dropped. The MIXED payload was live-probed before adopting it, since
only
{-1, -1}had been verified:Dropped member cleared, kept member preserved.
Implementation notes
{-1, -1}would clear a sibling limit the template still declares.translation of a
PROVISIONEDside, not a template removal.verification and the read-back-is-absence caveat, so a future drift /
read-back comparison does not expect
-1.Test plan
Four unit tests: the full reset, the partial removal (keep read, reset
write) the review caught, the reset-only-what-was-set case, and the negative
that must NOT emit a reset when the index simply never had limits.
Real AWS:
dynamodb-globaltablegained adrop-gsi-ondemand-limitsupdatephase that drops only the write limit — the harder case — and
verify.shasserts the write member is absent while the read one is still 50. It also
asserts the index EXISTS first: a
| [0]query against a missing index alsoanswers
None, so the absence check alone would pass ifbyOwnerhad vanished.The first run of that assertion FAILED: the rewritten queries had lost the
Table.prefix solength()received null. Caught by the integ, fixed, re-runclean — 182s, 3 deleted / 0 errors / 0 orphans. Full local gate: typecheck, lint,
build, 523 files / 8966 tests,
vp run gen:all-matricesclean.Not in scope
Filed as #1434: the table-level on-demand ceiling and the cross-region
replica
OnDemandThroughputOverridehave the SAME absent-field-reset class.Each needs its own live probe (reset semantics are field-specific) and the
table-level one touches a different code path (
flatUpdate/flatChanged)than the per-GSI loop this PR reworked.
Also still open on #1423 itself: the non-array
GlobalSecondaryIndexesinconsistency also noted on #1423(
toSdkGlobalSecondaryIndexesreturns[]andcreate()drops it, while theper-ENTRY policy two lines down deliberately passes a malformed entry through
so AWS reports the real error). Changing it means sending a known-bad shape to
AWS — a behavior change that wants its own test rather than a rider on a reset
fix. #1423 stays open for it.