Skip to content

Commit

Permalink
feat(dynamodb): add option to skip waiting for global replication to …
Browse files Browse the repository at this point in the history
…finish (aws#16983)

Motivation - On large tables, replication takes long time to complete. CloudFormation has a hard timeout of 1 hour on the Custom Resources, to bypass this, we want to have the replication continue in background based on a property.

Fixes aws#16611 


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
TheSPD authored and TikiTDO committed Feb 21, 2022
1 parent 4fcb80c commit 64a7bc3
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 3 deletions.
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-dynamodb/lib/replica-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ export async function isCompleteHandler(event: IsCompleteRequest): Promise<IsCom
const replicas = data.Table?.Replicas ?? [];
const regionReplica = replicas.find(r => r.RegionName === event.ResourceProperties.Region);
const replicaActive = !!(regionReplica?.ReplicaStatus === 'ACTIVE');
const skipReplicationCompletedWait = event.ResourceProperties.SkipReplicationCompletedWait ?? false;

switch (event.RequestType) {
case 'Create':
case 'Update':
// Complete when replica is reported as ACTIVE
return { IsComplete: tableActive && replicaActive };
return { IsComplete: tableActive && (replicaActive || skipReplicationCompletedWait) };
case 'Delete':
// Complete when replica is gone
return { IsComplete: tableActive && regionReplica === undefined };
Expand Down
21 changes: 19 additions & 2 deletions packages/@aws-cdk/aws-dynamodb/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,22 @@ export interface TableOptions extends SchemaOptions {
*/
readonly replicationTimeout?: Duration;

/**
* Indicates whether CloudFormation stack waits for replication to finish.
* If set to false, the CloudFormation resource will mark the resource as
* created and replication will be completed asynchronously. This property is
* ignored if replicationRegions property is not set.
*
* DO NOT UNSET this property if adding/removing multiple replicationRegions
* in one deployment, as CloudFormation only supports one region replication
* at a time. CDK overcomes this limitation by waiting for replication to
* finish before starting new replicationRegion.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-replicas
* @default true
*/
readonly waitForReplicationToFinish?: boolean;

/**
* Whether CloudWatch contributor insights is enabled.
*
Expand Down Expand Up @@ -1152,7 +1168,7 @@ export class Table extends TableBase {
}

if (props.replicationRegions && props.replicationRegions.length > 0) {
this.createReplicaTables(props.replicationRegions, props.replicationTimeout);
this.createReplicaTables(props.replicationRegions, props.replicationTimeout, props.waitForReplicationToFinish);
}
}

Expand Down Expand Up @@ -1494,7 +1510,7 @@ export class Table extends TableBase {
*
* @param regions regions where to create tables
*/
private createReplicaTables(regions: string[], timeout?: Duration) {
private createReplicaTables(regions: string[], timeout?: Duration, waitForReplicationToFinish?: boolean) {
const stack = Stack.of(this);

if (!Token.isUnresolved(stack.region) && regions.includes(stack.region)) {
Expand Down Expand Up @@ -1524,6 +1540,7 @@ export class Table extends TableBase {
properties: {
TableName: this.tableName,
Region: region,
SkipReplicationCompletedWait: waitForReplicationToFinish === undefined ? undefined : !waitForReplicationToFinish,
},
});
currentRegion.node.addDependency(
Expand Down
54 changes: 54 additions & 0 deletions packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,60 @@ describe('global', () => {
});
});

test('create replicas without waiting to finish replication', () => {
// GIVEN
const stack = new Stack();

// WHEN
new Table(stack, 'Table', {
partitionKey: {
name: 'id',
type: AttributeType.STRING,
},
replicationRegions: [
'eu-west-2',
'eu-central-1',
],
waitForReplicationToFinish: false,
});

// THEN
expect(stack).toHaveResource('Custom::DynamoDBReplica', {
Properties: {
TableName: {
Ref: 'TableCD117FA1',
},
Region: 'eu-west-2',
SkipReplicationCompletedWait: true,
},
Condition: 'TableStackRegionNotEqualseuwest2A03859E7',
}, ResourcePart.CompleteDefinition);

expect(stack).toHaveResource('Custom::DynamoDBReplica', {
Properties: {
TableName: {
Ref: 'TableCD117FA1',
},
Region: 'eu-central-1',
SkipReplicationCompletedWait: true,
},
Condition: 'TableStackRegionNotEqualseucentral199D46FC0',
}, ResourcePart.CompleteDefinition);

expect(SynthUtils.toCloudFormation(stack).Conditions).toEqual({
TableStackRegionNotEqualseuwest2A03859E7: {
'Fn::Not': [
{ 'Fn::Equals': ['eu-west-2', { Ref: 'AWS::Region' }] },
],
},
TableStackRegionNotEqualseucentral199D46FC0: {
'Fn::Not': [
{ 'Fn::Equals': ['eu-central-1', { Ref: 'AWS::Region' }] },
],
},
});
});

test('grantReadData', () => {
const stack = new Stack();
const table = new Table(stack, 'Table', {
Expand Down

0 comments on commit 64a7bc3

Please sign in to comment.