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

chore(lambda-nodejs): do not add unused environment variable by default for sdk v3 lambda runtimes #30117

Merged
merged 10 commits into from
May 20, 2024
4 changes: 3 additions & 1 deletion packages/aws-cdk-lib/aws-lambda-nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ All properties of `lambda.Function` can be used to customize the underlying `lam
See also the [AWS Lambda construct library](https://github.com/aws/aws-cdk/tree/main/packages/aws-cdk-lib/aws-lambda).

The `NodejsFunction` construct automatically [reuses existing connections](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html)
when working with the AWS SDK for JavaScript. Set the `awsSdkConnectionReuse` prop to `false` to disable it.
when working with the AWS SDK v2 for JavaScript. Set the `awsSdkConnectionReuse` prop to `false` to disable it.

The AWS SDK v3 for JavaScript does not include the environment variable set by `awsSdkConnectionReuse`. See [this guide](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html) for information about reusing connections. Therefore, for runtimes >= Node 18, which include SDK v3, the prop defaults to `false`, and must be explicitly set to `true` in order for the environment variable to be set.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would setting it to true do anything in sdk v3?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the property does not exist. I believe it has no effect whatsoever.


## Runtime

Expand Down
20 changes: 1 addition & 19 deletions packages/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IConstruct } from 'constructs';
import { PackageInstallation } from './package-installation';
import { LockFile, PackageManager } from './package-manager';
import { BundlingOptions, OutputFormat, SourceMapMode } from './types';
import { exec, extractDependencies, findUp, getTsconfigCompilerOptions } from './util';
import { exec, extractDependencies, findUp, getTsconfigCompilerOptions, isSdkV2Runtime } from './util';
import { Architecture, AssetCode, Code, Runtime } from '../../aws-lambda';
import * as cdk from '../../core';

Expand Down Expand Up @@ -442,24 +442,6 @@ function toCliArgs(esbuildArgs: { [key: string]: string | boolean }): string {
return args.join(' ');
}

/**
* Detect if a given Node.js runtime uses SDKv2
*/
function isSdkV2Runtime(runtime: Runtime): boolean {
const sdkV2RuntimeList = [
Runtime.NODEJS,
Runtime.NODEJS_4_3,
Runtime.NODEJS_6_10,
Runtime.NODEJS_8_10,
Runtime.NODEJS_10_X,
Runtime.NODEJS_12_X,
Runtime.NODEJS_14_X,
Runtime.NODEJS_16_X,
];

return sdkV2RuntimeList.some((r) => {return r.family === runtime.family && r.name === runtime.name;});
}

/**
* Detect if a given Node.js runtime supports ESM (ECMAScript modules)
*/
Expand Down
29 changes: 20 additions & 9 deletions packages/aws-cdk-lib/aws-lambda-nodejs/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { Construct } from 'constructs';
import { Bundling } from './bundling';
import { LockFile } from './package-manager';
import { BundlingOptions } from './types';
import { callsites, findUpMultiple } from './util';
import { callsites, findUpMultiple, isSdkV2Runtime } from './util';
import { Architecture } from '../../aws-lambda';
import * as lambda from '../../aws-lambda';
import { FeatureFlags } from '../../core';
import { Annotations, FeatureFlags } from '../../core';
import { LAMBDA_NODEJS_USE_LATEST_RUNTIME } from '../../cx-api';

/**
Expand Down Expand Up @@ -43,15 +43,19 @@ export interface NodejsFunctionProps extends lambda.FunctionOptions {
readonly runtime?: lambda.Runtime;

/**
* The `AWS_NODEJS_CONNECTION_REUSE_ENABLED` environment variable does not exist in the AWS SDK for JavaScript v3.
*
* This prop will be deprecated when the Lambda Node16 runtime is deprecated on June 12, 2024.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add a @see and the link to where this deadline is mentioned

*
* Info for Node 16 runtimes / SDK v2 users:
*
* Whether to automatically reuse TCP connections when working with the AWS
* SDK for JavaScript.
* SDK for JavaScript v2.
*
* This sets the `AWS_NODEJS_CONNECTION_REUSE_ENABLED` environment variable
* to `1`.
*
* @see https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html
*
* @default true
* @default - false (obsolete) for runtimes >= Node 18, true for runtimes <= Node 16.
*/
readonly awsSdkConnectionReuse?: boolean;

Expand Down Expand Up @@ -116,9 +120,16 @@ export class NodejsFunction extends lambda.Function {
handler: handler.indexOf('.') !== -1 ? `${handler}` : `index.${handler}`,
});

// Enable connection reuse for aws-sdk
if (props.awsSdkConnectionReuse ?? true) {
this.addEnvironment('AWS_NODEJS_CONNECTION_REUSE_ENABLED', '1', { removeInEdge: true });
// Enable connection reuse for aws-sdk v2, do not set for sdk v3
if (isSdkV2Runtime(runtime)) {
if (props.awsSdkConnectionReuse ?? true) {
this.addEnvironment('AWS_NODEJS_CONNECTION_REUSE_ENABLED', '1', { removeInEdge: true });
}
} else {
if (props.awsSdkConnectionReuse) {
Annotations.of(scope).addWarningV2('aws-cdk-lib/aws-lambda-nodejs:unusedSdkEvironmentVariable', 'The AWS_NODEJS_CONNECTION_REUSE_ENABLED environment variable does not exist in SDK v3. You have explicitly set `awsSdkConnectionReuse`; please make sure this is intentional.');
this.addEnvironment('AWS_NODEJS_CONNECTION_REUSE_ENABLED', '1', { removeInEdge: true });
Comment on lines +133 to +134
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this env variable do anything in sdk v3? Is this for backward compatibility?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this so users can set it if need be (they can also configure their environment variables directly). I figured some users may define >= Node 18 runtimes, but bundle the sdk v2 into their v2 handlers, and this would be for ease of use / backwards compatibility.

}
}
}

Expand Down
19 changes: 19 additions & 0 deletions packages/aws-cdk-lib/aws-lambda-nodejs/lib/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { spawnSync, SpawnSyncOptions } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { Runtime } from '../../aws-lambda';

export interface CallSite {
getThis(): any;
Expand Down Expand Up @@ -209,3 +210,21 @@ function extractTsConfig(tsconfigPath: string, previousCompilerOptions?: Record<
}
return updatedCompilerOptions;
}

/**
* Detect if a given Node.js runtime uses SDKv2
*/
export function isSdkV2Runtime(runtime: Runtime): boolean {
const sdkV2RuntimeList = [
Runtime.NODEJS,
Runtime.NODEJS_4_3,
Runtime.NODEJS_6_10,
Runtime.NODEJS_8_10,
Runtime.NODEJS_10_X,
Runtime.NODEJS_12_X,
Runtime.NODEJS_14_X,
Runtime.NODEJS_16_X,
];

return sdkV2RuntimeList.some((r) => {return r.family === runtime.family && r.name === runtime.name;});
}
74 changes: 62 additions & 12 deletions packages/aws-cdk-lib/aws-lambda-nodejs/test/function.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bockfs } from '@aws-cdk/cdk-build-tools';
import { Template, Match } from '../../assertions';
import { Annotations, Template, Match } from '../../assertions';
import { Vpc } from '../../aws-ec2';
import { CodeConfig, Runtime } from '../../aws-lambda';
import { App, Stack } from '../../core';
Expand Down Expand Up @@ -303,19 +303,69 @@ test('defaults to NODEJS_16_X with feature flag disabled', () => {
});
});

test('defaults to NODEJS_LATEST with feature flag enabled', () => {
// GIVEN
const appLocal = new App({
context: {
[LAMBDA_NODEJS_USE_LATEST_RUNTIME]: true,
},
describe('Node 18+ runtimes', () => {
test('defaults to NODEJS_LATEST with feature flag enabled', () => {
// GIVEN
const appFF = new App({
context: {
[LAMBDA_NODEJS_USE_LATEST_RUNTIME]: true,
},
});

const stackFF = new Stack(appFF, 'TestStackFF');

// WHEN
new NodejsFunction(stackFF, 'handler1');

Template.fromStack(stackFF).hasResourceProperties('AWS::Lambda::Function', {
Runtime: 'nodejs18.x',
});
});
const stackLocal = new Stack(appLocal, 'TestStackFF');

// WHEN
new NodejsFunction(stackLocal, 'handler1');
test('connection reuse for aws sdk v2 not set by default', () => {
// WHEN
new NodejsFunction(stack, 'handler1', {
runtime: Runtime.NODEJS_18_X,
});

Template.fromStack(stackLocal).hasResourceProperties('AWS::Lambda::Function', {
Runtime: 'nodejs18.x',
// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
Environment: Match.absent(),
});
});

test('connection reuse for aws sdk v2 can be explicitly not set', () => {
// WHEN
new NodejsFunction(stack, 'handler1', {
runtime: Runtime.NODEJS_18_X,
awsSdkConnectionReuse: false,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
Environment: Match.absent(),
});
});

test('setting connection reuse for aws sdk v2 has warning', () => {
// WHEN
new NodejsFunction(stack, 'handler1', {
runtime: Runtime.NODEJS_18_X,
awsSdkConnectionReuse: true,
});

// THEN
Annotations.fromStack(stack).hasWarning('*',
'The AWS_NODEJS_CONNECTION_REUSE_ENABLED environment variable does not exist in SDK v3. You have explicitly set `awsSdkConnectionReuse`; please make sure this is intentional. [ack: aws-cdk-lib/aws-lambda-nodejs:unusedSdkEvironmentVariable]',
);
// AND
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
Environment: {
Variables: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
},
},
});
});
});