Skip to content

Commit

Permalink
feat(rds): General Purpose gp3 storage volumes for database instance (#…
Browse files Browse the repository at this point in the history
…22864)

Add support for GP3 storage type. Closes #23036 

See https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-rds-general-purpose-gp3-storage-volumes/


----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jogold authored Nov 25, 2022
1 parent bf7586b commit 82c3646
Show file tree
Hide file tree
Showing 12 changed files with 1,909 additions and 7 deletions.
28 changes: 25 additions & 3 deletions packages/@aws-cdk/aws-rds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ new rds.DatabaseClusterFromSnapshot(this, 'Database', {

### Updating the database instances in a cluster

Database cluster instances may be updated in bulk or on a rolling basis.
Database cluster instances may be updated in bulk or on a rolling basis.

An update to all instances in a cluster may cause significant downtime. To reduce the downtime, set the `instanceUpdateBehavior` property in `DatabaseClusterBaseProps` to `InstanceUpdateBehavior.ROLLING`. This adds a dependency between each instance so the update is performed on only one instance at a time.

Expand Down Expand Up @@ -188,6 +188,28 @@ Creating a "production" Oracle database instance with option and parameter group

[example of setting up a production oracle instance](test/integ.instance.lit.ts)

Use the `storageType` property to specify the [type of storage](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html)
to use for the instance:

```ts
declare const vpc: ec2.Vpc;

const iopsInstance = new rds.DatabaseInstance(this, 'IopsInstance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: MysqlEngineVersion.VER_8_0_30 }),
vpc,
storageType: rds.StorageType.IO1,
iops: 5000,
});

const gp3Instance = new rds.DatabaseInstance(this, 'Gp3Instance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: MysqlEngineVersion.VER_8_0_30 }),
vpc,
allocatedStorage: 500,
storageType: rds.StorageType.GP3,
storageThroughput: 500, // only applicable for GP3
});
```

## Setting Public Accessibility

You can set public accessibility for the database instance or cluster using the `publiclyAccessible` property.
Expand Down Expand Up @@ -737,8 +759,8 @@ To learn more about using the Data API, see the [documentation](https://docs.aws

### Default VPC

The `vpc` parameter is optional.
The `vpc` parameter is optional.

If not provided, the cluster will be created in the default VPC of the account and region.
As this VPC is not deployed with AWS CDK, you can't configure the `vpcSubnets`, `subnetGroup` or `securityGroups` of the Aurora Serverless Cluster.
As this VPC is not deployed with AWS CDK, you can't configure the `vpcSubnets`, `subnetGroup` or `securityGroups` of the Aurora Serverless Cluster.
If you want to provide one of `vpcSubnets`, `subnetGroup` or `securityGroups` parameter, please provide a `vpc`.
67 changes: 63 additions & 4 deletions packages/@aws-cdk/aws-rds/lib/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,20 +256,42 @@ export interface ProcessorFeatures {

/**
* The type of storage.
*
* @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html
*/
export enum StorageType {
/**
* Standard.
*
* Amazon RDS supports magnetic storage for backward compatibility. It is recommended to use
* General Purpose SSD or Provisioned IOPS SSD for any new storage needs.
*
* @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#CHAP_Storage.Magnetic
*/
STANDARD = 'standard',

/**
* General purpose (SSD).
* General purpose SSD (gp2).
*
* Baseline performance determined by volume size
*
* @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#Concepts.Storage.GeneralSSD
*/
GP2 = 'gp2',

/**
* General purpose SSD (gp3).
*
* Performance scales independently from storage
*
* @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#Concepts.Storage.GeneralSSD
*/
GP3 = 'gp3',

/**
* Provisioned IOPS (SSD).
*
* @see https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#USER_PIOPS
*/
IO1 = 'io1'
}
Expand Down Expand Up @@ -316,11 +338,27 @@ export interface DatabaseInstanceNewProps {
*/
readonly storageType?: StorageType;

/**
* The storage throughput, specified in mebibytes per second (MiBps).
*
* Only applicable for GP3.
*
* @see https://docs.aws.amazon.com//AmazonRDS/latest/UserGuide/CHAP_Storage.html#gp3-storage
*
* @default - 125 MiBps if allocated storage is less than 400 GiB for MariaDB, MySQL, and PostgreSQL,
* less than 200 GiB for Oracle and less than 20 GiB for SQL Server. 500 MiBps otherwise (except for
* SQL Server where the default is always 125 MiBps).
*/
readonly storageThroughput?: number;

/**
* The number of I/O operations per second (IOPS) that the database provisions.
* The value must be equal to or greater than 1000.
*
* @default - no provisioned iops
* @default - no provisioned iops if storage type is not specified. For GP3: 3,000 IOPS if allocated
* storage is less than 400 GiB for MariaDB, MySQL, and PostgreSQL, less than 200 GiB for Oracle and
* less than 20 GiB for SQL Server. 12,000 IOPS otherwise (except for SQL Server where the default is
* always 3,000 IOPS).
*/
readonly iops?: number;

Expand Down Expand Up @@ -712,8 +750,16 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData
});
}

const storageType = props.storageType || StorageType.GP2;
const iops = storageType === StorageType.IO1 ? (props.iops || 1000) : undefined;
const storageType = props.storageType ?? StorageType.GP2;
const iops = defaultIops(storageType, props.iops);
if (props.storageThroughput && storageType !== StorageType.GP3) {
throw new Error(`The storage throughput can only be specified with GP3 storage type. Got ${storageType}.`);
}
if (storageType === StorageType.GP3 && props.storageThroughput && iops
&& !Token.isUnresolved(props.storageThroughput) && !Token.isUnresolved(iops)
&& props.storageThroughput/iops > 0.25) {
throw new Error(`The maximum ratio of storage throughput to IOPS is 0.25. Got ${props.storageThroughput/iops}.`);
}

this.cloudwatchLogsExports = props.cloudwatchLogsExports;
this.cloudwatchLogsRetention = props.cloudwatchLogsRetention;
Expand Down Expand Up @@ -777,6 +823,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData
processorFeatures: props.processorFeatures && renderProcessorFeatures(props.processorFeatures),
publiclyAccessible: props.publiclyAccessible ?? (this.vpcPlacement && this.vpcPlacement.subnetType === ec2.SubnetType.PUBLIC),
storageType,
storageThroughput: props.storageThroughput,
vpcSecurityGroups: securityGroups.map(s => s.securityGroupId),
maxAllocatedStorage: props.maxAllocatedStorage,
domain: this.domainId,
Expand Down Expand Up @@ -1234,3 +1281,15 @@ function renderProcessorFeatures(features: ProcessorFeatures): CfnDBInstance.Pro

return featuresList.length === 0 ? undefined : featuresList;
}

function defaultIops(storageType: StorageType, iops?: number): number | undefined {
switch (storageType) {
case StorageType.STANDARD:
case StorageType.GP2:
return undefined;
case StorageType.GP3:
return iops;
case StorageType.IO1:
return iops ?? 1000;
}
}
40 changes: 40 additions & 0 deletions packages/@aws-cdk/aws-rds/test/instance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,46 @@ describe('instance', () => {
Engine: 'postgres',
});
});

test('gp3 storage type', () => {
new rds.DatabaseInstance(stack, 'Instance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_30 }),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
vpc,
allocatedStorage: 500,
storageType: rds.StorageType.GP3,
storageThroughput: 500,
iops: 4000,
});

Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', {
StorageType: 'gp3',
StorageThroughput: 500,
Iops: 4000,
});
});

test('throws with storage throughput and not GP3', () => {
expect(() => new rds.DatabaseInstance(stack, 'Instance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_30 }),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
vpc,
storageType: rds.StorageType.GP2,
storageThroughput: 500,
})).toThrow(/storage throughput can only be specified with GP3 storage type/);
});

test('throws with a ratio of storage throughput to IOPS greater than 0.25', () => {
expect(() => new rds.DatabaseInstance(stack, 'Instance', {
engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_30 }),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
vpc,
allocatedStorage: 1000,
storageType: rds.StorageType.GP3,
iops: 5000,
storageThroughput: 2500,
})).toThrow(/maximum ratio of storage throughput to IOPS is 0.25/);
});
});

test.each([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
"source": {
"path": "InstanceGp3TestDefaultTestDeployAssert21C147A7.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"fcd2d721206458838590e53d084bf2c4ecb08d40f957ba8c2813e1832d5c8aa8": {
"source": {
"path": "cdk-integ-rds-instance-gp3.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "fcd2d721206458838590e53d084bf2c4ecb08d40f957ba8c2813e1832d5c8aa8.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading

0 comments on commit 82c3646

Please sign in to comment.