Skip to content

Commit b42dbc8

Browse files
authored
feat(neptune-alpha): support for Neptune serverless (#26445)
Adds support for [Neptune serverless](https://docs.aws.amazon.com/neptune/latest/userguide/neptune-serverless-using.html). Example of how to launch a Neptune serverless cluster: ``` new DatabaseCluster(stack, 'Database', { vpc, instanceType: InstanceType.SERVERLESS, clusterParameterGroup, removalPolicy: cdk.RemovalPolicy.DESTROY, serverlessScalingConfiguration: { minCapacity: 1, maxCapacity: 5, }, }); ``` Closes #26428 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f24ece1 commit b42dbc8

14 files changed

+1976
-0
lines changed

packages/@aws-cdk/aws-neptune-alpha/README.md

+18
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,21 @@ instance.metric('SparqlRequestsPerSec') // instance-level SparqlErrors metric
183183
```
184184

185185
For more details on the available metrics, refer to https://docs.aws.amazon.com/neptune/latest/userguide/cw-metrics.html
186+
187+
## Neptune Serverless
188+
189+
You can configure a Neptune Serverless cluster using the dedicated instance type along with the
190+
`serverlessScalingConfiguration` property.
191+
192+
> Visit [Using Amazon Neptune Serverless](https://docs.aws.amazon.com/neptune/latest/userguide/neptune-serverless-using.html) for more details.
193+
194+
```ts
195+
const cluster = new neptune.DatabaseCluster(this, 'ServerlessDatabase', {
196+
vpc,
197+
instanceType: neptune.InstanceType.SERVERLESS,
198+
serverlessScalingConfiguration: {
199+
minCapacity: 1,
200+
maxCapacity: 5,
201+
},
202+
});
203+
```

packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts

+41
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ export class LogType {
104104
public constructor(public readonly value: string) {}
105105
}
106106

107+
export interface ServerlessScalingConfiguration {
108+
/**
109+
* Minimum NCU capacity (min value 1)
110+
*/
111+
readonly minCapacity: number;
112+
113+
/**
114+
* Maximum NCU capacity (min value 2.5 - max value 128)
115+
*/
116+
readonly maxCapacity: number;
117+
}
118+
107119
/**
108120
* Properties for a new database cluster
109121
*/
@@ -297,6 +309,14 @@ export interface DatabaseClusterProps {
297309
* @default - a new role is created.
298310
*/
299311
readonly cloudwatchLogsRetentionRole?: iam.IRole;
312+
313+
/**
314+
* Specify minimum and maximum NCUs capacity for a serverless cluster.
315+
* See https://docs.aws.amazon.com/neptune/latest/userguide/neptune-serverless-capacity-scaling.html
316+
*
317+
* @default - required if instanceType is db.serverless
318+
*/
319+
readonly serverlessScalingConfiguration?: ServerlessScalingConfiguration;
300320
}
301321

302322
/**
@@ -565,6 +585,12 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu
565585

566586
this.enableIamAuthentication = props.iamAuthentication;
567587

588+
if (props.instanceType === InstanceType.SERVERLESS && !props.serverlessScalingConfiguration) {
589+
throw new Error('You need to specify a serverless scaling configuration with a db.serverless instance type.');
590+
}
591+
592+
this.validateServerlessScalingConfiguration(props.serverlessScalingConfiguration);
593+
568594
// Create the Neptune cluster
569595
const cluster = new CfnDBCluster(this, 'Resource', {
570596
// Basic
@@ -585,6 +611,7 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu
585611
// CloudWatch Logs exports
586612
enableCloudwatchLogsExports: props.cloudwatchLogsExports?.map(logType => logType.value),
587613
storageEncrypted,
614+
serverlessScalingConfiguration: props.serverlessScalingConfiguration,
588615
});
589616

590617
cluster.applyRemovalPolicy(props.removalPolicy, {
@@ -649,4 +676,18 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu
649676
securityGroups: securityGroups,
650677
});
651678
}
679+
680+
private validateServerlessScalingConfiguration(serverlessScalingConfiguration?: ServerlessScalingConfiguration) {
681+
if (!serverlessScalingConfiguration) return;
682+
if (serverlessScalingConfiguration.minCapacity < 1) {
683+
throw new Error(`ServerlessScalingConfiguration minCapacity must be greater or equal than 1, received ${serverlessScalingConfiguration.minCapacity}`);
684+
}
685+
if (serverlessScalingConfiguration.maxCapacity < 2.5 || serverlessScalingConfiguration.maxCapacity > 128) {
686+
throw new Error(`ServerlessScalingConfiguration maxCapacity must be between 2.5 and 128, reveived ${serverlessScalingConfiguration.maxCapacity}`);
687+
}
688+
if (serverlessScalingConfiguration.minCapacity >= serverlessScalingConfiguration.maxCapacity) {
689+
throw new Error(`ServerlessScalingConfiguration minCapacity ${serverlessScalingConfiguration.minCapacity} ` +
690+
`must be less than serverlessScalingConfiguration maxCapacity ${serverlessScalingConfiguration.maxCapacity}`);
691+
}
692+
}
652693
}

packages/@aws-cdk/aws-neptune-alpha/lib/instance.ts

+5
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ export class InstanceType {
118118
*/
119119
public static readonly T3_MEDIUM = InstanceType.of('db.t3.medium');
120120

121+
/**
122+
* db.serverless
123+
*/
124+
public static readonly SERVERLESS = InstanceType.of('db.serverless');
125+
121126
/**
122127
* Build an InstanceType from given string or token, such as CfnParameter.
123128
*/

packages/@aws-cdk/aws-neptune-alpha/test/cluster.test.ts

+76
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,82 @@ describe('DatabaseCluster', () => {
754754
Threshold: 1,
755755
});
756756
});
757+
758+
test('should instantiate a serverless cluster', () => {
759+
// GIVEN
760+
const stack = testStack();
761+
const vpc = new ec2.Vpc(stack, 'VPC');
762+
763+
// WHEN
764+
new DatabaseCluster(stack, 'Database', {
765+
vpc,
766+
instanceType: InstanceType.SERVERLESS,
767+
serverlessScalingConfiguration: {
768+
minCapacity: 1,
769+
maxCapacity: 10,
770+
},
771+
});
772+
773+
// THEN
774+
Template.fromStack(stack).hasResource('AWS::Neptune::DBCluster', {
775+
Properties: {
776+
ServerlessScalingConfiguration: {
777+
MinCapacity: 1,
778+
MaxCapacity: 10,
779+
},
780+
},
781+
});
782+
783+
Template.fromStack(stack).hasResourceProperties('AWS::Neptune::DBInstance', {
784+
DBInstanceClass: 'db.serverless',
785+
});
786+
});
787+
788+
test('should validate serverlessScalingConfiguration', () => {
789+
// GIVEN
790+
const stack = testStack();
791+
const vpc = new ec2.Vpc(stack, 'VPC');
792+
793+
expect(() => {
794+
new DatabaseCluster(stack, 'Database0', {
795+
vpc,
796+
instanceType: InstanceType.SERVERLESS,
797+
});
798+
}).toThrow(/You need to specify a serverless scaling configuration with a db.serverless instance type./);
799+
800+
expect(() => {
801+
new DatabaseCluster(stack, 'Database1', {
802+
vpc,
803+
instanceType: InstanceType.SERVERLESS,
804+
serverlessScalingConfiguration: {
805+
minCapacity: 0,
806+
maxCapacity: 10,
807+
},
808+
});
809+
}).toThrow(/ServerlessScalingConfiguration minCapacity must be greater or equal than 1, received 0/);
810+
811+
expect(() => {
812+
new DatabaseCluster(stack, 'Database2', {
813+
vpc,
814+
instanceType: InstanceType.SERVERLESS,
815+
serverlessScalingConfiguration: {
816+
minCapacity: 1,
817+
maxCapacity: 200,
818+
},
819+
});
820+
}).toThrow(/ServerlessScalingConfiguration maxCapacity must be between 2.5 and 128, reveived 200/);
821+
822+
expect(() => {
823+
new DatabaseCluster(stack, 'Database3', {
824+
vpc,
825+
instanceType: InstanceType.SERVERLESS,
826+
serverlessScalingConfiguration: {
827+
minCapacity: 10,
828+
maxCapacity: 5,
829+
},
830+
});
831+
}).toThrow(/ServerlessScalingConfiguration minCapacity 10 must be less than serverlessScalingConfiguration maxCapacity 5/);
832+
});
757833
});
758834

759835
function testStack() {

packages/@aws-cdk/aws-neptune-alpha/test/instance.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,24 @@ describe('DatabaseInstance', () => {
181181
Threshold: 1,
182182
});
183183
});
184+
185+
test('should instantiate a serverless instance', () => {
186+
// GIVEN
187+
const stack = testStack();
188+
189+
// WHEN
190+
new DatabaseInstance(stack, 'Instance', {
191+
cluster: stack.cluster,
192+
instanceType: InstanceType.SERVERLESS,
193+
});
194+
195+
// THEN
196+
Template.fromStack(stack).hasResource('AWS::Neptune::DBInstance', {
197+
Properties: {
198+
DBInstanceClass: 'db.serverless',
199+
},
200+
});
201+
});
184202
});
185203

186204
class TestStack extends cdk.Stack {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "32.0.0",
3+
"files": {
4+
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
5+
"source": {
6+
"path": "ClusterServerlessTestDefaultTestDeployAssert8C9220E6.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"Parameters": {
3+
"BootstrapVersion": {
4+
"Type": "AWS::SSM::Parameter::Value<String>",
5+
"Default": "/cdk-bootstrap/hnb659fds/version",
6+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
7+
}
8+
},
9+
"Rules": {
10+
"CheckBootstrapVersion": {
11+
"Assertions": [
12+
{
13+
"Assert": {
14+
"Fn::Not": [
15+
{
16+
"Fn::Contains": [
17+
[
18+
"1",
19+
"2",
20+
"3",
21+
"4",
22+
"5"
23+
],
24+
{
25+
"Ref": "BootstrapVersion"
26+
}
27+
]
28+
}
29+
]
30+
},
31+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
32+
}
33+
]
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "32.0.0",
3+
"files": {
4+
"2d99c86b451354d15c576c59505ff89003cb23ed1c48a33b0b57bf3eee545c16": {
5+
"source": {
6+
"path": "aws-cdk-neptune-serverless-integ.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "2d99c86b451354d15c576c59505ff89003cb23ed1c48a33b0b57bf3eee545c16.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}

0 commit comments

Comments
 (0)