Skip to content
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

feat(dynamodb): allow setting TableClass for a Table #18719

Merged
merged 8 commits into from
Feb 1, 2022
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-dynamodb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,23 @@ const table = new dynamodb.Table(this, 'Table', {
Further reading:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.

## Table Class

DynamoDB supports two table classes:

* STANDARD - the default mode, and is recommended for the vast majority of workloads.
* STANDARD_INFREQUENT_ACCESS - optimized for tables where storage is the dominant cost.

```ts
const table = new dynamodb.Table(this, 'Table', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
tableClass: dynamodb.TableClass.STANDARD_INFREQUENT_ACCESS,
});
```

Further reading:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.TableClasses.html

## Configure AutoScaling for your table

You can have DynamoDB automatically raise and lower the read and write capacities
Expand Down
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-dynamodb/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ export interface TableOptions extends SchemaOptions {
*/
readonly serverSideEncryption?: boolean;

/**
* Specifiy the table class.
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
* @default STANDARD else STANDARD_INFREQUENT_ACCESS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the else part necessary here?

Suggested change
* @default STANDARD else STANDARD_INFREQUENT_ACCESS
* @default STANDARD

*/
readonly tableClass?: TableClass;

/**
* Whether server-side encryption with an AWS managed customer master key is enabled.
*
Expand Down Expand Up @@ -1169,6 +1175,7 @@ export class Table extends TableBase {
},
sseSpecification,
streamSpecification,
tableClass: props.tableClass ? props.tableClass : undefined,
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
timeToLiveSpecification: props.timeToLiveAttribute ? { attributeName: props.timeToLiveAttribute, enabled: true } : undefined,
contributorInsightsSpecification: props.contributorInsightsEnabled !== undefined ? { enabled: props.contributorInsightsEnabled } : undefined,
kinesisStreamSpecification: props.kinesisStream ? { streamArn: props.kinesisStream.streamArn } : undefined,
Expand Down Expand Up @@ -1760,6 +1767,19 @@ export enum StreamViewType {
KEYS_ONLY = 'KEYS_ONLY'
}

/**
* DynamoDB's table class.
*
* @see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.TableClasses.html
*/
export enum TableClass {
madeline-k marked this conversation as resolved.
Show resolved Hide resolved
/** Default table class for DynamoDB */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full-stop please:

Suggested change
/** Default table class for DynamoDB */
/** Default table class for DynamoDB. */

STANDARD = 'STANDARD',

/** table class for DynamoDB that reduces storage costs compared to existing DynamoDB Standard tables */
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
STANDARD_INFREQUENT_ACCESS = 'STANDARD_INFREQUENT_ACCESS',
}

/**
* Just a convenient way to keep track of both attributes
*/
Expand Down
66 changes: 66 additions & 0 deletions packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ProjectionType,
StreamViewType,
Table,
TableClass,
TableEncryption,
Operation,
CfnTable,
Expand Down Expand Up @@ -334,6 +335,7 @@ testDeprecated('when specifying every property', () => {
sortKey: TABLE_SORT_KEY,
contributorInsightsEnabled: true,
kinesisStream: stream,
tableClass: TableClass.STANDARD,
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
});
Tags.of(table).add('Environment', 'Production');

Expand Down Expand Up @@ -719,6 +721,70 @@ test('if an encryption key is included, encrypt/decrypt permissions are added to
});
});

test('when specifying STANDARD_INFREQUENT_ACCESS table class', () => {
const stack = new Stack();
new Table(stack, CONSTRUCT_NAME, {
tableName: TABLE_NAME,
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
partitionKey: TABLE_PARTITION_KEY,
tableClass: TableClass.STANDARD_INFREQUENT_ACCESS,
});

Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table',
{
KeySchema: [
{ AttributeName: 'hashKey', KeyType: 'HASH' },
],
TableClass: 'STANDARD_INFREQUENT_ACCESS',
AttributeDefinitions: [
{ AttributeName: 'hashKey', AttributeType: 'S' },
],
TableName: 'MyTable',
},
);
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
});

test('when specifying STANDARD table class', () => {
const stack = new Stack();
new Table(stack, CONSTRUCT_NAME, {
tableName: TABLE_NAME,
Copy link
Contributor

@skinny85 skinny85 Jan 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here (we don't need this property).

partitionKey: TABLE_PARTITION_KEY,
tableClass: TableClass.STANDARD,
});

Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table',
{
KeySchema: [
{ AttributeName: 'hashKey', KeyType: 'HASH' },
],
TableClass: 'STANDARD',
AttributeDefinitions: [
{ AttributeName: 'hashKey', AttributeType: 'S' },
],
TableName: 'MyTable',
},
);
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
});

test('when specifying no table class', () => {
const stack = new Stack();
new Table(stack, CONSTRUCT_NAME, {
tableName: TABLE_NAME,
arjanschaaf marked this conversation as resolved.
Show resolved Hide resolved
partitionKey: TABLE_PARTITION_KEY,
});

Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, we should also verify that a TableClass property is not present in the resulting template:

  Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', Match.not(
    {
      TableClass: Match.anyValue(),
    }),
  );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, however I did implement it slightly different with Match.absent().

{
KeySchema: [
{ AttributeName: 'hashKey', KeyType: 'HASH' },
],
AttributeDefinitions: [
{ AttributeName: 'hashKey', AttributeType: 'S' },
],
TableName: 'MyTable',
},
);
});

test('when specifying PAY_PER_REQUEST billing mode', () => {
const stack = new Stack();
new Table(stack, CONSTRUCT_NAME, {
Expand Down