From 1cf2e2041ef0cf2d57d42acbe70a05cd782ed8cc Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 27 Oct 2023 11:42:12 -0700 Subject: [PATCH 1/3] added seed capacity property to AutoscaledCapacityOptions and created unit tests Signed-off-by: Francis --- .../aws-cdk-lib/aws-dynamodb/lib/capacity.ts | 15 +++++++++++- .../aws-dynamodb/test/capacity.test.ts | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts index 0df1591d17178..5f4d13f270e94 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts @@ -39,7 +39,15 @@ export interface AutoscaledCapacityOptions { * * @default 70 */ - readonly targetUtilizationPercent?: number + readonly targetUtilizationPercent?: number; + + /** + * If you want to switch a table's billing mode from on-demand to provisioned, you + * must specify a value for this property for each autoscaled resource. + * + * @default no seed capacity + */ + readonly seedCapacity?: number; } /** @@ -85,6 +93,10 @@ export abstract class Capacity { if (options.targetUtilizationPercent !== undefined && (options.targetUtilizationPercent < 20 || options.targetUtilizationPercent > 90)) { throw new Error('`targetUtilizationPercent` cannot be less than 20 or greater than 90'); } + + if (options.seedCapacity !== undefined && (options.seedCapacity < 1)) { + throw new Error(`'seedCapacity' cannot be less than 1 - received ${options.seedCapacity}`); + } } public _renderReadCapacity() { @@ -103,6 +115,7 @@ export abstract class Capacity { return { minCapacity: options.minCapacity ?? 1, maxCapacity: options.maxCapacity, + seedCapacity: options.seedCapacity, targetTrackingScalingPolicyConfiguration: { targetValue: options.targetUtilizationPercent ?? 70, }, diff --git a/packages/aws-cdk-lib/aws-dynamodb/test/capacity.test.ts b/packages/aws-cdk-lib/aws-dynamodb/test/capacity.test.ts index 0f0a472376aab..09ac42e9ce10c 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/test/capacity.test.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/test/capacity.test.ts @@ -90,6 +90,23 @@ describe('autoscaled capacity', () => { }); }); + test('can specify seed capacity', () => { + // GIVEN + const capacity = Capacity.autoscaled({ maxCapacity: 10, seedCapacity: 20 }); + + // WHEN / THEN + expect(capacity._renderReadCapacity()).toEqual({ + readCapacityAutoScalingSettings: { + minCapacity: 1, + maxCapacity: 10, + seedCapacity: 20, + targetTrackingScalingPolicyConfiguration: { + targetValue: 70, + }, + }, + }); + }); + test('capacity mode is AUTOSCALED', () => { // GIVEN const capacity = Capacity.autoscaled({ minCapacity: 1, maxCapacity: 10 }); @@ -118,4 +135,11 @@ describe('autoscaled capacity', () => { Capacity.autoscaled({ maxCapacity: 10, targetUtilizationPercent: 91 }); }).toThrow('`targetUtilizationPercent` cannot be less than 20 or greater than 90'); }); + + test('throws if seed capacity is less than 1', () => { + // GIVEN / WHEN / THEN + expect(() => { + Capacity.autoscaled({ maxCapacity: 10, seedCapacity: 0 }); + }).toThrow("'seedCapacity' cannot be less than 1 - received 0"); + }); }); From c39b22f86d5061e363b5e2450a2bdf610f4d0715 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 27 Oct 2023 12:24:12 -0700 Subject: [PATCH 2/3] integ test and snapshots Signed-off-by: Francis --- .../aws-cdk-global-table.assets.json | 4 ++-- .../aws-cdk-global-table.template.json | 2 ++ .../test/integ.table-v2-global.js.snapshot/manifest.json | 4 +++- .../test/integ.table-v2-global.js.snapshot/tree.json | 2 ++ .../test/aws-dynamodb/test/integ.table-v2-global.ts | 2 +- packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts | 5 +++-- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.assets.json index 2a47502d9bdf3..367978078a86f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.assets.json @@ -1,7 +1,7 @@ { "version": "34.0.0", "files": { - "116ecd47ef9b5b3d293d7b3733b6a742a38eb54e2d54d79f9009fdbbb8c36dff": { + "655325b6fb8ec8800b23d95ba84700a27856d6a10c2a0557c2b17da00bf4f7b1": { "source": { "path": "aws-cdk-global-table.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "116ecd47ef9b5b3d293d7b3733b6a742a38eb54e2d54d79f9009fdbbb8c36dff.json", + "objectKey": "655325b6fb8ec8800b23d95ba84700a27856d6a10c2a0557c2b17da00bf4f7b1.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json index c108cf892122a..9e1ae805f618c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/aws-cdk-global-table.template.json @@ -49,6 +49,7 @@ "WriteCapacityAutoScalingSettings": { "MaxCapacity": 20, "MinCapacity": 1, + "SeedCapacity": 10, "TargetTrackingScalingPolicyConfiguration": { "TargetValue": 60 } @@ -251,6 +252,7 @@ "WriteCapacityAutoScalingSettings": { "MaxCapacity": 20, "MinCapacity": 1, + "SeedCapacity": 10, "TargetTrackingScalingPolicyConfiguration": { "TargetValue": 60 } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/manifest.json index 9f5f8980ccc4a..48e0da18859d3 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/manifest.json @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/us-east-1", "properties": { "templateFile": "aws-cdk-global-table.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/116ecd47ef9b5b3d293d7b3733b6a742a38eb54e2d54d79f9009fdbbb8c36dff.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/655325b6fb8ec8800b23d95ba84700a27856d6a10c2a0557c2b17da00bf4f7b1.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -87,6 +88,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "awscdkglobaltableintegDefaultTestDeployAssertA2A9E81F.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json index 6c0d53c9afce4..5cbdfe133287c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.js.snapshot/tree.json @@ -90,6 +90,7 @@ "writeCapacityAutoScalingSettings": { "minCapacity": 1, "maxCapacity": 20, + "seedCapacity": 10, "targetTrackingScalingPolicyConfiguration": { "targetValue": 60 } @@ -292,6 +293,7 @@ "writeCapacityAutoScalingSettings": { "minCapacity": 1, "maxCapacity": 20, + "seedCapacity": 10, "targetTrackingScalingPolicyConfiguration": { "targetValue": 60 } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts index 5a81eadcae96c..8cf849249f998 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.table-v2-global.ts @@ -16,7 +16,7 @@ class TestStack extends Stack { sortKey: { name: 'sk', type: AttributeType.NUMBER }, billing: Billing.provisioned({ readCapacity: Capacity.fixed(10), - writeCapacity: Capacity.autoscaled({ maxCapacity: 20, targetUtilizationPercent: 60 }), + writeCapacity: Capacity.autoscaled({ maxCapacity: 20, targetUtilizationPercent: 60, seedCapacity: 10 }), }), encryption: TableEncryptionV2.awsManagedKey(), contributorInsights: true, diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts index 5f4d13f270e94..a0443d1a41622 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/capacity.ts @@ -42,8 +42,9 @@ export interface AutoscaledCapacityOptions { readonly targetUtilizationPercent?: number; /** - * If you want to switch a table's billing mode from on-demand to provisioned, you - * must specify a value for this property for each autoscaled resource. + * If you want to switch a table's billing mode from on-demand to provisioned or + * from provisioned to on-demand, you must specify a value for this property for + * each autoscaled resource. * * @default no seed capacity */ From bba9eaa1c3eb3db51129024846da9ebc49a48c20 Mon Sep 17 00:00:00 2001 From: Francis Date: Fri, 27 Oct 2023 12:54:58 -0700 Subject: [PATCH 3/3] readme Signed-off-by: Francis --- packages/aws-cdk-lib/aws-dynamodb/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/aws-cdk-lib/aws-dynamodb/README.md b/packages/aws-cdk-lib/aws-dynamodb/README.md index e35e679605236..2212bf7ad305f 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/README.md +++ b/packages/aws-cdk-lib/aws-dynamodb/README.md @@ -201,6 +201,18 @@ const globalTable = new dynamodb.TableV2(stack, 'GlobalTable', { }); ``` +When changing the billing for a table from provisioned to on-demand or from on-demand to provisioned, `seedCapacity` must be configured for each autoscaled resource: + +```ts +const globalTable = new dynamodb.TableV2(this, 'Table', { + partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, + billing: dynamodb.Billing.provisioned({ + readCapacity: dynamodb.Capacity.fixed(10), + writeCapacity: dynamodb.Capacity.autoscaled({ maxCapacity: 10, seedCapacity: 20 }), + }), +}); +``` + Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html