Skip to content

Commit

Permalink
feat(AWS Lambda): functions[].snapStart support (#11576)
Browse files Browse the repository at this point in the history
  • Loading branch information
debae committed Dec 16, 2022
1 parent dd5b8f6 commit adf11b7
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 1 deletion.
16 changes: 16 additions & 0 deletions docs/providers/aws/guide/functions.md
Expand Up @@ -398,6 +398,22 @@ functions:
architecture: arm64
```

## SnapStart

[Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) for Java can improve startup performance for latency-sensitive applications.

To enable SnapStart for your lambda function you can add the `snapStart` object property in the function configuration which can be put to true and will result in the value `PublishedVersions` for this function.

```yaml
functions:
hello:
...
runtime: java11
snapStart: true
```

**Note:** Lambda SnapStart only supports the Java 11 runtime and does not support provisioned concurrency, the arm64 architecture, the Lambda Extensions API, Amazon Elastic File System (Amazon EFS), AWS X-Ray, or ephemeral storage greater than 512 MB.

## VPC Configuration

You can add VPC configuration to a specific function in `serverless.yml` by adding a `vpc` object property in the function configuration. This object should contain the `securityGroupIds` and `subnetIds` array properties needed to construct VPC for this function. Here's an example configuration:
Expand Down
2 changes: 2 additions & 0 deletions docs/providers/aws/guide/serverless.yml.md
Expand Up @@ -641,6 +641,8 @@ functions:
onError: arn:aws:sns:us-east-1:XXXXXX:sns-topic
# KMS key ARN to use for encryption for this function
kmsKeyArn: arn:aws:kms:us-east-1:XXXXXX:key/some-hash
# Defines if you want to make use of SnapStart, this feature can only be used in combination with a Java runtime. Configuring this property will result in either None or PublishedVersions for the Lambda function
snapStart: true
# Disable the creation of the CloudWatch log group
disableLogs: false
# Duration for CloudWatch log retention (default: forever).
Expand Down
6 changes: 6 additions & 0 deletions lib/plugins/aws/deploy-function.js
Expand Up @@ -227,6 +227,12 @@ class AwsDeployFunction {
delete params.KMSKeyArn;
}

if (functionObj.snapStart) {
params.SnapStart = {
ApplyOn: 'PublishedVersions',
};
}

if (
functionObj.description &&
functionObj.description !== remoteFunctionConfiguration.Description
Expand Down
6 changes: 6 additions & 0 deletions lib/plugins/aws/lib/naming.js
Expand Up @@ -186,9 +186,15 @@ module.exports = {
getLambdaProvisionedConcurrencyAliasLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}ProvConcLambdaAlias`;
},
getLambdaSnapStartAliasLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}SnapStartLambdaAlias`;
},
getLambdaProvisionedConcurrencyAliasName() {
return 'provisioned';
},
getLambdaSnapStartEnabledAliasName() {
return 'snapstart';
},
getLambdaFunctionUrlOutputLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}LambdaFunctionUrl`;
},
Expand Down
38 changes: 37 additions & 1 deletion lib/plugins/aws/package/compile/functions.js
Expand Up @@ -447,7 +447,11 @@ class AwsCompileFunctions {
? functionObject.versionFunction
: this.serverless.service.provider.versionFunctions;

if (shouldVersionFunction || functionObject.provisionedConcurrency) {
if (
shouldVersionFunction ||
functionObject.provisionedConcurrency ||
functionObject.snapStart
) {
// Create hashes for the artifact and the logical id of the version resource
// The one for the version resource must include the function configuration
// to make sure that a new version is created on configuration changes and
Expand Down Expand Up @@ -549,6 +553,13 @@ class AwsCompileFunctions {
[functionVersionOutputLogicalId]: newVersionOutput,
});

if (functionObject.provisionedConcurrency && functionObject.snapStart) {
throw new ServerlessError(
`Functions with enabled SnapStart does not support provisioned concurrency. Please remove at least one of the settings on function "${functionName}".`,
'FUNCTION_BOTH_PROVISIONED_CONCURRENCY_AND_SNAPSTART_ENABLED_ERROR'
);
}

if (functionObject.provisionedConcurrency) {
if (!shouldVersionFunction) delete versionResource.DeletionPolicy;

Expand All @@ -574,6 +585,31 @@ class AwsCompileFunctions {

cfTemplate.Resources[aliasLogicalId] = aliasResource;
}

if (functionObject.snapStart) {
if (!shouldVersionFunction) delete versionResource.DeletionPolicy;

functionResource.Properties.SnapStart = {
ApplyOn: 'PublishedVersions',
};

const aliasLogicalId = this.provider.naming.getLambdaSnapStartAliasLogicalId(functionName);
const aliasName = this.provider.naming.getLambdaSnapStartEnabledAliasName();

functionObject.targetAlias = { name: aliasName, logicalId: aliasLogicalId };

const aliasResource = {
Type: 'AWS::Lambda::Alias',
Properties: {
FunctionName: { Ref: functionLogicalId },
FunctionVersion: { 'Fn::GetAtt': [versionLogicalId, 'Version'] },
Name: aliasName,
},
DependsOn: functionLogicalId,
};

cfTemplate.Resources[aliasLogicalId] = aliasResource;
}
}

this.compileFunctionUrl(functionName);
Expand Down
1 change: 1 addition & 0 deletions lib/plugins/aws/provider.js
Expand Up @@ -1370,6 +1370,7 @@ class AwsProvider {
],
},
kmsKeyArn: { $ref: '#/definitions/awsKmsArn' },
snapStart: { type: 'boolean' },
layers: { $ref: '#/definitions/awsLambdaLayers' },
logRetentionInDays: {
$ref: '#/definitions/awsLogRetentionInDays',
Expand Down
54 changes: 54 additions & 0 deletions test/unit/lib/plugins/aws/package/compile/functions.test.js
Expand Up @@ -931,6 +931,40 @@ describe('AwsCompileFunctions', () => {
).to.deep.equal(compiledFunction);
});
});

it('should set function SnapStart ApplyOn to PublishedVersions when enabled', async () => {
const { cfTemplate } = await runServerless({
fixture: 'function',
configExt: {
functions: {
basic: {
snapStart: true,
},
},
},
command: 'package',
});

expect(cfTemplate.Resources.BasicLambdaFunction.Properties.SnapStart).to.deep.equal({
ApplyOn: 'PublishedVersions',
});
});

it('should not configure function SnapStart ApplyOn when disabled', async () => {
const { cfTemplate } = await runServerless({
fixture: 'function',
configExt: {
functions: {
basic: {
snapStart: false,
},
},
},
command: 'package',
});

expect(cfTemplate.Resources.BasicLambdaFunction.Properties).to.not.have.property('SnapStart');
});
});

describe('#compileRole()', () => {
Expand Down Expand Up @@ -2279,6 +2313,26 @@ describe('lib/plugins/aws/package/compile/functions/index.test.js', () => {
expect(error).to.have.property('code', 'LAMBDA_FILE_SYSTEM_CONFIG_MISSING_VPC');
});
});

it('should throw error when `SnapStart` and `ProvisionedConcurrency` is enabled on the function', () => {
return runServerless({
fixture: 'function',
configExt: {
functions: {
basic: {
snapStart: true,
provisionedConcurrency: 10,
},
},
},
command: 'package',
}).catch((error) => {
expect(error).to.have.property(
'code',
'FUNCTION_BOTH_PROVISIONED_CONCURRENCY_AND_SNAPSTART_ENABLED_ERROR'
);
});
});
});

describe('Version hash resolution', () => {
Expand Down

0 comments on commit adf11b7

Please sign in to comment.