Skip to content

Commit 364d862

Browse files
astuyvehelen278
andauthored
feat: Handle provisioned concurrency and proactive initialization (#389)
* feat: Handle provisioned concurrency and proactive initialization * feat: lint * feat: lint * feat: lint --------- Co-authored-by: helen278 <helen.li@datadoghq.com>
1 parent 0548d77 commit 364d862

File tree

6 files changed

+70
-18
lines changed

6 files changed

+70
-18
lines changed

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
Logger,
1616
LogLevel,
1717
promisifiedHandler,
18-
setColdStart,
18+
setSandboxInit,
1919
setLogger,
2020
setLogLevel,
2121
} from "./utils";
@@ -92,6 +92,8 @@ if (getEnvValue(coldStartTracingEnvVar, "true").toLowerCase() === "true") {
9292
subscribeToDC();
9393
}
9494

95+
const initTime = Date.now();
96+
9597
/**
9698
* Wraps your AWS lambda handler functions to add tracing/metrics support
9799
* @param handler A lambda handler function.
@@ -131,8 +133,8 @@ export function datadog<TEvent, TResult>(
131133
let wrappedFunc: any;
132134
wrappedFunc = async (...args: any[]) => {
133135
const { event, context, responseStream } = extractArgs(isResponseStreamFunction, ...args);
134-
setColdStart();
135136
const startTime = new Date();
137+
setSandboxInit(initTime, startTime.getTime());
136138

137139
currentMetricsListener = metricsListener;
138140
currentTraceListener = traceListener;

src/metrics/enhanced-metrics.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { sendDistributionMetric } from "../index";
33

44
import { Context } from "aws-lambda";
55
import { parseTagsFromARN } from "../utils/arn";
6-
import { getColdStartTag } from "../utils/cold-start";
6+
import { getSandboxInitTags } from "../utils/cold-start";
77
import { getProcessVersion } from "../utils/process-version";
88
import { writeMetricToStdout } from "./metric-log";
99
import { MetricsListener } from "./listener";
@@ -54,7 +54,7 @@ export function getEnhancedMetricTags(context: Context): string[] {
5454
if (context.invokedFunctionArn) {
5555
arnTags = parseTagsFromARN(context.invokedFunctionArn, context.functionVersion);
5656
}
57-
const tags = [...arnTags, getColdStartTag(), `memorysize:${context.memoryLimitInMB}`, getVersionTag()];
57+
const tags = [...arnTags, ...getSandboxInitTags(), `memorysize:${context.memoryLimitInMB}`, getVersionTag()];
5858

5959
const runtimeTag = getRuntimeTag();
6060
if (runtimeTag) {

src/trace/listener.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { extractTriggerTags, extractHTTPStatusCodeTag } from "./trigger";
1212
import { ColdStartTracerConfig, ColdStartTracer } from "./cold-start-tracer";
1313

1414
import { logDebug, tagObject } from "../utils";
15-
import { didFunctionColdStart } from "../utils/cold-start";
15+
import { didFunctionColdStart, isProactiveInitialization } from "../utils/cold-start";
1616
import { datadogLambdaVersion } from "../constants";
1717
import { Source, ddtraceVersion, parentSpanFinishTimeHeader, authorizingRequestIdHeader } from "./constants";
1818
import { patchConsole } from "./patch-console";
@@ -160,12 +160,15 @@ export class TraceListener {
160160
if (coldStartNodes.length > 0) {
161161
const coldStartConfig: ColdStartTracerConfig = {
162162
tracerWrapper: this.tracerWrapper,
163-
parentSpan: didFunctionColdStart() ? this.inferredSpan || this.wrappedCurrentSpan : this.wrappedCurrentSpan,
163+
parentSpan:
164+
didFunctionColdStart() || isProactiveInitialization()
165+
? this.inferredSpan || this.wrappedCurrentSpan
166+
: this.wrappedCurrentSpan,
164167
lambdaFunctionName: this.context?.functionName,
165168
currentSpanStartTime: this.wrappedCurrentSpan?.startTime(),
166169
minDuration: this.config.minColdStartTraceDuration,
167170
ignoreLibs: this.config.coldStartTraceSkipLib,
168-
isColdStart: didFunctionColdStart(),
171+
isColdStart: didFunctionColdStart() || isProactiveInitialization(),
169172
};
170173
const coldStartTracer = new ColdStartTracer(coldStartConfig);
171174
coldStartTracer.trace(coldStartNodes);
@@ -257,6 +260,9 @@ export class TraceListener {
257260
datadog_lambda: datadogLambdaVersion,
258261
dd_trace: ddtraceVersion,
259262
};
263+
if (isProactiveInitialization()) {
264+
options.tags.proactive_initialization = true;
265+
}
260266
if (
261267
(this.contextService.traceSource === Source.Xray && this.config.mergeDatadogXrayTraces) ||
262268
this.contextService.traceSource === Source.Event

src/utils/cold-start.spec.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
1-
import { _resetColdStart, didFunctionColdStart, setColdStart } from "./cold-start";
1+
import { _resetColdStart, didFunctionColdStart, setSandboxInit, isProactiveInitialization } from "./cold-start";
22

33
beforeEach(_resetColdStart);
44
afterAll(_resetColdStart);
55

66
describe("cold-start", () => {
77
it("identifies cold starts on the first execution", () => {
8-
setColdStart();
8+
setSandboxInit(0, 1);
99
expect(didFunctionColdStart()).toEqual(true);
1010
});
1111

1212
it("identifies non-cold starts on subsequent executions", () => {
13-
setColdStart();
13+
setSandboxInit(0, 1);
1414
expect(didFunctionColdStart()).toEqual(true);
1515

16-
setColdStart();
16+
setSandboxInit(0, 1);
1717
expect(didFunctionColdStart()).toEqual(false);
1818

19-
setColdStart();
19+
setSandboxInit(0, 1);
2020
expect(didFunctionColdStart()).toEqual(false);
2121
});
22+
23+
it("identifies proactive invocations on the first execution", () => {
24+
setSandboxInit(0, 100000);
25+
expect(didFunctionColdStart()).toEqual(false);
26+
expect(isProactiveInitialization()).toEqual(true);
27+
28+
setSandboxInit(0, 1);
29+
expect(didFunctionColdStart()).toEqual(false);
30+
31+
setSandboxInit(0, 1);
32+
expect(didFunctionColdStart()).toEqual(false);
33+
});
34+
35+
it("identifies non-proactive invocations on subsequent invocations", () => {
36+
setSandboxInit(0, 100000);
37+
expect(didFunctionColdStart()).toEqual(false);
38+
expect(isProactiveInitialization()).toEqual(true);
39+
40+
setSandboxInit(0, 100000);
41+
expect(didFunctionColdStart()).toEqual(false);
42+
expect(isProactiveInitialization()).toEqual(false);
43+
44+
setSandboxInit(0, 100000);
45+
expect(didFunctionColdStart()).toEqual(false);
46+
expect(isProactiveInitialization()).toEqual(false);
47+
});
2248
});

src/utils/cold-start.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,45 @@
11
let functionDidColdStart = true;
2+
let proactiveInitialization = false;
23

34
let isColdStartSet = false;
45

56
/**
67
* Use global variables to determine whether the container cold started
8+
* and if the start was proactively initialized
79
* On the first container run, isColdStartSet and functionDidColdStart are true
810
* For subsequent executions isColdStartSet will be true and functionDidColdStart will be false
911
*/
10-
export function setColdStart() {
11-
functionDidColdStart = !isColdStartSet;
12+
export function setSandboxInit(initTime: number, invocationStartTime: number) {
13+
if (!isColdStartSet && invocationStartTime - initTime > 10_000) {
14+
proactiveInitialization = true;
15+
functionDidColdStart = false;
16+
} else {
17+
functionDidColdStart = !isColdStartSet;
18+
proactiveInitialization = false;
19+
}
1220
isColdStartSet = true;
1321
}
1422

15-
export function didFunctionColdStart() {
23+
export function didFunctionColdStart(): boolean {
1624
return functionDidColdStart;
1725
}
1826

19-
export function getColdStartTag() {
20-
return `cold_start:${didFunctionColdStart()}`;
27+
export function isProactiveInitialization(): boolean {
28+
return proactiveInitialization;
29+
}
30+
31+
export function getSandboxInitTags(): string[] {
32+
const tags = [`cold_start:${didFunctionColdStart()}`];
33+
if (isProactiveInitialization()) {
34+
tags.push("proactive_initialization:true");
35+
}
36+
37+
return tags;
2138
}
2239

2340
// For testing, reset the globals to their original values
2441
export function _resetColdStart() {
2542
functionDidColdStart = true;
43+
proactiveInitialization = false;
2644
isColdStartSet = false;
2745
}

src/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { didFunctionColdStart, getColdStartTag, setColdStart } from "./cold-start";
1+
export { didFunctionColdStart, getSandboxInitTags, setSandboxInit, isProactiveInitialization } from "./cold-start";
22
export { wrap, promisifiedHandler } from "./handler";
33
export { Timer } from "./timer";
44
export { logError, logDebug, Logger, setLogLevel, setLogger, LogLevel } from "./log";

0 commit comments

Comments
 (0)