• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alexcasalboni / aws-lambda-power-tuning / 4345884293

pending completion
4345884293

Pull #194

github

GitHub
<a href="https://github.com/alexcasalboni/aws-lambda-power-tuning/commit/<a class=hub.com/alexcasalboni/aws-lambda-power-tuning/commit/fd841d1b72d65ce294bc101ff104ec12843c80b1">fd841d1b7<a href="https://github.com/alexcasalboni/aws-lambda-power-tuning/commit/fd841d1b72d65ce294bc101ff104ec12843c80b1">">Merge </a><a class="double-link" href="https://github.com/alexcasalboni/aws-lambda-power-tuning/commit/<a class="double-link" href="https://github.com/alexcasalboni/aws-lambda-power-tuning/commit/389479c42ba778034a9fcfb43f8e27461fb4145e">389479c42</a>">389479c42</a><a href="https://github.com/alexcasalboni/aws-lambda-power-tuning/commit/fd841d1b72d65ce294bc101ff104ec12843c80b1"> into 01d5277ec">01d5277ec</a>
Pull Request #194: Add sam build to GH actions

168 of 168 branches covered (100.0%)

Branch coverage included in aggregate %.

453 of 453 relevant lines covered (100.0%)

51.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/lambda/executor.js
1
'use strict';
2

3
const AWS = require('aws-sdk');
1✔
4

5
// the executor needs a longer socket timeout to invoke long-running functions
6
// 15 minutes is fine here because the Executor will timeout anyway
7
AWS.config.update({httpOptions: {timeout: 15 * 60 * 1000}});
1✔
8

9
const utils = require('./utils');
1✔
10

11
const minRAM = parseInt(process.env.minRAM, 10);
1✔
12

13
/**
14
 * Execute the given function N times in series or in parallel.
15
 * Then compute execution statistics (average cost and duration).
16
 */
17
module.exports.handler = async(event, context) => {
1✔
18
    // read input from event
19
    let {
20
        lambdaARN,
21
        value,
22
        num,
23
        enableParallel,
24
        payload,
25
        dryRun,
26
        preProcessorARN,
27
        postProcessorARN,
28
        discardTopBottom,
29
        sleepBetweenRunsMs,
30
    } = await extractDataFromInput(event);
69✔
31

32
    validateInput(lambdaARN, value, num); // may throw
62✔
33

34
    // force only 1 execution if dryRun
35
    if (dryRun) {
49✔
36
        console.log('[Dry-run] forcing num=1');
1✔
37
        num = 1;
1✔
38
    }
39

40
    const lambdaAlias = 'RAM' + value;
49✔
41
    let results;
42

43
    // fetch architecture from $LATEST
44
    const {architecture, isPending} = await utils.getLambdaConfig(lambdaARN, lambdaAlias);
49✔
45
    console.log(`Detected architecture type: ${architecture}, isPending: ${isPending}`);
49✔
46

47
    // pre-generate an array of N payloads
48
    const payloads = utils.generatePayloads(num, payload);
49✔
49

50
    const runInput = {
42✔
51
        num: num,
52
        lambdaARN: lambdaARN,
53
        lambdaAlias: lambdaAlias,
54
        payloads: payloads,
55
        preARN: preProcessorARN,
56
        postARN: postProcessorARN,
57
        sleepBetweenRunsMs: sleepBetweenRunsMs,
58
    };
59

60
    // wait if the function/alias state is Pending
61
    if (isPending) {
42✔
62
        await utils.waitForAliasActive(lambdaARN, lambdaAlias);
1✔
63
        console.log('Alias active');
1✔
64
    }
65

66
    if (enableParallel) {
42✔
67
        results = await runInParallel(runInput);
9✔
68
    } else {
69
        results = await runInSeries(runInput);
33✔
70
    }
71

72
    // get base cost for Lambda
73
    const baseCost = utils.lambdaBaseCost(utils.regionFromARN(lambdaARN), architecture);
32✔
74

75
    return computeStatistics(baseCost, results, value, discardTopBottom);
32✔
76
};
77

78
const validateInput = (lambdaARN, value, num) => {
1✔
79
    if (!lambdaARN) {
62✔
80
        throw new Error('Missing or empty lambdaARN');
4✔
81
    }
82
    if (!value || isNaN(value)) {
58✔
83
        throw new Error('Invalid value: ' + value);
4✔
84
    }
85
    if (!num || isNaN(num)) {
54✔
86
        throw new Error('Invalid num: ' + num);
5✔
87
    }
88
};
89

90
const extractPayloadValue = async(input) => {
1✔
91
    if (input.payloadS3) {
68✔
92
        return await utils.fetchPayloadFromS3(input.payloadS3); // might throw if access denied or 404
3✔
93
    } else if (input.payload) {
59✔
94
        return input.payload;
20✔
95
    }
96
    return null;
39✔
97
};
98

99

100
const extractDiscardTopBottomValue = (event) => {
1✔
101
    // extract discardTopBottom used to trim values from average duration
102
    let discardTopBottom = event.discardTopBottom;
62✔
103
    if (typeof discardTopBottom === 'undefined') {
62✔
104
        discardTopBottom = 0.2;
58✔
105
    }
106
    // discardTopBottom must be between 0 and 0.4
107
    return Math.min(Math.max(discardTopBottom, 0.0), 0.4);
62✔
108
};
109

110
const extractSleepTime = (event) => {
1✔
111
    let sleepBetweenRunsMs = event.sleepBetweenRunsMs;
62✔
112
    if (isNaN(sleepBetweenRunsMs)) {
62✔
113
        sleepBetweenRunsMs = 0;
60✔
114
    } else {
115
        sleepBetweenRunsMs = parseInt(sleepBetweenRunsMs, 10);
2✔
116
    }
117
    return sleepBetweenRunsMs;
62✔
118
};
119

120
const extractDataFromInput = async(event) => {
1✔
121
    const input = event.input; // original state machine input
69✔
122
    const payload = await extractPayloadValue(input);
68✔
123
    const discardTopBottom = extractDiscardTopBottomValue(input);
62✔
124
    const sleepBetweenRunsMs = extractSleepTime(input);
62✔
125
    return {
62✔
126
        value: parseInt(event.value, 10),
127
        lambdaARN: input.lambdaARN,
128
        num: parseInt(input.num, 10),
129
        enableParallel: !!input.parallelInvocation,
130
        payload: payload,
131
        dryRun: input.dryRun === true,
132
        preProcessorARN: input.preProcessorARN,
133
        postProcessorARN: input.postProcessorARN,
134
        discardTopBottom: discardTopBottom,
135
        sleepBetweenRunsMs: sleepBetweenRunsMs,
136
    };
137
};
138

139
const runInParallel = async({num, lambdaARN, lambdaAlias, payloads, preARN, postARN}) => {
1✔
140
    const results = [];
9✔
141
    // run all invocations in parallel ...
142
    const invocations = utils.range(num).map(async(_, i) => {
9✔
143
        const {invocationResults, actualPayload} = await utils.invokeLambdaWithProcessors(lambdaARN, lambdaAlias, payloads[i], preARN, postARN);
181✔
144
        // invocation errors return 200 and contain FunctionError and Payload
145
        if (invocationResults.FunctionError) {
181✔
146
            throw new Error(`Invocation error (running in parallel): ${invocationResults.Payload} with payload ${JSON.stringify(actualPayload)}`);
40✔
147
        }
148
        results.push(invocationResults);
141✔
149
    });
150
    // ... and wait for results
151
    await Promise.all(invocations);
9✔
152
    return results;
5✔
153
};
154

155
const runInSeries = async({num, lambdaARN, lambdaAlias, payloads, preARN, postARN, sleepBetweenRunsMs}) => {
1✔
156
    const results = [];
33✔
157
    for (let i = 0; i < num; i++) {
33✔
158
        // run invocations in series
159
        const {invocationResults, actualPayload} = await utils.invokeLambdaWithProcessors(lambdaARN, lambdaAlias, payloads[i], preARN, postARN);
608✔
160
        // invocation errors return 200 and contain FunctionError and Payload
161
        if (invocationResults.FunctionError) {
606✔
162
            throw new Error(`Invocation error (running in series): ${invocationResults.Payload} with payload ${JSON.stringify(actualPayload)}`);
4✔
163
        }
164
        if (sleepBetweenRunsMs > 0) {
602✔
165
            await utils.sleep(sleepBetweenRunsMs);
10✔
166
        }
167
        results.push(invocationResults);
602✔
168
    }
169
    return results;
27✔
170
};
171

172
const computeStatistics = (baseCost, results, value, discardTopBottom) => {
1✔
173
    // use results (which include logs) to compute average duration ...
174

175
    const durations = utils.parseLogAndExtractDurations(results);
32✔
176

177
    const averageDuration = utils.computeAverageDuration(durations, discardTopBottom);
32✔
178
    console.log('Average duration: ', averageDuration);
32✔
179

180
    // ... and overall statistics
181
    const averagePrice = utils.computePrice(baseCost, minRAM, value, averageDuration);
32✔
182

183
    // .. and total cost (exact $)
184
    const totalCost = utils.computeTotalCost(baseCost, minRAM, value, durations);
32✔
185

186
    const stats = {
32✔
187
        averagePrice,
188
        averageDuration,
189
        totalCost,
190
        value,
191
    };
192

193
    console.log('Stats: ', stats);
32✔
194
    return stats;
32✔
195
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc