Found by the live CloudFormation probe run for #1427 (see that comment for the full probe record).
Summary
deriveReadCapacityUnits / deriveWriteCapacityUnits in src/provisioning/providers/dynamodb-globaltable-provider.ts both end with:
return toFiniteNumber(autoScaling['SeedCapacity']) ?? toFiniteNumber(autoScaling['MinCapacity']);
SeedCapacity first. CloudFormation uses MinCapacity on a fresh CREATE, so cdkd over-provisions any autoscaled PROVISIONED GlobalTable by the seed-to-min ratio. It is a billing symptom, not an error: the deploy succeeds and the table is simply more expensive than the same template under CloudFormation.
Evidence (real AWS, us-east-1)
Stack CdkdIssue1427Control, a AWS::DynamoDB::GlobalTable with BillingMode: PROVISIONED and, at table level and on the GSI:
WriteProvisionedThroughputSettings:
WriteCapacityAutoScalingSettings:
MinCapacity: 1
MaxCapacity: 30
SeedCapacity: 20
TargetTrackingScalingPolicyConfiguration:
TargetValue: 70
CREATE_COMPLETE, then dynamodb describe-table:
{"Table": {"ReadCapacityUnits": 5, "WriteCapacityUnits": 1, "NumberOfDecreasesToday": 0},
"GSI": [{"Name": "gsi1", "PT": {"ReadCapacityUnits": 5, "WriteCapacityUnits": 1, "NumberOfDecreasesToday": 0}}]}
CloudFormation created at 1 (MinCapacity), not 20 (SeedCapacity). cdkd's helpers return 20 for the same input.
NumberOfDecreasesToday: 0 rules out an auto-scaling scale-down between create and read-back, and application-autoscaling describe-scalable-targets confirms Min: 1 / Max: 30 registered on both dynamodb:table:WriteCapacityUnits and dynamodb:index:WriteCapacityUnits.
Why CloudFormation is right here
The CapacityAutoScalingSettings docs scope SeedCapacity to the billing-mode transition, not to a fresh provisioned create:
When switching billing mode from PAY_PER_REQUEST to PROVISIONED, DynamoDB requires you to specify read and write capacity unit values for the table and for each global secondary index. [...] The table will use these provisioned values until CloudFormation creates the autoscaling policies you configured in your template.
You must also specify a value for SeedCapacity when you plan to switch a table's billing mode from PROVISIONED to PAY_PER_REQUEST, because CloudFormation might need to roll back the operation [...]
MinCapacity / MaxCapacity are Required: Yes; SeedCapacity is Required: No. So the correct precedence is context-dependent, not a fixed ?? chain:
- fresh
CREATE of a PROVISIONED table -> MinCapacity
PAY_PER_REQUEST -> PROVISIONED flip -> SeedCapacity (falling back to MinCapacity when absent)
Do NOT fix this in isolation
Flipping the order alone makes things worse. Per #1419, create() registers no Application Auto Scaling target at all, so a table created at MinCapacity would be pinned at MinCapacity forever with no policy to scale it up — strictly worse than today's over-provision. Today's SeedCapacity-first behavior is accidentally acting as a safety margin for the missing registration.
The two belong in one change: register the scaling targets/policies on create() (#1419) and seed the initial capacity from MinCapacity, so the table starts where CloudFormation starts it and the policy takes over from there.
Scope
Related
Found by the live CloudFormation probe run for #1427 (see that comment for the full probe record).
Summary
deriveReadCapacityUnits/deriveWriteCapacityUnitsinsrc/provisioning/providers/dynamodb-globaltable-provider.tsboth end with:SeedCapacityfirst. CloudFormation usesMinCapacityon a freshCREATE, so cdkd over-provisions any autoscaled PROVISIONED GlobalTable by the seed-to-min ratio. It is a billing symptom, not an error: the deploy succeeds and the table is simply more expensive than the same template under CloudFormation.Evidence (real AWS, us-east-1)
Stack
CdkdIssue1427Control, aAWS::DynamoDB::GlobalTablewithBillingMode: PROVISIONEDand, at table level and on the GSI:CREATE_COMPLETE, thendynamodb describe-table:{"Table": {"ReadCapacityUnits": 5, "WriteCapacityUnits": 1, "NumberOfDecreasesToday": 0}, "GSI": [{"Name": "gsi1", "PT": {"ReadCapacityUnits": 5, "WriteCapacityUnits": 1, "NumberOfDecreasesToday": 0}}]}CloudFormation created at 1 (
MinCapacity), not 20 (SeedCapacity). cdkd's helpers return 20 for the same input.NumberOfDecreasesToday: 0rules out an auto-scaling scale-down between create and read-back, andapplication-autoscaling describe-scalable-targetsconfirmsMin: 1 / Max: 30registered on bothdynamodb:table:WriteCapacityUnitsanddynamodb:index:WriteCapacityUnits.Why CloudFormation is right here
The
CapacityAutoScalingSettingsdocs scopeSeedCapacityto the billing-mode transition, not to a fresh provisioned create:MinCapacity/MaxCapacityareRequired: Yes;SeedCapacityisRequired: No. So the correct precedence is context-dependent, not a fixed??chain:CREATEof a PROVISIONED table ->MinCapacityPAY_PER_REQUEST->PROVISIONEDflip ->SeedCapacity(falling back toMinCapacitywhen absent)Do NOT fix this in isolation
Flipping the order alone makes things worse. Per #1419,
create()registers no Application Auto Scaling target at all, so a table created atMinCapacitywould be pinned atMinCapacityforever with no policy to scale it up — strictly worse than today's over-provision. Today'sSeedCapacity-first behavior is accidentally acting as a safety margin for the missing registration.The two belong in one change: register the scaling targets/policies on
create()(#1419) and seed the initial capacity fromMinCapacity, so the table starts where CloudFormation starts it and the policy takes over from there.Scope
MinCapacityon create,SeedCapacity ?? MinCapacityon the billing-mode flip.dynamodb-globaltableinteg to assert the CREATED capacity equalsMinCapacity(the current fixture asserts the seed value, so it would need updating in lockstep with AWS::DynamoDB::GlobalTable: per-GSI auto-scaling never registered (and create() registers no auto-scaling at all) #1419).Related
AWS::DynamoDB::GlobalTable: GSIWrite/ReadProvisionedThroughputSettings/ on-demand settings not translated to SDK shape — PROVISIONED GSI create fails, TableV2 per-GSI limits silently dropped #1387 / fix(dynamodb): translate GlobalTable GSI throughput to the CreateTable SDK shape #1422 — introduced theSeedCapacity ?? MinCapacityderivation.