Skip to content

Commit

Permalink
feat(autoscaling): support warm pools (#19214)
Browse files Browse the repository at this point in the history
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*

### Summary

This PR adds an interface to create a warm pool easily, which gives an ability to decrease latency for applications that have exceptionally long boot times: https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html

### Test

- Run unit test and integration test.
```
PASS test/warm-pool.test.js
...
=============================== Coverage summary ===============================
Statements   : 93.22% ( 413/443 )
Branches     : 87% ( 288/331 )
Functions    : 93.39% ( 99/106 )
Lines        : 92.95% ( 396/426 )
================================================================================
...
Verifying integ.warm-pool.js against integ.warm-pool.expected.json ... OK.
...
```

### Notes
- I borrowed description from [AWS public doc](https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_PutWarmPool.html) for docstring.
  • Loading branch information
jungseoklee committed Mar 23, 2022
1 parent 7104d2c commit 737e611
Show file tree
Hide file tree
Showing 8 changed files with 750 additions and 0 deletions.
21 changes: 21 additions & 0 deletions packages/@aws-cdk/aws-autoscaling/README.md
Expand Up @@ -488,6 +488,27 @@ const aspect = new autoscaling.AutoScalingGroupRequireImdsv2Aspect();
Aspects.of(this).add(aspect);
```

## Warm Pool

Auto Scaling offers [a warm pool](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html) which gives an ability to decrease latency for applications that have exceptionally long boot times. You can create a warm pool with default parameters as below:

```ts
declare const autoScalingGroup: autoscaling.AutoScalingGroup;

autoScalingGroup.addWarmPool();
```

You can also customize a warm pool by configuring parameters:

```ts
declare const autoScalingGroup: autoscaling.AutoScalingGroup;

autoScalingGroup.addWarmPool({
minSize: 1,
reuseOnScaleIn: true,
});
```

## Future work

* [ ] CloudWatch Events (impossible to add currently as the AutoScalingGroup ARN is
Expand Down
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
Expand Up @@ -23,6 +23,7 @@ import { BasicStepScalingPolicyProps, StepScalingPolicy } from './step-scaling-p
import { BaseTargetTrackingProps, PredefinedMetric, TargetTrackingScalingPolicy } from './target-tracking-scaling-policy';
import { TerminationPolicy } from './termination-policy';
import { BlockDevice, BlockDeviceVolume, EbsDeviceVolumeType } from './volume';
import { WarmPool, WarmPoolOptions } from './warm-pool';

/**
* Name tag constant
Expand Down Expand Up @@ -744,6 +745,16 @@ abstract class AutoScalingGroupBase extends Resource implements IAutoScalingGrou
});
}

/**
* Add a pool of pre-initialized EC2 instances that sits alongside an Auto Scaling group
*/
public addWarmPool(options?: WarmPoolOptions): WarmPool {
return new WarmPool(this, 'WarmPool', {
autoScalingGroup: this,
...options,
});
}

/**
* Scale out or in based on time
*/
Expand Down Expand Up @@ -1630,6 +1641,11 @@ export interface IAutoScalingGroup extends IResource, iam.IGrantable {
*/
addLifecycleHook(id: string, props: BasicLifecycleHookProps): LifecycleHook;

/**
* Add a pool of pre-initialized EC2 instances that sits alongside an Auto Scaling group
*/
addWarmPool(options?: WarmPoolOptions): WarmPool;

/**
* Scale out or in based on time
*/
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-autoscaling/lib/index.ts
Expand Up @@ -9,6 +9,7 @@ export * from './step-scaling-policy';
export * from './target-tracking-scaling-policy';
export * from './termination-policy';
export * from './volume';
export * from './warm-pool';

// AWS::AutoScaling CloudFormation Resources:
export * from './autoscaling.generated';
104 changes: 104 additions & 0 deletions packages/@aws-cdk/aws-autoscaling/lib/warm-pool.ts
@@ -0,0 +1,104 @@
import { Lazy, Names, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { IAutoScalingGroup } from './auto-scaling-group';
import { CfnWarmPool } from './autoscaling.generated';

/**
* Options for a warm pool
*/
export interface WarmPoolOptions {
/**
* Indicates whether instances in the Auto Scaling group can be returned to the warm pool on scale in.
*
* If the value is not specified, instances in the Auto Scaling group will be terminated
* when the group scales in.
*
* @default false
*/
readonly reuseOnScaleIn?: boolean;

/**
* The maximum number of instances that are allowed to be in the warm pool
* or in any state except Terminated for the Auto Scaling group.
*
* If the value is not specified, Amazon EC2 Auto Scaling launches and maintains
* the difference between the group's maximum capacity and its desired capacity.
*
* @default - max size of the Auto Scaling group
*/
readonly maxGroupPreparedCapacity?: number;
/**
* The minimum number of instances to maintain in the warm pool.
*
* @default 0
*/
readonly minSize?: number;
/**
* The instance state to transition to after the lifecycle actions are complete.
*
* @default PoolState.STOPPED
*/
readonly poolState?: PoolState;
}

/**
* Properties for a warm pool
*/
export interface WarmPoolProps extends WarmPoolOptions {
/**
* The Auto Scaling group to add the warm pool to.
*/
readonly autoScalingGroup: IAutoScalingGroup;
}

/**
* Define a warm pool
*/
export class WarmPool extends Resource {
constructor(scope: Construct, id: string, props: WarmPoolProps) {
super(scope, id, {
physicalName: Lazy.string({ produce: () => Names.uniqueId(this) }),
});

if (props.maxGroupPreparedCapacity && props.maxGroupPreparedCapacity < -1) {
throw new Error('\'maxGroupPreparedCapacity\' parameter should be greater than or equal to -1');
}

if (props.minSize && props.minSize < 0) {
throw new Error('\'minSize\' parameter should be greater than or equal to 0');
}

new CfnWarmPool(this, 'Resource', {
autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName,
instanceReusePolicy: props.reuseOnScaleIn !== undefined ? {
reuseOnScaleIn: props.reuseOnScaleIn,
} : undefined,
maxGroupPreparedCapacity: props.maxGroupPreparedCapacity,
minSize: props.minSize,
poolState: props.poolState,
});
}
}

/**
* The instance state in the warm pool
*/
export enum PoolState {
/**
* Hibernated
*
* To use this state, prerequisites must be in place.
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hibernating-prerequisites.html
*/
HIBERNATED = 'Hibernated',

/**
* Running
*/
RUNNING = 'Running',

/**
* Stopped
*/
STOPPED = 'Stopped',
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-autoscaling/package.json
Expand Up @@ -122,6 +122,7 @@
"export:@aws-cdk/aws-autoscaling.IAutoScalingGroup",
"props-physical-name:@aws-cdk/aws-autoscaling.AutoScalingGroupProps",
"props-physical-name:@aws-cdk/aws-autoscaling.ScheduledActionProps",
"props-physical-name:@aws-cdk/aws-autoscaling.WarmPoolProps",
"props-default-doc:@aws-cdk/aws-autoscaling.EbsDeviceOptionsBase.iops",
"docs-public-apis:@aws-cdk/aws-autoscaling.ScalingProcess.ADD_TO_LOAD_BALANCER",
"docs-public-apis:@aws-cdk/aws-autoscaling.ScalingProcess.SCHEDULED_ACTIONS",
Expand Down

0 comments on commit 737e611

Please sign in to comment.