diff --git a/lambda/analyzer.js b/lambda/analyzer.js index 4c0d5caa..3a4e326d 100644 --- a/lambda/analyzer.js +++ b/lambda/analyzer.js @@ -74,6 +74,7 @@ const extractStatistics = (event) => { power: stat.value, cost: stat.averagePrice, duration: stat.averageDuration, + initDuration: stat.initDuration, totalCost: stat.totalCost, })); }; diff --git a/lambda/executor.js b/lambda/executor.js index 0fff1eba..29ad9e4d 100644 --- a/lambda/executor.js +++ b/lambda/executor.js @@ -145,6 +145,11 @@ const computeStatistics = (baseCost, results, value, discardTopBottom) => { const averageDuration = utils.computeAverageDuration(durations, discardTopBottom); console.log('Average duration: ', averageDuration); + const initDurations = utils.parseLogAndExtractInitDurations(results); + + const [initDuration] = initDurations; + console.log('Init duration: ', initDuration); + // ... and overall statistics const averagePrice = utils.computePrice(baseCost, minRAM, value, averageDuration); @@ -154,6 +159,7 @@ const computeStatistics = (baseCost, results, value, discardTopBottom) => { const stats = { averagePrice, averageDuration, + initDuration, totalCost, value, }; diff --git a/lambda/utils.js b/lambda/utils.js index a03fcfad..557040ad 100644 --- a/lambda/utils.js +++ b/lambda/utils.js @@ -73,7 +73,7 @@ module.exports.createPowerConfiguration = async(lambdaARN, value, alias) => { try { await utils.setLambdaPower(lambdaARN, value); - // wait for functoin update to complete + // wait for function update to complete await utils.waitForFunctionUpdate(lambdaARN); const {Version} = await utils.publishLambdaVersion(lambdaARN); @@ -432,6 +432,15 @@ module.exports.parseLogAndExtractDurations = (data) => { }); }; +module.exports.parseLogAndExtractInitDurations = (data) => { + return data + .map((log) => { + const logString = utils.base64decode(log.LogResult || ''); + return utils.extractInitDuration(logString); + }) + // remove logs with no init duration (after cold start) + .filter((duration) => duration > 0); +}; /** * Compute total cost */ @@ -489,6 +498,16 @@ module.exports.extractDuration = (log) => { const durationStr = durationSplit[1].split(' ms')[0]; return parseFloat(durationStr); }; +/** + * Extract init duration (in ms) from a given Lambda's CloudWatch log. + */ +module.exports.extractInitDuration = (log) => { + const initDurationMatches = log.match(/\tInit Duration: ([0-9\.]*) ms/); + if (initDurationMatches === null) return 0; + + const durationStr = initDurationMatches[1]; + return parseFloat(durationStr); +}; /** * Encode a given string to base64. diff --git a/test/unit/test-lambda.js b/test/unit/test-lambda.js index 01499b5e..46d6db03 100644 --- a/test/unit/test-lambda.js +++ b/test/unit/test-lambda.js @@ -322,7 +322,7 @@ describe('Lambda Functions', async() => { await invokeForSuccess(handler, eventOK); }); - it('should fail is something goes wrong with the cleaup API calls', async() => { + it('should fail is something goes wrong with the cleanup API calls', async() => { deleteLambdaVersionStub && deleteLambdaVersionStub.restore(); deleteLambdaVersionStub = sandBox.stub(utils, 'deleteLambdaVersion') .callsFake(async() => { @@ -357,6 +357,15 @@ describe('Lambda Functions', async() => { .callsFake(async(_arn, _alias, payload) => { invokeLambdaCounter++; invokeLambdaPayloads.push(payload); + if (invokeLambdaCounter === 1){ + // first invocation log contains init duration + return { + StatusCode: 200, + LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJSW5pdCBEdXJhdGlvbjogMzQ1LjgxIG1zCQo=', + ExecutedVersion: '$LATEST', + Payload: '{}', + }; + } // logs will always return 1ms duration with 128MB return { StatusCode: 200, @@ -454,6 +463,7 @@ describe('Lambda Functions', async() => { expect(response).to.be.an('object'); expect(response.averagePrice).to.be.a('number'); expect(response.averageDuration).to.be.a('number'); + expect(response.initDuration).to.be.a('number'); expect(response.totalCost).to.be.a('number'); expect(parseFloat(response.totalCost.toPrecision(10))).to.be(2.1e-8); // 10ms in total expect(getLambdaArchitectureCounter).to.be(1); @@ -471,6 +481,7 @@ describe('Lambda Functions', async() => { expect(response).to.be.an('object'); expect(response.averagePrice).to.be.a('number'); expect(response.averageDuration).to.be.a('number'); + expect(response.initDuration).to.be.a('number'); expect(response.totalCost).to.be.a('number'); expect(parseFloat(response.totalCost.toPrecision(10))).to.be(2.8e-8); // 10ms in total expect(getLambdaArchitectureCounter).to.be(1); @@ -835,6 +846,7 @@ describe('Lambda Functions', async() => { }); expect(stats.averagePrice).to.be.a('number'); expect(stats.averageDuration).to.be.a('number'); + expect(stats.initDuration).to.be.a('number'); expect(getLambdaArchitectureCounter).to.be(1); }); @@ -912,7 +924,7 @@ describe('Lambda Functions', async() => { expect(getLambdaArchitectureCounter).to.be(1); }); - it('should invoke function with original payload if pre-precessor does not return a new payload', async() => { + it('should invoke function with original payload if pre-processor does not return a new payload', async() => { invokeLambdaProcessorStub && invokeLambdaProcessorStub.restore(); invokeLambdaProcessorStub = sandBox.stub(utils, 'invokeLambdaProcessor') @@ -1253,9 +1265,9 @@ describe('Lambda Functions', async() => { it('should also return the total cost of execution', async() => { const event = { stats: [ - { value: '128', averagePrice: 100, averageDuration: 100, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 300, totalCost: 2 }, - { value: '512', averagePrice: 30, averageDuration: 200, totalCost: 3 }, + { value: '128', averagePrice: 100, averageDuration: 100, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 300, initDuration: 450, totalCost: 2 }, + { value: '512', averagePrice: 30, averageDuration: 200, initDuration: 500, totalCost: 3 }, ], }; @@ -1264,6 +1276,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(30); expect(result.duration).to.be(200); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(6); @@ -1272,9 +1285,9 @@ describe('Lambda Functions', async() => { it('should also return visualization URL', async() => { const event = { stats: [ - { value: '128', averagePrice: 100, averageDuration: 100, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 300, totalCost: 2 }, - { value: '512', averagePrice: 30, averageDuration: 200, totalCost: 3 }, + { value: '128', averagePrice: 100, averageDuration: 100, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 300, initDuration: 450, totalCost: 2 }, + { value: '512', averagePrice: 30, averageDuration: 200, initDuration: 500, totalCost: 3 }, ], }; @@ -1287,9 +1300,9 @@ describe('Lambda Functions', async() => { it('should return the cheapest power configuration if no strategy', async() => { const event = { stats: [ - { value: '128', averagePrice: 100, averageDuration: 100, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 300, totalCost: 3 }, - { value: '512', averagePrice: 30, averageDuration: 200, totalCost: 5 }, + { value: '128', averagePrice: 100, averageDuration: 100, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 300, initDuration: 450, totalCost: 3 }, + { value: '512', averagePrice: 30, averageDuration: 200, initDuration: 500, totalCost: 5 }, ], }; @@ -1298,6 +1311,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(30); expect(result.duration).to.be(200); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(9); @@ -1307,9 +1321,9 @@ describe('Lambda Functions', async() => { const event = { strategy: 'cost', stats: [ - { value: '128', averagePrice: 100, averageDuration: 100, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 300, totalCost: 6 }, - { value: '512', averagePrice: 30, averageDuration: 200, totalCost: 9 }, + { value: '128', averagePrice: 100, averageDuration: 100, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 300, initDuration: 450, totalCost: 6 }, + { value: '512', averagePrice: 30, averageDuration: 200, initDuration: 500, totalCost: 9 }, ], }; @@ -1318,6 +1332,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(30); expect(result.duration).to.be(200); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(16); @@ -1327,9 +1342,9 @@ describe('Lambda Functions', async() => { const event = { strategy: 'cost', stats: [ - { value: '128', averagePrice: 100, averageDuration: 100, totalCost: 1 }, - { value: '256', averagePrice: 100, averageDuration: 90, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 200, totalCost: 9 }, + { value: '128', averagePrice: 100, averageDuration: 100, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 100, averageDuration: 90, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 200, initDuration: 500, totalCost: 9 }, ], }; @@ -1338,6 +1353,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('256'); expect(result.cost).to.be(100); expect(result.duration).to.be(90); + expect(result.initDuration).to.be(450); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(11); @@ -1347,9 +1363,9 @@ describe('Lambda Functions', async() => { const event = { strategy: 'speed', stats: [ - { value: '128', averagePrice: 100, averageDuration: 300, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 200, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 100, totalCost: 1 }, + { value: '128', averagePrice: 100, averageDuration: 300, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 200, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 100, initDuration: 500, totalCost: 1 }, ], }; @@ -1358,6 +1374,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(300); expect(result.duration).to.be(100); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(3); @@ -1367,9 +1384,9 @@ describe('Lambda Functions', async() => { const event = { strategy: 'speed', stats: [ - { value: '128', averagePrice: 100, averageDuration: 200, totalCost: 1 }, - { value: '256', averagePrice: 90, averageDuration: 200, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 400, totalCost: 1 }, + { value: '128', averagePrice: 100, averageDuration: 200, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 90, averageDuration: 200, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 400, initDuration: 500, totalCost: 1 }, ], }; @@ -1378,6 +1395,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('256'); expect(result.cost).to.be(90); expect(result.duration).to.be(200); + expect(result.initDuration).to.be(450); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(3); @@ -1388,9 +1406,9 @@ describe('Lambda Functions', async() => { strategy: 'balanced', balancedWeight: 1, stats: [ - { value: '128', averagePrice: 100, averageDuration: 100, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 300, totalCost: 6 }, - { value: '512', averagePrice: 30, averageDuration: 200, totalCost: 9 }, + { value: '128', averagePrice: 100, averageDuration: 100, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 300, initDuration: 450, totalCost: 6 }, + { value: '512', averagePrice: 30, averageDuration: 200, initDuration: 500, totalCost: 9 }, ], }; @@ -1399,6 +1417,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(30); expect(result.duration).to.be(200); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(16); @@ -1409,9 +1428,9 @@ describe('Lambda Functions', async() => { strategy: 'balanced', balancedWeight: 0, stats: [ - { value: '128', averagePrice: 100, averageDuration: 300, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 200, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 100, totalCost: 1 }, + { value: '128', averagePrice: 100, averageDuration: 300, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 200, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 100, initDuration: 500, totalCost: 1 }, ], }; @@ -1420,6 +1439,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(300); expect(result.duration).to.be(100); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(3); @@ -1429,9 +1449,9 @@ describe('Lambda Functions', async() => { const event = { strategy: 'balanced', stats: [ - { value: '128', averagePrice: 101, averageDuration: 300, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 200, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 101, totalCost: 1 }, + { value: '128', averagePrice: 101, averageDuration: 300, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 200, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 101, initDuration: 500, totalCost: 1 }, ], }; @@ -1440,6 +1460,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('256'); expect(result.cost).to.be(200); expect(result.duration).to.be(200); + expect(result.initDuration).to.be(450); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(3)); expect(result.stateMachine.lambdaCost).to.be(3); @@ -1450,10 +1471,10 @@ describe('Lambda Functions', async() => { strategy: 'balanced', balancedWeight: 0.3, stats: [ - { value: '128', averagePrice: 100, averageDuration: 300, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 200, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 100, totalCost: 1 }, - { value: '1024', averagePrice: 1000, averageDuration: 50, totalCost: 1 }, + { value: '128', averagePrice: 100, averageDuration: 300, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 200, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 100, initDuration: 500, totalCost: 1 }, + { value: '1024', averagePrice: 1000, averageDuration: 50, initDuration: 300, totalCost: 1 }, ], }; @@ -1462,6 +1483,7 @@ describe('Lambda Functions', async() => { expect(result.power).to.be('512'); expect(result.cost).to.be(300); expect(result.duration).to.be(100); + expect(result.initDuration).to.be(500); expect(result.stateMachine).to.be.an('object'); expect(result.stateMachine.executionCost).to.be(utils.stepFunctionsCost(4)); expect(result.stateMachine.lambdaCost).to.be(4); @@ -1471,7 +1493,7 @@ describe('Lambda Functions', async() => { const event = { strategy: 'cost', stats: [ - { value: '128', averagePrice: 100, averageDuration: 300, totalCost: 1 }, + { value: '128', averagePrice: 100, averageDuration: 300, initDuration: 450, totalCost: 1 }, ], dryRun: true, }; @@ -1494,9 +1516,9 @@ describe('Lambda Functions', async() => { const event = { strategy: strategy, stats: [ - { value: '128', averagePrice: 100, averageDuration: 300, totalCost: 1 }, - { value: '256', averagePrice: 200, averageDuration: 200, totalCost: 1 }, - { value: '512', averagePrice: 300, averageDuration: 100, totalCost: 1 }, + { value: '128', averagePrice: 100, averageDuration: 300, initDuration: 600, totalCost: 1 }, + { value: '256', averagePrice: 200, averageDuration: 200, initDuration: 450, totalCost: 1 }, + { value: '512', averagePrice: 300, averageDuration: 100, initDuration: 500, totalCost: 1 }, ], }; diff --git a/test/unit/test-utils.js b/test/unit/test-utils.js index 98a7e929..348d5904 100644 --- a/test/unit/test-utils.js +++ b/test/unit/test-utils.js @@ -160,6 +160,22 @@ describe('Lambda Utils', () => { }); }); + describe('extractInitDuration', () => { + const log = + 'START RequestId: 55bc566d-1e2c-11e7-93e6-6705ceb4c1cc Version: $LATEST\n' + + 'END RequestId: 55bc566d-1e2c-11e7-93e6-6705ceb4c1cc\n' + + 'REPORT RequestId: 55bc566d-1e2c-11e7-93e6-6705ceb4c1cc\tDuration: 469.40 ms\tBilled Duration: 500 ms\tMemory Size: 1024 MB\tMax Memory Used: 21 MB\tInit Duration: 340.74 ms' + ; + it('should extract the duration from a Lambda log', () => { + expect(utils.extractInitDuration(log)).to.be(340.74); + }); + it('should return 0 if duration is not found', () => { + expect(utils.extractInitDuration('hello world')).to.be(0); + const partialLog = 'START RequestId: 55bc566d-1e2c-11e7-93e6-6705ceb4c1cc Version: $LATEST\n'; + expect(utils.extractInitDuration(partialLog)).to.be(0); + }); + }); + describe('computePrice', () => { const minCost = 2.1e-9; // $ per ms const minRAM = 128; // MB @@ -175,7 +191,7 @@ describe('Lambda Utils', () => { describe('parseLogAndExtractDurations', () => { const results = [ - // 1s (will be discarted) + // 1s (will be discarded) { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, // 1s { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, @@ -183,7 +199,7 @@ describe('Lambda Utils', () => { { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMi4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, // 3s { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMy4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, - // 3s (will be discarted) + // 3s (will be discarded) { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMy4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, ]; @@ -208,6 +224,41 @@ describe('Lambda Utils', () => { }); }); + describe('parseLogAndExtractInitDurations', () => { + const results = [ + // 1s (with init duration) + { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJSW5pdCBEdXJhdGlvbjogMzQ1LjgxIG1zCQo=', Payload: 'null' }, + // 1s (no init duration) + { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, + // 2s (no init duration) + { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMi4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, + // 3s (no init duration) + { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMy4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, + // 3s (no init duration) + { StatusCode: 200, LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMy4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==', Payload: 'null' }, + ]; + + it('should return the list of durations', () => { + const durations = utils.parseLogAndExtractInitDurations(results); + expect(durations).to.be.a('array'); + expect(durations.length).to.be(1); + expect(durations).to.eql([345.81]); + }); + it('should return empty list if empty results', () => { + const durations = utils.parseLogAndExtractInitDurations([]); + expect(durations).to.be.an('array'); + expect(durations.length).to.be(0); + }); + + it('should not explode if missing logs', () => { + const durations = utils.parseLogAndExtractInitDurations([ + { StatusCode: 200, Payload: 'null' }, + ]); + expect(durations).to.be.an('array'); + expect(durations).to.eql([]); + }); + }); + describe('computeAverageDuration', () => { const durations = [ // keep 5 values because it's the minimum length