Skip to content

Commit a1e5208

Browse files
authored
fix(aws-lambda): improved ssm and coldstarts (#2199)
refs https://jsw.ibm.com/browse/INSTA-66082
1 parent 257ed74 commit a1e5208

File tree

3 files changed

+100
-11
lines changed

3 files changed

+100
-11
lines changed

packages/aws-lambda/src/ssm.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ let envValue = null;
1111
let errorFromAWS = null;
1212
let initTimeoutInMs = 0;
1313
let logger;
14+
let coldStart;
1415

1516
module.exports.reset = () => {
1617
fetchedValue = null;
@@ -33,8 +34,9 @@ module.exports.validate = () => {
3334
return true;
3435
};
3536

36-
module.exports.init = config => {
37+
module.exports.init = (config, _coldStart) => {
3738
logger = config.logger;
39+
coldStart = _coldStart;
3840

3941
// CASE: INSTANA_SSM_PARAM_NAME is not set, skip
4042
if (!exports.isUsed()) {
@@ -106,10 +108,15 @@ module.exports.waitAndGetInstanaKey = callback => {
106108
}
107109

108110
const endInMs = Date.now();
109-
const awsTimeoutInMs = process.env.INSTANA_AWS_SSM_TIMEOUT_IN_MS
111+
let awsTimeoutInMs = process.env.INSTANA_AWS_SSM_TIMEOUT_IN_MS
110112
? Number(process.env.INSTANA_AWS_SSM_TIMEOUT_IN_MS)
111113
: 1000;
112114

115+
// CASE: cold start will freeze the handler for a while and we have to allow more time to get the SSM response
116+
if (coldStart) {
117+
awsTimeoutInMs += 2000;
118+
}
119+
113120
// CASE: The time between SSM initialization and waitAndGetInstanaKey is too long to wait for the AWS response.
114121
// See init fn - we fetch the key as early as possible.
115122
if (endInMs - initTimeoutInMs > awsTimeoutInMs) {

packages/aws-lambda/src/wrapper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ function init(event, arnInfo, _config) {
313313
// After core init, because ssm requires require('@aws-sdk/client-ssm'), which triggers
314314
// the requireHook + shimmer. Any module which requires another external module has to be
315315
// initialized after the core.
316-
ssm.init(config);
316+
ssm.init(config, coldStart);
317317

318318
spanBuffer.setIsFaaS(true);
319319
captureHeaders.init(config);

packages/aws-lambda/test/ssm_test.js

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
'use strict';
66

77
const mock = require('@instana/core/test/test_util/mockRequire');
8+
const { createFakeLogger, delay } = require('@instana/core/test/test_util');
89
const sinon = require('sinon');
910
const expect = require('chai').expect;
1011
const ssm = require('../src/ssm');
1112

1213
let sendCommandMock;
1314

14-
describe('Unit: ssm library', () => {
15+
describe('Unit: ssm library', function () {
1516
before(() => {
1617
process.env.AWS_REGION = 'a-region';
1718
});
@@ -38,7 +39,9 @@ describe('Unit: ssm library', () => {
3839
expect(ssm.isUsed()).to.be.true;
3940
});
4041

41-
describe('init & waitAndGetInstanaKey', () => {
42+
describe('init & waitAndGetInstanaKey', function () {
43+
this.timeout(5000);
44+
4245
beforeEach(() => {
4346
sendCommandMock = sinon.stub();
4447

@@ -61,7 +64,7 @@ describe('Unit: ssm library', () => {
6164

6265
it('should not fetch AWS SSM value if ssm env is not set', () => {
6366
ssm.validate();
64-
expect(ssm.init({ logger: sinon.stub() })).to.be.undefined;
67+
expect(ssm.init({ logger: createFakeLogger() })).to.be.undefined;
6568
expect(ssm.isUsed()).to.be.false;
6669
expect(sendCommandMock.callCount).to.equal(0);
6770
});
@@ -70,7 +73,7 @@ describe('Unit: ssm library', () => {
7073
process.env.INSTANA_SSM_PARAM_NAME = 'hello instana agent key';
7174

7275
ssm.validate();
73-
expect(ssm.init({ logger: { debug: sinon.stub(), warn: sinon.stub() } })).to.be.undefined;
76+
expect(ssm.init({ logger: createFakeLogger() })).to.be.undefined;
7477

7578
expect(sendCommandMock.callCount).to.equal(1);
7679
expect(sendCommandMock.getCall(0).args[0].Name).to.equal('hello instana agent key');
@@ -99,7 +102,7 @@ describe('Unit: ssm library', () => {
99102
});
100103
});
101104

102-
expect(ssm.init({ logger: { debug: sinon.stub() } })).to.be.undefined;
105+
expect(ssm.init({ logger: createFakeLogger() })).to.be.undefined;
103106

104107
expect(sendCommandMock.callCount).to.equal(1);
105108
expect(sendCommandMock.getCall(0).args[0].Name).to.equal('hello instana agent key');
@@ -129,7 +132,7 @@ describe('Unit: ssm library', () => {
129132
});
130133
});
131134

132-
expect(ssm.init({ logger: { debug: sinon.stub() } })).to.be.undefined;
135+
expect(ssm.init({ logger: createFakeLogger() })).to.be.undefined;
133136

134137
expect(sendCommandMock.callCount).to.equal(1);
135138
expect(sendCommandMock.getCall(0).args[0].Name).to.equal('hello instana agent key');
@@ -151,9 +154,11 @@ describe('Unit: ssm library', () => {
151154
ssm.validate();
152155
expect(ssm.isUsed()).to.be.true;
153156

157+
let callsFakeCalled = false;
154158
sendCommandMock.callsFake(() => {
155159
return new Promise(resolve => {
156160
setTimeout(() => {
161+
callsFakeCalled = true;
157162
resolve({
158163
Parameter: {
159164
Value: 'instana-value'
@@ -163,7 +168,7 @@ describe('Unit: ssm library', () => {
163168
});
164169
});
165170

166-
expect(ssm.init({ logger: { debug: sinon.stub() } })).to.be.undefined;
171+
expect(ssm.init({ logger: createFakeLogger() })).to.be.undefined;
167172
expect(sendCommandMock.callCount).to.equal(1);
168173

169174
const interval = ssm.waitAndGetInstanaKey((err, value) => {
@@ -172,10 +177,87 @@ describe('Unit: ssm library', () => {
172177
'"hello instana agent key", because we have not received a response from AWS.'
173178
);
174179
expect(value).to.be.undefined;
175-
callback();
180+
181+
const checkIfCalled = () => {
182+
if (callsFakeCalled) {
183+
callback();
184+
} else {
185+
setTimeout(checkIfCalled, 50);
186+
}
187+
};
188+
189+
checkIfCalled();
176190
});
177191

178192
expect(interval).to.exist;
179193
});
194+
195+
it('coldstart is true', cb => {
196+
process.env.INSTANA_SSM_PARAM_NAME = 'hello instana agent key';
197+
198+
ssm.validate();
199+
expect(ssm.isUsed()).to.be.true;
200+
201+
let callsFakeCalled = false;
202+
sendCommandMock.callsFake(() => {
203+
return new Promise(resolve => {
204+
setTimeout(() => {
205+
callsFakeCalled = true;
206+
resolve({
207+
Parameter: {
208+
Value: 'instana-value'
209+
}
210+
});
211+
}, 2100);
212+
});
213+
});
214+
215+
ssm.init({ logger: createFakeLogger() }, true);
216+
expect(sendCommandMock.callCount).to.equal(1);
217+
218+
delay(2000).then(() => {
219+
ssm.waitAndGetInstanaKey(err => {
220+
expect(err).to.not.exist;
221+
222+
const checkIfCalled = () => {
223+
if (callsFakeCalled) {
224+
cb();
225+
} else {
226+
setTimeout(checkIfCalled, 50);
227+
}
228+
};
229+
230+
checkIfCalled();
231+
});
232+
});
233+
});
234+
235+
it('coldstart is false', cb => {
236+
process.env.INSTANA_SSM_PARAM_NAME = 'hello instana agent key';
237+
238+
ssm.validate();
239+
expect(ssm.isUsed()).to.be.true;
240+
241+
sendCommandMock.callsFake(() => {
242+
return new Promise(resolve => {
243+
setTimeout(() => {
244+
resolve({
245+
Parameter: {
246+
Value: 'instana-value'
247+
}
248+
});
249+
}, 900);
250+
});
251+
});
252+
253+
ssm.init({ logger: createFakeLogger() }, false);
254+
255+
delay(800).then(() => {
256+
ssm.waitAndGetInstanaKey(err => {
257+
expect(err).to.not.exist;
258+
cb();
259+
});
260+
});
261+
});
180262
});
181263
});

0 commit comments

Comments
 (0)