Skip to content

AWS::DynamoDB::GlobalTable: the per-GSI on-demand reset fires on a present-but-unresolvable value, silently clearing the ceiling #1440

Description

@go-to-k

Found by the code review of PR #1439 (the table-level sibling of this class).

DynamoDBGlobalTableProvider's per-GSI on-demand reset — the #1423 fix — decides
"the template dropped this member" from whether the value COERCED, not from
whether the key is present:

const maxWrite = toFiniteNumber(
  asRecord(gsi['WriteOnDemandThroughputSettings'])?.['MaxWriteRequestUnits']
);
...
if (previousOnDemand.MaxWriteRequestUnits !== undefined &&
    onDemand.MaxWriteRequestUnits === undefined) {
  onDemand.MaxWriteRequestUnits = ON_DEMAND_LIMIT_RESET; // -1
}

toFiniteNumber returns undefined for a present-but-unparseable value — an
unresolved intrinsic ({Ref: ...}), an empty string, an object. Such a GSI
therefore falls into the reset branch and is sent -1, which silently CLEARS
the ceiling the template was trying to set
.

That is the same silent-wrong-action class the reset exists to remove: the user
asked for a value, and cdkd answered by deleting the setting, with the deploy
reporting success.

Why PR #1439 did not fix it here

The identical hazard existed in that PR's own table-level code and was caught in
self-review; the table-level gate now tests RAW key presence
(rawMaxWrite !== undefined) and leaves the coercion untouched, so an
unresolvable value still reaches AWS and fails loudly.

Applying the same shape per-GSI is more invasive: the modified loop iterates
the SDK-TRANSLATED index shapes, so the raw CFn blob for that index (and the
matching local-replica entry, where the read half lives) has to be threaded in
or re-looked-up by IndexName before the presence test can be made. That is
surgery on #1423's shipped logic rather than a one-line gate change, and #1439
was already carrying a live-probe cycle plus a real-AWS integ.

Fix

In the per-GSI modified loop, gate each member's reset on the RAW CFn key
being ABSENT:

  • write half: GlobalSecondaryIndexes[?IndexName==X].WriteOnDemandThroughputSettings.MaxWriteRequestUnits
  • read half: Replicas[local].GlobalSecondaryIndexes[?IndexName==X].ReadOnDemandThroughputSettings.MaxReadRequestUnits
    (with the GSI-level spelling as the documented fallback)

Keep the existing coercion for the present case so an unresolvable value still
surfaces as an AWS-side error.

Tests

Mirror the table-level regression test added in #1439 — a per-GSI
MaxWriteRequestUnits: { Ref: 'SomeUnresolvedParameter' } must NOT produce
-1, and must still forward the value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions