(dynamo-db): Error when adding a new GSI with auto-scaling to an existing DynamoDB table with more than one replica regions via CDK
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
Creating this new issue to continue the discussion in [this closed issue](https://github.com/aws/aws-cdk/issues/19083).
Thank you for the detailed investigation done earlier [in this comment](https://github.com/aws/aws-cdk/issues/19083#issuecomment-1072295230).
I respectfully disagree with [this comment](https://github.com/aws/aws-cdk/issues/19083#issuecomment-1081273369) because I was able to add a new GSI with auto-scaling settings to an existing DynamoDB table with one or more replica regions using ["AWS::DynamoDB::GlobalTable"](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html) or [CfnGlobalTable](https://docs.aws.amazon.com/cdk/api/v2/java/software/amazon/awscdk/services/dynamodb/CfnGlobalTable.html). I think the current behavior by [Table](https://docs.aws.amazon.com/cdk/api/v2/java/software/amazon/awscdk/services/dynamodb/Table.html) is a bug.
### Expected Behavior
Step 1- Create a new DynamoDB table with one or more replica regions, provisioned capacity mode, and auto-scaling enabled
Step 2- Update the existing DynamoDB table with a new GSI which has its own auto-scaling settings should not fail
### Current Behavior
Scenario 1 - success
Step 1: Create a table with one or more replica regions, and also a GSI with its own auto-scaling settings
Scenario 2 - success
Step 1: Create a table with **only one** replica region
Step 2: Update the existing table by adding a GSI with its own auto-scaling settings
Scenario 3 - success
Step 1: Create a table with **only one** replica region
Step 2: Update the existing table by adding a GSI with its own auto-scaling settings
Step 3: Update the table by adding another replica region
Scenario 4 - fail
Step 1: Create a table with **only one** replica region
Step 2: Update the existing table by adding a GSI with its own auto-scaling settings
Step 3: Update the table by adding another replica region
Step 4: Update the existing table by adding the second GSI with its own auto-scaling settings to this existing table with two replica regions
Error message:
`table//index/|dynamodb:index:WriteCapacityUnits|dynamodb already exists`
Scenario 5 - success
Step 1: Create a table with **more than one** replica regions
Step 2: Update the existing table by adding a GSI **without** its own auto-scaling settings (inherits the auto-scaling settings of the table)
Scenario 6 - fail
Step 1: Create a table with **more than one** replica regions
Step 2: Update the existing table by adding a GSI **without** its own auto-scaling settings (inherits the auto-scaling settings of the table)
Step 3: Update the existing GSI with its own auto-scaling settings
Error message:
`table//index/|dynamodb:index:WriteCapacityUnits|dynamodb already exists`
### Reproduction Steps
Step 1- Create a new DynamoDB table with two replica regions, provisioned capacity mode, and auto-scaling enabled using the code below
Step 2- Update the existing DynamoDB table with a new GSI which has its own auto-scaling settings by removing the commented code for step 2
```
import software.amazon.awscdk.services.dynamodb.*;
import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.dynamodb.*;
import java.util.List;
public class CdkStack extends Stack {
public CdkStack(final Construct scope, final String id) {
this(scope, id, null);
}
public CdkStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
// create DynamoDB table - step 1
Table globalTable = Table.Builder.create(this, "TestTable")
.tableName("TestTable")
.partitionKey(Attribute.builder().name("pk").type(AttributeType.STRING).build())
.sortKey(Attribute.builder().name("sk").type(AttributeType.STRING).build())
// test with one or more replica regions
.replicationRegions(List.of("eu-north-1", "eu-west-1"))
.billingMode(BillingMode.PROVISIONED)
.build();
globalTable.autoScaleWriteCapacity(EnableScalingProps.builder()
.minCapacity(10)
.maxCapacity(50)
.build())
.scaleOnUtilization(UtilizationScalingProps.builder()
.targetUtilizationPercent(75)
.build());
globalTable.autoScaleReadCapacity(EnableScalingProps.builder()
.minCapacity(10)
.maxCapacity(50)
.build())
.scaleOnUtilization(UtilizationScalingProps.builder()
.targetUtilizationPercent(70)
.build());
// Optional
// to show that it works to create a GSI to a table with one or more replica regions during the table creation
// adding a GSI with its own auto-scaling settings
GlobalSecondaryIndexProps globalSecondaryIndexProps = GlobalSecondaryIndexProps.builder()
.indexName("TestIndex1")
.partitionKey(Attribute.builder().name("sk").type(AttributeType.STRING).build())
.sortKey(Attribute.builder().name("pk").type(AttributeType.STRING).build())
.projectionType(ProjectionType.KEYS_ONLY)
.build();
globalTable.addGlobalSecondaryIndex(globalSecondaryIndexProps);
globalTable.autoScaleGlobalSecondaryIndexReadCapacity("TestIndex1", EnableScalingProps.builder()
.minCapacity(5)
.maxCapacity(50)
.build())
.scaleOnUtilization(UtilizationScalingProps.builder()
.targetUtilizationPercent(75)
.build());
globalTable.autoScaleGlobalSecondaryIndexWriteCapacity("TestIndex1",EnableScalingProps.builder()
.minCapacity(5)
.maxCapacity(50)
.build())
.scaleOnUtilization(UtilizationScalingProps.builder()
.targetUtilizationPercent(75)
.build());
/*
// Step 2 - adding a GSI to the existing table
// this step fails when the table has more than one replica region
GlobalSecondaryIndexProps globalSecondaryIndexProps2 = GlobalSecondaryIndexProps.builder()
.indexName("TestIndex2")
.partitionKey(Attribute.builder().name("sk").type(AttributeType.STRING).build())
.sortKey(Attribute.builder().name("pk").type(AttributeType.STRING).build())
.projectionType(ProjectionType.KEYS_ONLY)
.build();
globalTable.addGlobalSecondaryIndex(globalSecondaryIndexProps2);
globalTable.autoScaleGlobalSecondaryIndexReadCapacity("TestIndex2", EnableScalingProps.builder()
.minCapacity(5)
.maxCapacity(70)
.build())
.scaleOnUtilization(UtilizationScalingProps.builder()
.targetUtilizationPercent(80)
.build());
globalTable.autoScaleGlobalSecondaryIndexWriteCapacity("TestIndex2",EnableScalingProps.builder()
.minCapacity(5)
.maxCapacity(50)
.build())
.scaleOnUtilization(UtilizationScalingProps.builder()
.targetUtilizationPercent(70)
.build());
*/
}
}
```
### Possible Solution
_No response_
### Additional Information/Context
_No response_
### CDK CLI Version
2.53.0
### Framework Version
_No response_
### Node.js Version
v19.0.1
### OS
macOS
### Language
Java
### Language Version
_No response_
### Other information
_No response_
Contributor guide
Research direction
Start with the Java reproduction using the CDK Table API, especially replicationRegions, autoScaleGlobalSecondaryIndexReadCapacity, and autoScaleGlobalSecondaryIndexWriteCapacity, and compare the listed success and failure scenarios. Done means updating an existing table with multiple replica regions and a new or modified GSI with auto-scaling no longer produces the reported “already exists” error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, java, typescript
- Domain
- cloud, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100