-
Notifications
You must be signed in to change notification settings - Fork 0
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
Onlycoldstarts #2
Changes from all commits
efd6ad0
89a255b
711e37d
4720586
f70677d
0445f32
4d8f554
f6d7ad1
32f9533
c0dd5ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
'use strict'; | ||
|
||
const utils = require('./utils'); | ||
|
||
|
||
module.exports.handler = async(event, context) => { | ||
const {iterator, aliases, currConfig, lambdaARN} = validateInputs(event); | ||
const {envVars} = await utils.getLambdaPower(lambdaARN); | ||
// Alias may not exist when we are reverting the Lambda function to its original configuration | ||
if (typeof currConfig.alias !== 'undefined'){ | ||
envVars.LambdaPowerTuningForceColdStart = currConfig.alias; | ||
} else { | ||
delete envVars.LambdaPowerTuningForceColdStart; | ||
} | ||
|
||
// publish version & assign alias (if present) | ||
await utils.createPowerConfiguration(lambdaARN, currConfig.powerValue, currConfig.alias, envVars); | ||
if (typeof currConfig.alias !== 'undefined') { | ||
// keep track of all aliases | ||
aliases.push(currConfig.alias); | ||
} | ||
|
||
// update iterator | ||
iterator.index++; | ||
iterator.continue = (iterator.index < iterator.count); | ||
if (!iterator.continue) { | ||
delete event.powerValues.initConfigurations; | ||
} | ||
event.powerValues.aliases = aliases; | ||
return event.powerValues; | ||
}; | ||
function validateInputs(event) { | ||
if (!event.lambdaARN) { | ||
throw new Error('Missing or empty lambdaARN'); | ||
} | ||
const lambdaARN = event.lambdaARN; | ||
if (!(event.powerValues && event.powerValues.iterator && event.powerValues.initConfigurations)){ | ||
throw new Error('Invalid input'); | ||
} | ||
const iterator = event.powerValues.iterator; | ||
if (!(iterator.index >= 0 && iterator.index < iterator.count)){ | ||
throw new Error('Invalid iterator input'); | ||
} | ||
const initConfigurations = event.powerValues.initConfigurations; | ||
const aliases = event.powerValues.aliases || []; | ||
const currIdx = iterator.index; | ||
const currConfig = initConfigurations[currIdx]; | ||
if (!(currConfig && currConfig.powerValue)){ | ||
throw new Error('Invalid configuration'); | ||
} | ||
return {iterator, aliases, currConfig, lambdaARN}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,14 @@ module.exports.lambdaBaseCost = (region, architecture) => { | |
return this.baseCostForRegion(priceMap, region); | ||
}; | ||
|
||
module.exports.buildAliasString = (baseAlias, onlyColdStarts, index) => { | ||
let alias = baseAlias; | ||
if (onlyColdStarts) { | ||
alias += `-${index}`; | ||
} | ||
return alias; | ||
}; | ||
|
||
module.exports.allPowerValues = () => { | ||
const increment = 64; | ||
const powerValues = []; | ||
|
@@ -69,28 +77,27 @@ module.exports.verifyAliasExistance = async(lambdaARN, alias) => { | |
/** | ||
* Update power, publish new version, and create/update alias. | ||
*/ | ||
module.exports.createPowerConfiguration = async(lambdaARN, value, alias) => { | ||
module.exports.createPowerConfiguration = async(lambdaARN, value, alias, envVars) => { | ||
try { | ||
await utils.setLambdaPower(lambdaARN, value); | ||
await utils.setLambdaPower(lambdaARN, value, envVars); | ||
|
||
// wait for functoin update to complete | ||
// wait for function update to complete | ||
await utils.waitForFunctionUpdate(lambdaARN); | ||
|
||
const {Version} = await utils.publishLambdaVersion(lambdaARN); | ||
if (typeof alias === 'undefined'){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When can this happen? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This happens when we want to create a lambda version with the original power values (before Power Tuner modifications), and we don't want to setup an Alias for it (e.g. it'll be $LATEST) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok cool, I'd add a comment about it :) it will be harder to guess/remember in 6 months |
||
console.log('No alias defined'); | ||
return; | ||
} | ||
const aliasExists = await utils.verifyAliasExistance(lambdaARN, alias); | ||
if (aliasExists) { | ||
await utils.updateLambdaAlias(lambdaARN, alias, Version); | ||
} else { | ||
await utils.createLambdaAlias(lambdaARN, alias, Version); | ||
} | ||
} catch (error) { | ||
if (error.message && error.message.includes('Alias already exists')) { | ||
// shouldn't happen, but nothing we can do in that case | ||
console.log('OK, even if: ', error); | ||
} else { | ||
console.log('error during config creation for value ' + value); | ||
throw error; // a real error :) | ||
} | ||
console.log('error during config creation for value ' + value); | ||
throw error; // a real error :) | ||
} | ||
}; | ||
|
||
|
@@ -126,7 +133,7 @@ module.exports.waitForAliasActive = async(lambdaARN, alias) => { | |
}, | ||
}; | ||
const lambda = utils.lambdaClientFromARN(lambdaARN); | ||
return lambda.waitFor('functionActive', params).promise(); | ||
return lambda.waitFor('functionActiveV2', params).promise(); | ||
}; | ||
|
||
/** | ||
|
@@ -140,7 +147,11 @@ module.exports.getLambdaPower = async(lambdaARN) => { | |
}; | ||
const lambda = utils.lambdaClientFromARN(lambdaARN); | ||
const config = await lambda.getFunctionConfiguration(params).promise(); | ||
return config.MemorySize; | ||
return { | ||
power: config.MemorySize, | ||
// we need to fetch env vars only to add a new one and force a cold start | ||
envVars: (config.Environment || {}).Variables || {}, | ||
}; | ||
}; | ||
|
||
/** | ||
|
@@ -175,11 +186,12 @@ module.exports.getLambdaConfig = async(lambdaARN, alias) => { | |
/** | ||
* Update a given Lambda Function's memory size (always $LATEST version). | ||
*/ | ||
module.exports.setLambdaPower = (lambdaARN, value) => { | ||
module.exports.setLambdaPower = (lambdaARN, value, envVars) => { | ||
console.log('Setting power to ', value); | ||
const params = { | ||
FunctionName: lambdaARN, | ||
MemorySize: parseInt(value, 10), | ||
Environment: {Variables: envVars}, | ||
}; | ||
const lambda = utils.lambdaClientFromARN(lambdaARN); | ||
return lambda.updateFunctionConfiguration(params).promise(); | ||
|
@@ -543,9 +555,15 @@ module.exports.regionFromARN = (arn) => { | |
return arn.split(':')[3]; | ||
}; | ||
|
||
let client; | ||
module.exports.lambdaClientFromARN = (lambdaARN) => { | ||
const region = this.regionFromARN(lambdaARN); | ||
return new AWS.Lambda({region}); | ||
// create a client only once | ||
if (typeof client === 'undefined'){ | ||
// set Max Retries to 20, increase the retry delay to 500 | ||
client = new AWS.Lambda({region: region, maxRetries: 20, retryDelayOptions: {base: 500}}); | ||
} | ||
return client; | ||
}; | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no try-catch needed here? if it's not needed, I'd remove it above too :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The one above was for debug purposes - I will remove both