-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(aws-dynamodb): TableV2 : CREATE_FAILED (An error occurred (ValidationException) when calling the CreateTable operation: No provisioned throughput specified for the global secondary index) #32080
Comments
Hello @pahud , I have tried removing the GSI capacity specification, so that it inherits from parent tableV2, but I still get the same error. import * as cdk from 'aws-cdk-lib';
import {
Billing,
Capacity,
TableV2
} from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
import { dynamoDBTables } from '../appConfig/dynamodb-config';
import { DynamoDBTableConfig, } from '../interface/dynamodb';
interface DynamoDBConstructProps {
stackName: string;
}
export class DynamoDBConstruct extends Construct {
public readonly tables: { [tableId: string]: TableV2 } = {};
constructor(scope: Construct, id: string, props: DynamoDBConstructProps) {
super(scope, id);
const { stackName } = props;
dynamoDBTables.forEach((tableConfig: DynamoDBTableConfig) => {
const { tableId } = tableConfig;
const prefixedTableName = `${stackName}-${tableConfig.tableName}`;
let billingMode: Billing;
if (tableConfig.billingMode === 'PROVISIONED') {
if (!tableConfig.readCapacity || !tableConfig.writeCapacity) {
throw new Error(
`Read and write capacity must be specified for provisioned billing mode on table "${tableConfig.tableName}"`
);
}
// Configure fixed read capacity
const readCapacity = Capacity.fixed(tableConfig.readCapacity.fixedCapacity);
// Configure autoscaled write capacity
const writeCapacity = Capacity.autoscaled({
maxCapacity: tableConfig.writeCapacity.maxCapacity,
// minCapacity: tableConfig.writeCapacity.minCapacity || 1, // Optional
// targetUtilizationPercent: tableConfig.writeCapacity.targetUtilization || 70, // Optional
});
billingMode = Billing.provisioned({
readCapacity,
writeCapacity,
});
} else {
billingMode = Billing.onDemand();
}
const dynamoTable = new TableV2(this, `Table-${prefixedTableName}`, {
tableName: prefixedTableName,
partitionKey: {
name: tableConfig.keySchema.partitionKey.name,
type: tableConfig.keySchema.partitionKey.type,
},
sortKey: tableConfig.keySchema.sortKey
? {
name: tableConfig.keySchema.sortKey.name,
type: tableConfig.keySchema.sortKey.type,
}
: undefined,
billing: billingMode,
pointInTimeRecovery: true,
removalPolicy: cdk.RemovalPolicy.RETAIN,
timeToLiveAttribute: tableConfig.ttlAttribute,
});
// Add Global Secondary Indexes (GSIs)
if (tableConfig.globalSecondaryIndexes) {
tableConfig.globalSecondaryIndexes.forEach((gsi) => {
if (tableConfig.billingMode === 'PROVISIONED') {
const gsiConfig = {
indexName: gsi.indexName,
partitionKey: {
name: gsi.keySchema.partitionKey.name,
type: gsi.keySchema.partitionKey.type,
},
sortKey: gsi.keySchema.sortKey
? {
name: gsi.keySchema.sortKey.name,
type: gsi.keySchema.sortKey.type,
}
: undefined,
projectionType: gsi.projection.projectionType,
nonKeyAttributes: gsi.projection.nonKeyAttributes,
};
dynamoTable.addGlobalSecondaryIndex(gsiConfig);
}else{
const gsiConfig = {
indexName: gsi.indexName,
partitionKey: {
name: gsi.keySchema.partitionKey.name,
type: gsi.keySchema.partitionKey.type,
},
sortKey: gsi.keySchema.sortKey
? {
name: gsi.keySchema.sortKey.name,
type: gsi.keySchema.sortKey.type,
}
: undefined,
projectionType: gsi.projection.projectionType,
nonKeyAttributes: gsi.projection.nonKeyAttributes,
};
dynamoTable.addGlobalSecondaryIndex(gsiConfig);
}
});
}
// Store the table instance using the tableId as the key
this.tables[tableId] = dynamoTable;
});
}
} dynamodb-config.ts export const dynamoDBTables: DynamoDBTableConfig[] = [
{
tableId: 'ScanResults',
tableName: 'ScanResults', // Base table name without prefixes
keySchema: {
partitionKey: { name: 'objectKey', type: AttributeType.STRING },
sortKey: { name: 'createdAt', type: AttributeType.NUMBER },
},
billingMode: 'PROVISIONED',
readCapacity: {
fixedCapacity: 5, // Fixed read capacity units
},
writeCapacity: {
maxCapacity: 5, // Autoscaled write capacity with only maxCapacity
minCapacity: 1, // aws defaults
targetUtilization: 70 // aws defaults
},
globalSecondaryIndexes: [
{
indexName: 'scanStatus-createdAt-index',
keySchema: {
partitionKey: { name: 'scanStatus', type: AttributeType.STRING },
sortKey: { name: 'createdAt', type: AttributeType.NUMBER },
},
projection: {
projectionType: ProjectionType.ALL,
},
},
{
indexName: 'submissionId-index',
keySchema: {
partitionKey: { name: 'submissionId', type: AttributeType.STRING },
},
projection: {
projectionType: ProjectionType.ALL,
},
},
],
ttlAttribute: 'expiryDt',
},
// Add more tables if necessary
]; cdk synth shows the settings, malwareanalysissv1devDynamoDBConstructTablemalwareanalysissv1devScanResultsEAD68F9D:
Type: AWS::DynamoDB::GlobalTable
Properties:
AttributeDefinitions:
- AttributeName: objectKey
AttributeType: S
- AttributeName: createdAt
AttributeType: "N"
- AttributeName: scanStatus
AttributeType: S
- AttributeName: submissionId
AttributeType: S
BillingMode: PROVISIONED
GlobalSecondaryIndexes:
- IndexName: scanStatus-createdAt-index
KeySchema:
- AttributeName: scanStatus
KeyType: HASH
- AttributeName: createdAt
KeyType: RANGE
Projection:
ProjectionType: ALL
WriteProvisionedThroughputSettings:
WriteCapacityAutoScalingSettings:
MaxCapacity: 5
MinCapacity: 1
TargetTrackingScalingPolicyConfiguration:
TargetValue: 70
- IndexName: submissionId-index
KeySchema:
- AttributeName: submissionId
KeyType: HASH
Projection:
ProjectionType: ALL
WriteProvisionedThroughputSettings:
WriteCapacityAutoScalingSettings:
MaxCapacity: 5
MinCapacity: 1
TargetTrackingScalingPolicyConfiguration:
TargetValue: 70
KeySchema:
- AttributeName: objectKey
KeyType: HASH
- AttributeName: createdAt
KeyType: RANGE
Replicas:
- GlobalSecondaryIndexes:
- IndexName: scanStatus-createdAt-index
ReadProvisionedThroughputSettings:
ReadCapacityUnits: 5
- IndexName: submissionId-index
ReadProvisionedThroughputSettings:
ReadCapacityUnits: 5
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
ReadProvisionedThroughputSettings:
ReadCapacityUnits: 5
Region:
Ref: AWS::Region
TableName: malware-analysis-sv1-dev-ScanResults
TimeToLiveSpecification:
AttributeName: expiryDt
Enabled: true
WriteProvisionedThroughputSettings:
WriteCapacityAutoScalingSettings:
MaxCapacity: 5
MinCapacity: 1
TargetTrackingScalingPolicyConfiguration:
TargetValue: 70
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: malware-analysis-sv1-dev-InfrastructureStack/malware-analysis-sv1-dev-DynamoDBConstruct/Table-malware-analysis-sv1-dev-ScanResults/Resource cdk deploy -vvv --debug error
Thanks |
Hi, Are you able to simplify your provided code snippets and just create one single table with minimal required properties without using conditions like if/else or foreach loops so we can solely focus on what's happening in this single Table? This would help us better address and focus on what's going wrong there rather then dealing many conditions in the snippets. If you prefer, you can extend my provided sample above if you like. Thank you. Also, I've seen codes like import { dynamoDBTables } from '../appConfig/dynamodb-config';
import { DynamoDBTableConfig, } from '../interface/dynamodb'; As I can't access those imported codes, it adds unnecessary complexity to address the issue. Appreciated if you could provide a very minimal self-contained reproducible code snippets for us. |
This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled. |
Describe the bug
When creating a TableV2 with GSI, even the ReadCapacity and WriteCapacity are specified, getting error "No provisioned throughput specified for the global secondary index"
Created the construct as show in the docs . As per the doc, also tried "If TableV2 is configured with provisioned billing but readCapacity or writeCapacity are not configured on a globalSecondaryIndex, then they will be inherited from the capacity settings specified with the billing configuration"
cdk synth template.json
Regression Issue
Last Known Working CDK Version
No response
Expected Behavior
As both readCapacity and writeCapacity is specified for GSI, table should be created.
Current Behavior
CDK Deploy throws error
malware-analysis-sv1-dev-InfrastructureStack/malware-analysis-sv1-dev-DynamoDBConstruct/Table-malware-analysis-sv1-dev-ScanResults (malwareanalysissv1devDynamoDBConstructTablemalwareanalysissv1devScanResultsEAD68F9D) An error occurred (ValidationException) when calling the CreateTable operation: No provisioned throughput specified for the global secondary index
Reproduction Steps
Create a TableV2 having billing mode as PROVISIONED w/o readCapacity and WriteCapacity in GSI.
Possible Solution
No response
Additional Information/Context
No response
CDK CLI Version
2.166.0 (build 7bb9203)
Framework Version
No response
Node.js Version
20.14.9
OS
MAC
Language
TypeScript
Language Version
~5.6.3
Other information
No response
The text was updated successfully, but these errors were encountered: