aws / aws/aws-cdk

(dynamo-db): Error when adding a new GSI with auto-scaling to an existing DynamoDB table with more than one replica regions via CDK

Open
#23,217 5 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-dynamodb bug ddb-legacy-table p2
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.