From 191391e52f28b8d7a2ca1d227e3232274b6d5001 Mon Sep 17 00:00:00 2001 From: Emanuel Tesar Date: Thu, 1 Dec 2022 17:11:29 +0100 Subject: [PATCH 1/3] Fix failed lambda execution crashing coordinator --- .../src/workers/cloud-platforms/aws.ts | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/airnode-node/src/workers/cloud-platforms/aws.ts b/packages/airnode-node/src/workers/cloud-platforms/aws.ts index 2fab9c6590..6fa4201360 100644 --- a/packages/airnode-node/src/workers/cloud-platforms/aws.ts +++ b/packages/airnode-node/src/workers/cloud-platforms/aws.ts @@ -1,9 +1,11 @@ +import { logger } from '@api3/airnode-utilities'; +import { goSync } from '@api3/promise-utils'; import AWS from 'aws-sdk'; import { WorkerParameters, WorkerResponse } from '../../types'; export function spawn(params: WorkerParameters): Promise { // lambda.invoke is synchronous so we need to wrap this in a promise - return new Promise((resolve, reject) => { + return new Promise((spawnResolve, spawnReject) => { // Uses the current region by default const lambda = new AWS.Lambda(); @@ -15,12 +17,32 @@ export function spawn(params: WorkerParameters): Promise { Payload: JSON.stringify(params.payload), }; + const resolve: typeof spawnResolve = (value) => { + logger.debug(`Successful Lambda response: ${value}`); + spawnResolve(value); + }; + + const reject: typeof spawnReject = (error) => { + logger.debug(`Lambda invocation or execution failed. Response: ${error}`); + spawnReject(error); + }; + + // The Lambda invocation callback populates the error (first argument) only when the invocation fails (e.g. status + // code is 400) or the request is not parsed properly by the SDK and can't be invoked. For errors and timeouts that + // happen inside the invoked lambda have "data.FunctionError" and "data.Payload.errorMessage" populated instead. + // See: https://stackoverflow.com/q/42672023 and https://stackoverflow.com/q/48644093 lambda.invoke(options, (err, data) => { - if (err) { - reject(err); - return; - } - resolve(JSON.parse(JSON.parse(data.Payload as string).body) as WorkerResponse); + if (err) return reject(err); + + if (data.FunctionError) return reject(data.Payload); + + const goPayload = goSync(() => JSON.parse(data.Payload?.toString() ?? '')); + if (!goPayload.success) return reject(goPayload.error); + + const goBody = goSync(() => JSON.parse(goPayload.data.body)); + if (!goBody.success) return reject(goBody.error); + + resolve(goBody.data as WorkerResponse); }); }); } From a4683183290890d2217cf787dda3998f33b9ca3a Mon Sep 17 00:00:00 2001 From: Emanuel Tesar Date: Thu, 1 Dec 2022 19:05:31 +0100 Subject: [PATCH 2/3] Add changeset --- .changeset/dry-weeks-cough.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/dry-weeks-cough.md diff --git a/.changeset/dry-weeks-cough.md b/.changeset/dry-weeks-cough.md new file mode 100644 index 0000000000..4245aa5925 --- /dev/null +++ b/.changeset/dry-weeks-cough.md @@ -0,0 +1,5 @@ +--- +'@api3/airnode-node': patch +--- + +Fix failed lambda execution crashing coordinator From ef151cd569b317a2debbc207367431fc4361c8d1 Mon Sep 17 00:00:00 2001 From: Emanuel Tesar Date: Mon, 5 Dec 2022 09:44:04 +0100 Subject: [PATCH 3/3] Check for data payload explicitly --- packages/airnode-node/src/workers/cloud-platforms/aws.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/airnode-node/src/workers/cloud-platforms/aws.ts b/packages/airnode-node/src/workers/cloud-platforms/aws.ts index 6fa4201360..1b81e5eb19 100644 --- a/packages/airnode-node/src/workers/cloud-platforms/aws.ts +++ b/packages/airnode-node/src/workers/cloud-platforms/aws.ts @@ -36,7 +36,8 @@ export function spawn(params: WorkerParameters): Promise { if (data.FunctionError) return reject(data.Payload); - const goPayload = goSync(() => JSON.parse(data.Payload?.toString() ?? '')); + if (!data.Payload) return reject(new Error('Missing payload in lambda response')); + const goPayload = goSync(() => JSON.parse(data.Payload!.toString())); if (!goPayload.success) return reject(goPayload.error); const goBody = goSync(() => JSON.parse(goPayload.data.body));