Home > other >  Node.js AWS Lambda not waiting for anything
Node.js AWS Lambda not waiting for anything

Time:05-05

I've tried every suggested solution and looked at every similar question, but nothing is resolving my issue. This script works perfectly fine on my local machine. The issue with Lambda is that it completely skips time out's and awaits. Whereas my local machine actually waits.

My index file looks like this:

import { getData } from './src/EC2Scripts/taskScripts/otherLogic.js';

async function handler(event) {
        await getData();
        
        const response = {
            statusCode: 200,
            body: JSON.stringify("Not waiting."),
        };

        return response;
}

export { handler };

Here's the otherLogic class:

import fetch from "node-fetch";
import { PoolConnection } from '../common/classes/PoolConnection.js';

async function getData() {
    return new Promise(async (resolve, reject) => {
       let pool = PoolConnection.getInstance();
       let currentResponse = await fetch(
            "dummyURL"
       );
        
       let currentData = await currentResponse.json();
        
       pool.uploadCoins(currentData).then((result) => { 
            pool.endConnection().then((end) => { resolve() }); 
       });
    });
}

export { getData };

Here's the PoolConnection class:

import pg from "pg";
import fetch from "node-fetch";

import { getCurrentDateTime } from "../dateTime.js";

class PoolConnection {
    // DO NOT CALL THIS CONSTRUCTOR, CALL THE GETINSTANCE() METHOD
    constructor() {
        if (!PoolConnection.instance) {
            PoolConnection.instance = this;

            this.config = {
                user: "user",
                password: "password",
                database: "postgres",
                host: "host",
                port: 5432,
                max: 100,
                connectionTimeoutMillis: 30000,
                idleTimeoutMillis: 30000
            };
            this.pool = this.establishConnection(1).catch((error) => { });
        }
    }

    static getInstance() {
        if (PoolConnection.instance) {
            return PoolConnection.instance;
        }

        PoolConnection.instance = new PoolConnection();
        return PoolConnection.instance;
    }

    async connect() {
        return new Promise((resolve, reject) => {
            const poolPromise = new pg.Pool(this.config);
            poolPromise.connect()
                .then(pool => {
                    console.log(getCurrentDateTime()   " ----- Connection to article database successful.");
                    resolve(pool);
                })
                .catch(err => {
                    console.error(getCurrentDateTime()   " ----- ERROR CONNECTING TO ARTICLE DATABASE :: "   err);
                    reject(err);
                });
        });
    }

    async establishConnection(attempts) {
        return new Promise((resolve, reject) => {
            if (attempts > 5) {
                console.log(getCurrentDateTime()   " ---- Connection unsuccessful to database, maximum number of attempts reached.");
                reject("ERROR");
            }

            this.connect().then(pool => {
                resolve(pool);
            }).catch(err => {
                console.error(getCurrentDateTime()   " RETRYING CONNECTION TO DATABASE ATTEMPT #"   attempts);
                attempts  ;
                // try again in 3 seconds
                setTimeout(() => { this.establishConnection(attempts) }, 3000);
            });
        })
    }

    //  Connection has to be terminated gracefully or else script will hang.
    async endConnection() {
        return new Promise((resolve, reject) => {
           this.pool.then(connection => connection.end(() => console.log(getCurrentDateTime()   " ---- Connection to database successfully terminated."))); 
        });
    }

    async uploadData(data) {
        return new Promise((resolve, reject) => {
           this.pool.then(async (poolInstance) => {
                for(const entry of data) {
                    
                    let getMoreData = await fetch (
                        "dummyUrl2"   entry
                    );
    
                    const result = poolInstance.query("INSERT INTO table(data) VALUES ($1)", 
                                                    [entry['getMoreData']]);
                }
                resolve();
            }); 
        });
    }
}

export { PoolConnection }

It tries to close the connection before I can do anything. Here's the error:

> START RequestId: 5aa4984b-d57f-4d69-84a3-005a6c590c0f Version: $LATEST
> 2022-05-03T14:48:11.625Z  5aa4984b-d57f-4d69-84a3-005a6c590c0f    INFO    2022-5-3
> **** 14:48:11 ----- Connection to article database successful. 2022-05-03T14:48:12.946Z   5aa4984b-d57f-4d69-84a3-005a6c590c0f    INFO    2022-5-3
> **** 14:48:12 ---- Connection to database successfully terminated. 2022-05-03T14:48:14.146Z   5aa4984b-d57f-4d69-84a3-005a6c590c0f    ERROR   Unhandled Promise Rejection
>   {"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"Error:
> Client was closed and is not
> queryable","reason":{"errorType":"Error","errorMessage":"Client was
> closed and is not queryable","stack":["Error: Client was closed and is
> not queryable","    at
> /var/task/node_modules/pg/lib/client.js:570:27","    at
> processTicksAndRejections
> (internal/process/task_queues.js:77:11)"]},"promise":{},"stack":["Runtime.UnhandledPromiseRejection:
> Error: Client was closed and is not queryable","    at
> process.<anonymous> (/var/runtime/index.js:35:15)","    at
> process.emit (events.js:400:28)","    at processPromiseRejections
> (internal/process/promises.js:245:33)","    at
> processTicksAndRejections (internal/process/task_queues.js:96:32)"]}
> END RequestId: 5aa4984b-d57f-4d69-84a3-005a6c590c0f REPORT RequestId:
> 5aa4984b-d57f-4d69-84a3-005a6c590c0f  Duration: 2985.31 ms    Billed
> Duration: 2986 ms Memory Size: 128 MB Max Memory Used: 75 MB  Init
> Duration: 267.75 ms

I've tried converting everything to return a new Promise and then using .then(), but that actually made the program run even faster somehow. I've tried using Promise.all().then(). I've tried moving the setTimeout() into the handler in the index.js, but that didn't work either. I've tried using then() after the pool.uploadData(). I've also tried removing async from the method handler.

I'm out of ideas at this point. I've refactored and tried to implement this code in every way I've seen, and nothing is working.

There are no errors or uncaught exceptions, it's just not waiting for anything.

Lambda is using NodeJS version 14.x

EDIT: added pool connection class and updated classes to show more clearly what I have tried.

CodePudding user response:

While getData() is marked as async, it doesn't return any explicit promises so it returns a Promise<void> which immediately resolves (to undefined).

You need to return an explicit Promise which resolves/rejects after running your business logic to make Lambda actually wait for processing to complete.

As per Node.js Lambda docs on async handlers:

If your code performs an asynchronous task, return a promise to make sure that it finishes running. When you resolve or reject the promise, Lambda sends the response or error to the invoker.

For example, this returns a Promise which resolves after 5 seconds, making Lambda wait before it returns a response:

async function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(), 5000)
    });
}

async function handler(event) {
    await getData();

    const response = {
        statusCode: 200,
        body: JSON.stringify("Waiting 5 seconds..."),
    };

    return response;
}

export { handler };
START RequestId: 0ee7693e-8d6a-4c3f-b23e-119377e633e3 Version: $LATEST
END RequestId: 0ee7693e-8d6a-4c3f-b23e-119377e633e3
REPORT RequestId: 0ee7693e-8d6a-4c3f-b23e-119377e633e3  Duration: 5009.05 ms    Billed Duration: 5010 ms    Memory Size: 128 MB Max Memory Used: 56 MB  Init Duration: 224.62 ms

CodePudding user response:

I'm adding this because I was able to figure out what was going wrong. With some help from previous answers/comments, I've reformatted my code so if anyone else runs into a similar issue, hopefully, this helps.

index.js:

import { getData } from './src/EC2Scripts/taskScripts/otherLogic.js';

async function handler(event) {
        await getData();
        
        const response = {
            statusCode: 200,
            body: JSON.stringify("Now waiting."),
        };

        return response;
}

export { handler };

other logic class:

import fetch from "node-fetch";
import { PoolConnection } from '../common/classes/PoolConnection.js';

async function getData() {
    return new Promise(async (resolve, reject) => {
       let pool = await PoolConnection.getInstance();
       let currentResponse = await fetch(
            "dummyURL"
       );
        
       let currentData = await currentResponse.json();
       await pool.uploadData(currentData);
       await pool.endConnection();
       resolve();
    });
}

export { getData };

PoolConnection class:

import pg from "pg";
import fetch from "node-fetch";

import { getCurrentDateTime } from "../dateTime.js";

class PoolConnection {
    // DO NOT CALL THIS CONSTRUCTOR, CALL THE GETINSTANCE() METHOD
    constructor() {
        if (!PoolConnection.instance) {
            PoolConnection.instance = this;

            this.config = {
                user: "user",
                password: "password",
                database: "postgres",
                host: "host",
                port: 5432,
                max: 100,
                connectionTimeoutMillis: 30000,
                idleTimeoutMillis: 30000
            };

            this.pool = new pg.Pool(this.config);
        }
    }

    static async getInstance() {
        return new Promise(async (resolve, reject) => {
            if (PoolConnection.instance) {
                return PoolConnection.instance;
            }

            PoolConnection.instance = new PoolConnection();
            resolve(PoolConnection.instance);
        });
    }

    //  Connection has to be terminated gracefully or else script will hang.
    async endConnection() {
        return new Promise(async (resolve, reject) => {
            await this.pool.end(() => console.log(getCurrentDateTime()   " ---- Connection to database successfully terminated."));
            resolve();
        });
    }

    async uploadData(data) {
        return new Promise(async (resolve) => {
            let promiseArray = [];

            for (const entry of data) {
                promiseArray.push(new Promise(async (resolve) => {
                    await this.pool.connect(async (error, client, release) => {
                        if (error) {
                            console.error(getCurrentDateTime()   " ----- Error getting client. Error :: ", error.stack);
                        }
                        await client.query("query", (error, result, release) => {
                                release();
                                if (error) {
                                    console.error(getCurrentDateTime()   " ----- Error executing query :: ", error.stack);
                                }
                                resolve();
                         });
                    });
                }));
            }
            await Promise.all(promiseArray);
            resolve();
        });
    }
}

export { PoolConnection }

If this doesn't solve your issue, the two biggest helps for finding a fix were this post and this post.

  • Related