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(rds): General Purpose gp3 storage volumes for database instance #22864

Merged
merged 9 commits into from
Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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