Skip to content

Commit

Permalink
Take coldstart delays out of Lambda timeout path
Browse files Browse the repository at this point in the history
Print coldstart status on startup
Update tests
  • Loading branch information
ryanblock committed Oct 25, 2023
1 parent 128c84d commit 45efc52
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 40 deletions.
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

---

## [5.9.0] 2023-10-24
## [5.9.0 - 5.9.1] 2023-10-24

### Added

Expand Down
45 changes: 20 additions & 25 deletions src/invoke-lambda/exec/spawn.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = function spawnChild (params, callback) {

// Let's go!
let pid = 'init'
let child, error, closed
let child, error, closed, to, check
function start () {
child = spawn(command, args, options)
pid = child.pid
Expand All @@ -28,33 +28,28 @@ module.exports = function spawnChild (params, callback) {
update.debug.status(`[${requestID}] Emitted 'close' (pid ${pid}, code '${code}', signal '${signal}')`)
shutdown('child process closure')
})

// Set an execution timeout
to = setTimeout(function () {
timedOut = true
let duration = `${timeout / 1000}s`
update.warn(`[${requestID}] Timed out after hitting its ${duration} timeout!`)
shutdown(`${duration} timeout`)
}, timeout)

// Terminate once we find a result from the runtime API
// 25ms is arbitrary, but hopefully it should be solid enough
check = setInterval(function () {
if (invocations[requestID].response ||
invocations[requestID].initError ||
invocations[requestID].error) {
shutdown('runtime API completion check')
}
}, 25)
}
if (coldstart) {
if (coldstart < timeout) setTimeout(start, coldstart)
else {
update.debug.status(`[${requestID}] Coldstart simulator: coldstart of ${coldstart}ms exceeds timeout of ${timeout}ms, not spawning the Lambda`)
}
}
if (coldstart) setTimeout(start, coldstart)
else start()

// Set an execution timeout
let to = setTimeout(function () {
timedOut = true
let duration = `${timeout / 1000}s`
update.warn(`[${requestID}] Timed out after hitting its ${duration} timeout!`)
shutdown(`${duration} timeout`)
}, timeout)

// Terminate once we find a result from the runtime API
// 25ms is arbitrary, but hopefully it should be solid enough
let check = setInterval(function () {
if (invocations[requestID].response ||
invocations[requestID].initError ||
invocations[requestID].error) {
shutdown('runtime API completion check')
}
}, 25)

// Ensure we don't have dangling processes due to open connections, etc. before we wrap up
function shutdown (event) {
// Immediately shut down all timeouts and intervals
Expand Down
8 changes: 7 additions & 1 deletion src/sandbox/maybe-hydrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ let { chars } = require('@architect/utils')
* Checks for the existence of supported dependency manifests, and auto-hydrates dependencies as necessary
* Supported manifests: `package.json`, `requirements.txt`, `Gemfile`
*/
module.exports = function maybeHydrate ({ cwd, inventory, quiet, deleteVendor }, callback) {
module.exports = function maybeHydrate ({ cwd, inventory, quiet, deleteVendor, update }, callback) {
let { inv } = inventory
if (!inv.lambdaSrcDirs || !inv.lambdaSrcDirs.length) {
callback()
}
else {
/**
* Coldstart simulator status
*/
let coldstart = inv._project?.preferences?.sandbox?.coldstart || false
if (coldstart) {
update.done('Started with Lambda coldstart simulator')
}

// Enable vendor dir deletion by default
let del = deleteVendor === undefined ? true : deleteVendor
Expand Down
31 changes: 18 additions & 13 deletions test/integration/http/misc-http-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ function runTests (runType, t) {
t.plan(1)
let file = join(process.cwd(), 'test', 'mock', 'coldstart', 'src', 'http', 'get-chonk', 'chonky.txt')
let MB = 1024 * 1024
let size = MB * 115
let size = MB * 50
if (existsSync(file)) t.equal(statSync(file).size, size, 'Found coldstart enchonkinator')
else {
let start = Date.now()
Expand All @@ -312,29 +312,34 @@ function runTests (runType, t) {
}
})

t.test(`[Misc / ${runType}] No coldstart timeout`, t => {
t.plan(1)
t.test(`[Misc / ${runType}] No coldstart delay`, t => {
t.plan(2)
let start = Date.now()
tiny.get({
url: url + '/smol'
}, function _got (err, result) {
if (err) t.end(err)
else t.deepEqual(result.body, { ok: true }, 'Lambda did not timeout from a coldstart')
else {
t.deepEqual(result.body, { ok: true }, 'Lambda did not timeout from a coldstart')
let time = Date.now() - start
// 250 is probably extremely conservative, but sometimes CI can be super slow
t.ok(time < 250, `Response returned quickly (${time}ms)`)
}
})
})

t.test(`[Misc / ${runType}] Coldstart timeout`, t => {
t.plan(3)
t.test(`[Misc / ${runType}] Coldstart delay`, t => {
t.plan(2)
let start = Date.now()
tiny.get({
url: url + '/chonk'
}, function _got (err, result) {
if (err) {
let message = 'Timeout error'
let time = '1 second'
t.equal(err.statusCode, 500, 'Errors with 500')
t.match(err.body, new RegExp(message), `Errors with message: '${message}'`)
t.match(err.body, new RegExp(time), `Timed out set to ${time}`)
if (err) t.end(err)
else {
t.deepEqual(result.body, { ok: true }, 'Lambda did not timeout from a coldstart')
let time = Date.now() - start
t.ok(time > 450, `Response returned slowly (${time}ms)`)
}
else t.end(result)
})
})

Expand Down

0 comments on commit 45efc52

Please sign in to comment.