Skip to content

Commit

Permalink
feat: add timeout feature, hookup noTimeout option (#1551)
Browse files Browse the repository at this point in the history
  • Loading branch information
dnalborczyk authored Aug 27, 2022
1 parent e6f5d34 commit 0896acc
Show file tree
Hide file tree
Showing 17 changed files with 60 additions and 7 deletions.
43 changes: 38 additions & 5 deletions src/lambda/LambdaFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { readFile, writeFile } from 'node:fs/promises'
import { dirname, join, resolve } from 'node:path'
import process from 'node:process'
import { performance } from 'node:perf_hooks'
import { promisify } from 'node:util'
import { log } from '@serverless/utils/log.js'
import { emptyDir, ensureDir, remove } from 'fs-extra'
import jszip from 'jszip'
Expand All @@ -18,6 +19,8 @@ import { createUniqueId } from '../utils/index.js'
const { ceil } = Math
const { entries, fromEntries } = Object

const setTimeoutPromise = promisify(setTimeout)

export default class LambdaFunction {
#artifact = null

Expand All @@ -37,6 +40,8 @@ export default class LambdaFunction {

#handler = null

#handlerRunDone = false

#handlerRunner = null

#idleTimeStarted = null
Expand All @@ -49,13 +54,15 @@ export default class LambdaFunction {

#memorySize = null

#noTimeout = null

#region = null

#runtime = null

#timeout = null
#status = 'IDLE' // can be 'BUSY' or 'IDLE'

status = 'IDLE' // can be 'BUSY' or 'IDLE'
#timeout = null

constructor(functionKey, functionDefinition, serverless, options) {
const {
Expand All @@ -81,6 +88,8 @@ export default class LambdaFunction {
provider.memorySize ||
DEFAULT_LAMBDA_MEMORY_SIZE

this.#noTimeout = options.noTimeout

this.#region = provider.region

this.#runtime =
Expand Down Expand Up @@ -265,8 +274,25 @@ export default class LambdaFunction {
return this.#functionName
}

get status() {
return this.#status
}

async #timeoutAndTerminate() {
await setTimeoutPromise(this.#timeout)

// if the handler has finished before the timeout don't terminate
if (this.#handlerRunDone) {
return
}

await this.#handlerRunner.cleanup()

throw new Error('Lambda timeout.')
}

async runHandler() {
this.status = 'BUSY'
this.#status = 'BUSY'

if (!this.#initialized) {
await this.#initialize()
Expand All @@ -281,7 +307,14 @@ export default class LambdaFunction {

this.#startExecutionTimer()

const result = await this.#handlerRunner.run(this.#event, context)
this.#handlerRunDone = false

const result = await Promise.race([
this.#handlerRunner.run(this.#event, context),
...(this.#noTimeout ? [] : [this.#timeoutAndTerminate()]),
])

this.#handlerRunDone = true

this.#stopExecutionTimer()

Expand All @@ -296,7 +329,7 @@ export default class LambdaFunction {
)
}

this.status = 'IDLE'
this.#status = 'IDLE'
this.#startIdleTimer()

return result
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/access-host/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/artifact/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/go/go1.x/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/layers/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/multiple/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/nodejs/nodejs12.x/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/provided/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/python/python3.6/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/python/python3.7/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/python/python3.8/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker/ruby/ruby2.7/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('run mode with worker threads', function desc() {
const url = new URL('/dev/foo', BASE_URL)

// eslint-disable-next-line no-unused-vars
for (const i of Array(10).keys()) {
for (const i of Array(5).keys()) {
// eslint-disable-next-line no-await-in-loop
const response = await fetch(url)
// eslint-disable-next-line no-await-in-loop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('run mode with worker threads', function desc() {
const url = new URL('/dev/foo', BASE_URL)

// eslint-disable-next-line no-unused-vars
for (const i of Array(10).keys()) {
for (const i of Array(5).keys()) {
// eslint-disable-next-line no-await-in-loop
const response = await fetch(url)
// eslint-disable-next-line no-await-in-loop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ package:
plugins:
- ../../../../

custom:
serverless-offline:
noTimeout: true

provider:
name: aws
runtime: go1.x
Expand Down
4 changes: 4 additions & 0 deletions tests/runtimes/go/go1.x/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ provider:
stage: dev
versionFunctions: false

custom:
serverless-offline:
noTimeout: true

functions:
hello:
events:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ provider:

custom:
serverless-offline:
noTimeout: true
useDocker: true

functions:
Expand Down

0 comments on commit 0896acc

Please sign in to comment.