Skip to content

Commit

Permalink
feat(dynamodb): add warm-throughput to L2
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee Hannigan committed Nov 14, 2024
1 parent 4af1564 commit 021d1c3
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 1 deletion.
20 changes: 20 additions & 0 deletions packages/aws-cdk-lib/aws-dynamodb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,26 @@ const globalTable = new dynamodb.TableV2(this, 'Table', {
Further reading:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html

## Warm Throughput
Warm throughput refers to the number of read and write operations your DynamoDB table can instantaneously support.

This optional configuration allows you to pre-warm your table or index to handle anticipated throughput, ensuring optimal performance under expected load.

The Warm Throughput configuration settings are automatically replicated across all Global Table replicas.

```ts
const table = new dynamodb.TableV2(this, 'Table', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
warmThroughput: {
readUnitsPerSecond: 15000,
writeUnitsPerSecond: 20000,
},
});
```
Further reading:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/warm-throughput.html


## Encryption

All user data stored in a DynamoDB table is fully encrypted at rest. When creating an instance of the `TableV2` construct, you can select the following table encryption options:
Expand Down
19 changes: 19 additions & 0 deletions packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ const table = new dynamodb.Table(this, 'Table', {
Further reading:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.

## Warm Throughput
Warm throughput refers to the number of read and write operations your DynamoDB table can instantaneously support.

This optional configuration allows you to pre-warm your table or index to handle anticipated throughput, ensuring optimal performance under expected load.

Note: The Warm Throughput feature is not available for Global Table replicas using `Table` construct; use the `TableV2` construct instead to enable this functionality.

```ts
const table = new dynamodb.Table(this, 'Table', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
warmThroughput: {
readUnitsPerSecond: 15000,
writeUnitsPerSecond: 20000,
},
});
```
Further reading:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/warm-throughput.html

## Table Class

DynamoDB supports two table classes:
Expand Down
16 changes: 16 additions & 0 deletions packages/aws-cdk-lib/aws-dynamodb/lib/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,22 @@ export interface Attribute {
readonly type: AttributeType;
}

/**
* Reference to WarmThroughput for a DynamoDB table
*/
export interface WarmThroughput {
/**
* Configures the number of read units per second a table will be able to handle instantly
* @default - no readUnitsPerSecond configured
*/
readonly readUnitsPerSecond?: number;
/**
* Configures the number of write units per second a table will be able to handle instantly
* @default - no writeUnitsPerSecond configured
*/
readonly writeUnitsPerSecond?: number;
}

/**
* Data types for attributes within a table
*
Expand Down
19 changes: 19 additions & 0 deletions packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SecondaryIndexProps,
StreamViewType,
TableClass,
WarmThroughput,
} from './shared';
import { ITableV2, TableBaseV2 } from './table-v2-base';
import { PolicyDocument } from '../../aws-iam';
Expand Down Expand Up @@ -118,6 +119,13 @@ export interface GlobalSecondaryIndexPropsV2 extends SecondaryIndexProps {
* @default - inherited from the primary table.
*/
readonly maxWriteRequestUnits?: number;

/**
* The warm throughput configuration for the global secondary index.
*
* @default - no warm throughput is configured
*/
readonly warmThroughput?: WarmThroughput;
}

/**
Expand Down Expand Up @@ -298,6 +306,13 @@ export interface TablePropsV2 extends TableOptionsV2 {
* @default TableEncryptionV2.dynamoOwnedKey()
*/
readonly encryption?: TableEncryptionV2;

/**
* The warm throughput configuration for the table.
*
* @default - no warm throughput is configured
*/
readonly warmThroughput?: WarmThroughput;
}

/**
Expand Down Expand Up @@ -576,6 +591,7 @@ export class TableV2 extends TableBaseV2 {
timeToLiveSpecification: props.timeToLiveAttribute
? { attributeName: props.timeToLiveAttribute, enabled: true }
: undefined,
warmThroughput: props.warmThroughput ?? undefined,
});
resource.applyRemovalPolicy(props.removalPolicy);

Expand Down Expand Up @@ -741,6 +757,8 @@ export class TableV2 extends TableBaseV2 {

props.maxReadRequestUnits && this.globalSecondaryIndexMaxReadUnits.set(props.indexName, props.maxReadRequestUnits);

const warmThroughput = props.warmThroughput ?? undefined;

const writeOnDemandThroughputSettings: CfnGlobalTable.WriteOnDemandThroughputSettingsProperty | undefined = props.maxWriteRequestUnits
? { maxWriteRequestUnits: props.maxWriteRequestUnits }
: undefined;
Expand All @@ -751,6 +769,7 @@ export class TableV2 extends TableBaseV2 {
projection,
writeProvisionedThroughputSettings,
writeOnDemandThroughputSettings,
warmThroughput,
};
}

Expand Down
19 changes: 18 additions & 1 deletion packages/aws-cdk-lib/aws-dynamodb/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ScalableTableAttribute } from './scalable-table-attribute';
import {
Operation, OperationsMetricOptions, SystemErrorsForOperationsMetricOptions,
Attribute, BillingMode, ProjectionType, ITable, SecondaryIndexProps, TableClass,
LocalSecondaryIndexProps, TableEncryption, StreamViewType,
LocalSecondaryIndexProps, TableEncryption, StreamViewType, WarmThroughput,
} from './shared';
import * as appscaling from '../../aws-applicationautoscaling';
import * as cloudwatch from '../../aws-cloudwatch';
Expand Down Expand Up @@ -259,6 +259,14 @@ export interface TableOptions extends SchemaOptions {
*/
readonly billingMode?: BillingMode;

/**
* Specify values to pre-warm you DynamoDB Table
* Warm Throughput feature is not available for Global Table replicas using the `Table` construct. To enable Warm Throughput, use the `TableV2` construct instead.
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html#cfn-dynamodb-table-warmthroughput
* @default - warm throughput is not configured
*/
readonly warmThroughput?: WarmThroughput;

/**
* Whether point-in-time recovery is enabled.
* @default - point-in-time recovery is disabled
Expand Down Expand Up @@ -457,6 +465,13 @@ export interface GlobalSecondaryIndexProps extends SecondaryIndexProps, SchemaOp
*/
readonly maxWriteRequestUnits?: number;

/**
* The warm throughput configuration for the global secondary index.
*
* @default - no warm throughput is configured
*/
readonly warmThroughput?: WarmThroughput;

/**
* Whether CloudWatch contributor insights is enabled for the specified global secondary index.
*
Expand Down Expand Up @@ -1187,6 +1202,7 @@ export class Table extends TableBase {
resourcePolicy: props.resourcePolicy
? { policyDocument: props.resourcePolicy }
: undefined,
warmThroughput: props.warmThroughput?? undefined,
});
this.table.applyRemovalPolicy(props.removalPolicy);

Expand Down Expand Up @@ -1249,6 +1265,7 @@ export class Table extends TableBase {
maxWriteRequestUnits: props.maxWriteRequestUnits || undefined,
},
} : undefined),
warmThroughput: props.warmThroughput ?? undefined,
});

this.secondaryIndexSchemas.set(props.indexName, {
Expand Down
141 changes: 141 additions & 0 deletions packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3642,3 +3642,144 @@ test('Resource policy test', () => {
},
});
});

test('Warm Throughput test on-demand', () => {
// GIVEN
const app = new App();
const stack = new Stack(app, 'Stack');

// WHEN
const table = new Table(stack, 'Table', {
partitionKey: { name: 'id', type: AttributeType.STRING },
warmThroughput: {
readUnitsPerSecond: 13000,
writeUnitsPerSecond: 5000,
},
});

table.addGlobalSecondaryIndex({
indexName: 'my-index-1',
partitionKey: { name: 'gsi1pk', type: AttributeType.STRING },
warmThroughput: {
readUnitsPerSecond: 15000,
writeUnitsPerSecond: 6000,
},
});

table.addGlobalSecondaryIndex({
indexName: 'my-index-2',
partitionKey: { name: 'gsi2pk', type: AttributeType.STRING },
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', {
KeySchema: [
{ AttributeName: 'id', KeyType: 'HASH' },
],
AttributeDefinitions: [
{ AttributeName: 'id', AttributeType: 'S' },
{ AttributeName: 'gsi1pk', AttributeType: 'S' },
{ AttributeName: 'gsi2pk', AttributeType: 'S' },
],
WarmThroughput: {
ReadUnitsPerSecond: 13000,
WriteUnitsPerSecond: 5000,
},
GlobalSecondaryIndexes: [
{
IndexName: 'my-index-1',
KeySchema: [
{ AttributeName: 'gsi1pk', KeyType: 'HASH' },
],
Projection: { ProjectionType: 'ALL' },
WarmThroughput: {
ReadUnitsPerSecond: 15000,
WriteUnitsPerSecond: 6000,
},
},
{
IndexName: 'my-index-2',
KeySchema: [
{ AttributeName: 'gsi2pk', KeyType: 'HASH' },
],
Projection: { ProjectionType: 'ALL' },
},
],
});

});

test('Warm Throughput test provisioned', () => {
// GIVEN
const app = new App();
const stack = new Stack(app, 'Stack');

// WHEN
const table = new Table(stack, 'Table', {
partitionKey: { name: 'id', type: AttributeType.STRING },
readCapacity: 5,
writeCapacity: 6,
warmThroughput: {
readUnitsPerSecond: 2000,
writeUnitsPerSecond: 1000,
},
});

table.addGlobalSecondaryIndex({
indexName: 'my-index-1',
partitionKey: { name: 'gsi1pk', type: AttributeType.STRING },
readCapacity: 7,
writeCapacity: 8,
warmThroughput: {
readUnitsPerSecond: 3000,
writeUnitsPerSecond: 4000,
},
});

table.addGlobalSecondaryIndex({
indexName: 'my-index-2',
partitionKey: { name: 'gsi2pk', type: AttributeType.STRING },
readCapacity: 9,
writeCapacity: 10,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', {
KeySchema: [
{ AttributeName: 'id', KeyType: 'HASH' },
],
AttributeDefinitions: [
{ AttributeName: 'id', AttributeType: 'S' },
{ AttributeName: 'gsi1pk', AttributeType: 'S' },
{ AttributeName: 'gsi2pk', AttributeType: 'S' },
],
WarmThroughput: {
ReadUnitsPerSecond: 2000,
WriteUnitsPerSecond: 1000,
},
ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 6 },
GlobalSecondaryIndexes: [
{
IndexName: 'my-index-1',
KeySchema: [
{ AttributeName: 'gsi1pk', KeyType: 'HASH' },
],
Projection: { ProjectionType: 'ALL' },
WarmThroughput: {
ReadUnitsPerSecond: 3000,
WriteUnitsPerSecond: 4000,
},
ProvisionedThroughput: { ReadCapacityUnits: 7, WriteCapacityUnits: 8 },
},
{
IndexName: 'my-index-2',
KeySchema: [
{ AttributeName: 'gsi2pk', KeyType: 'HASH' },
],
Projection: { ProjectionType: 'ALL' },
ProvisionedThroughput: { ReadCapacityUnits: 9, WriteCapacityUnits: 10 },
},
],
});

});
Loading

0 comments on commit 021d1c3

Please sign in to comment.