Skip to content

Commit

Permalink
feat(rds): isFromLegacyInstanceProps migration flag with `ClusterIn…
Browse files Browse the repository at this point in the history
…stance.serverlessV2` (aws#26472)

**Context**
A recent feature release aws#25437 has added support for Aurora Serverless V2 cluster instances. This change also introduced a new approach for defining cluster instances, deprecating `instanceProps` in the process.

The new approach uses `ClusterInstance.provisioned()` and `ClusterInstance.serverlessV2()` to define instances and their parameters on a per-instance basis. A migration flag `isFromLegacyInstanceProps` has also been added to the `ClusterInstance.provisioned()` constructor props to allow for migration to this new approach without destructive changes to the generated CFN template.

**Bug**
Because the `DatabaseCluster` construct has not previously had official support for Serverless V2 instances, the same migration flag has not been made available for `ClusterInstance.serverlessV2()`. This ignores the fact that many people have already provisioned serverless v2 instances using a common workaround described here aws#20197 (comment). People who have used this method previously have no clean migration path. This has been previously raised in aws#25942.

**Fix**
This fix simply exposes the `isFromLegacyInstanceProps` flag on **both** `ProvisionedClusterInstanceProps` and `ServerlessV2ClusterInstanceProps`. The behaviour for this flag is already implemented and applied across both instance types, so this is a type-only change. I have however added a test to capture this upgrade path for Serverless V2 instances from the common workaround.


Closes aws#25942.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
dabrowne authored and bmoffatt committed Jul 28, 2023
1 parent 29c66e2 commit f62c34b
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 56 deletions.
103 changes: 48 additions & 55 deletions packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts
Expand Up @@ -128,53 +128,6 @@ export interface ProvisionedClusterInstanceProps extends ClusterInstanceOptions
* @default 2
*/
readonly promotionTier?: number;

/**
* Only used for migrating existing clusters from using `instanceProps` to `writer` and `readers`
*
* @example
* // existing cluster
* declare const vpc: ec2.Vpc;
* const cluster = new rds.DatabaseCluster(this, 'Database', {
* engine: rds.DatabaseClusterEngine.auroraMysql({
* version: rds.AuroraMysqlEngineVersion.VER_3_03_0,
* }),
* instances: 2,
* instanceProps: {
* instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
* vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
* vpc,
* },
* });
*
* // migration
*
* const instanceProps = {
* instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
* isFromLegacyInstanceProps: true,
* };
*
* const myCluster = new rds.DatabaseCluster(this, 'Database', {
* engine: rds.DatabaseClusterEngine.auroraMysql({
* version: rds.AuroraMysqlEngineVersion.VER_3_03_0,
* }),
* vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
* vpc,
* writer: rds.ClusterInstance.provisioned('Instance1', {
* instanceType: instanceProps.instanceType,
* isFromLegacyInstanceProps: instanceProps.isFromLegacyInstanceProps,
* }),
* readers: [
* rds.ClusterInstance.provisioned('Instance2', {
* instanceType: instanceProps.instanceType,
* isFromLegacyInstanceProps: instanceProps.isFromLegacyInstanceProps,
* }),
* ],
* });
*
* @default false
*/
readonly isFromLegacyInstanceProps?: boolean;
}

/**
Expand All @@ -199,7 +152,7 @@ export interface ServerlessV2ClusterInstanceProps extends ClusterInstanceOptions
/**
* Common options for creating cluster instances (both serverless and provisioned)
*/
export interface ClusterInstanceProps extends ClusterInstanceOptions{
export interface ClusterInstanceProps extends ClusterInstanceOptions {
/**
* The type of cluster instance to create. Can be either
* provisioned or serverless v2
Expand All @@ -218,13 +171,6 @@ export interface ClusterInstanceProps extends ClusterInstanceOptions{
* @default 2
*/
readonly promotionTier?: number;

/**
* Only used for migrating existing clusters from using `instanceProps` to `writer` and `readers`
*
* @default false
*/
readonly isFromLegacyInstanceProps?: boolean;
}

/**
Expand Down Expand Up @@ -299,6 +245,53 @@ export interface ClusterInstanceOptions {
* @default the cluster parameter group is used
*/
readonly parameterGroup?: IParameterGroup;

/**
* Only used for migrating existing clusters from using `instanceProps` to `writer` and `readers`
*
* @example
* // existing cluster
* declare const vpc: ec2.Vpc;
* const cluster = new rds.DatabaseCluster(this, 'Database', {
* engine: rds.DatabaseClusterEngine.auroraMysql({
* version: rds.AuroraMysqlEngineVersion.VER_3_03_0,
* }),
* instances: 2,
* instanceProps: {
* instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
* vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
* vpc,
* },
* });
*
* // migration
*
* const instanceProps = {
* instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
* isFromLegacyInstanceProps: true,
* };
*
* const myCluster = new rds.DatabaseCluster(this, 'Database', {
* engine: rds.DatabaseClusterEngine.auroraMysql({
* version: rds.AuroraMysqlEngineVersion.VER_3_03_0,
* }),
* vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
* vpc,
* writer: rds.ClusterInstance.provisioned('Instance1', {
* instanceType: instanceProps.instanceType,
* isFromLegacyInstanceProps: instanceProps.isFromLegacyInstanceProps,
* }),
* readers: [
* rds.ClusterInstance.provisioned('Instance2', {
* instanceType: instanceProps.instanceType,
* isFromLegacyInstanceProps: instanceProps.isFromLegacyInstanceProps,
* }),
* ],
* });
*
* @default false
*/
readonly isFromLegacyInstanceProps?: boolean;
}

/**
Expand Down
81 changes: 80 additions & 1 deletion packages/aws-cdk-lib/aws-rds/test/cluster.test.ts
Expand Up @@ -207,7 +207,7 @@ describe('cluster new api', () => {
});

describe('migrate from instanceProps', () => {
test('template contains no changes', () => {
test('template contains no changes (provisioned instances)', () => {
// GIVEN
const stack1 = testStack();
const stack2 = testStack();
Expand Down Expand Up @@ -268,6 +268,85 @@ describe('cluster new api', () => {
test1Template,
).toEqual(Template.fromStack(stack2).toJSON());
});

test('template contains no changes (serverless instances)', () => {
// GIVEN
const stack1 = testStack();
const stack2 = testStack();

function createCase(stack: Stack) {
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
const pg = new ParameterGroup(stack, 'pg', {
engine: DatabaseClusterEngine.AURORA,
});
const sg = new ec2.SecurityGroup(stack, 'sg', {
vpc,
});
const instanceProps = {
instanceType: new ec2.InstanceType('serverless'),
vpc,
allowMajorVersionUpgrade: true,
autoMinorVersionUpgrade: true,
deleteAutomatedBackups: true,
enablePerformanceInsights: true,
parameterGroup: pg,
securityGroups: [sg],
};
return instanceProps;
}
const test1 = createCase(stack1);
const test2 = createCase(stack2);

// Create serverless cluster using workaround described here:
// https://github.com/aws/aws-cdk/issues/20197#issuecomment-1284485844
const workaroundCluster = new DatabaseCluster(stack1, 'Database', {
engine: DatabaseClusterEngine.AURORA,
instanceProps: test1,
iamAuthentication: true,
});

cdk.Aspects.of(workaroundCluster).add({
visit(node) {
if (node instanceof CfnDBCluster) {
node.serverlessV2ScalingConfiguration = {
minCapacity: 1,
maxCapacity: 12,
};
}
},
});

// Create serverless cluster using new/official approach.
// This should provide a non-breaking migration path from the workaround.
new DatabaseCluster(stack2, 'Database', {
engine: DatabaseClusterEngine.AURORA,
vpc: test2.vpc,
securityGroups: test2.securityGroups,
writer: ClusterInstance.serverlessV2('Instance1', {
...test2,
isFromLegacyInstanceProps: true,
}),
readers: [
ClusterInstance.serverlessV2('Instance2', {
...test2,
scaleWithWriter: true,
isFromLegacyInstanceProps: true,
}),
],
iamAuthentication: true,
serverlessV2MinCapacity: 1,
serverlessV2MaxCapacity: 12,
});

// THEN
const test1Template = Template.fromStack(stack1).toJSON();
// deleteAutomatedBackups is not needed on the instance, it is set on the cluster
delete test1Template.Resources.DatabaseInstance1844F58FD.Properties.DeleteAutomatedBackups;
delete test1Template.Resources.DatabaseInstance2AA380DEE.Properties.DeleteAutomatedBackups;
expect(test1Template).toEqual(Template.fromStack(stack2).toJSON());
});
});

describe('creates a writer instance', () => {
Expand Down

0 comments on commit f62c34b

Please sign in to comment.