1

I know this has been asked in various ways before, but I can't figure it out. I'm still very new to node.js and lambda. This code will work if I run the lambda twice, but never runs to completion the first time. This also works fine if I run this from a local IDE by adding exports.handler(); to the end of the code block.

The code queries DynamoDB for results and then attempts to delete those records from Dynamo. The query part seems to work every time, but the deletion part fails to happen on the first invocation. I can't seem to figure out what changes are necessary to for lambda to wait until all of my processes are complete.

Thanks in advance.

// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({ region: 'us-east-2' });
exports.handler = async (event) => {
    // Create DynamoDB service object
    const ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
    const documentClient = new AWS.DynamoDB.DocumentClient({ region: "us-east-2" });
    const tablename = process.env.table_name;

    let dynapromises = [];

    let params = {
        ExpressionAttributeValues: {
            ':offNum': { S: process.env.cost_center },
            ':s': { N: '2' }
        },
        ExpressionAttributeNames: {
            "#notif_status": "status"
        },
        KeyConditionExpression: 'officeNumber = :offNum',
        TableName: tablename,
        IndexName: 'officeNumberIndex',
        ProjectionExpression: "notificationNumber",
        FilterExpression: '(attribute_not_exists(#notif_status) or #notif_status = :s) and attribute_not_exists(statusTimes)'
    };

    let qresults = await ddb.query(params).promise();
    console.log("Count of notifs again " + qresults.Items.length);

    qresults.Items.forEach(function(element, index, array) {
        console.log(element.notificationNumber.S);
        let delparams = {
            TableName: tablename,
            ReturnValues: "ALL_OLD",
            Key: {
                notificationNumber: {
                    S: element.notificationNumber.S
                }
            }
        };

        dynapromises.push(ddb.deleteItem(delparams).promise().then(function(data) {
            console.log("Deleted Record:"+ JSON.stringify(data)); // successful response
        }, function(error) {
            console.log(error, error.stack); // an error occurred
        }));
        console.log("deletion parameters " + JSON.stringify(delparams));
    });
    Promise.all(dynapromises).then(res => {
        console.log("All promises done");
    });
    return qresults.Items.length;
};
RobCent
  • 11
  • 1
  • Side note: I would clean up your code to consistently use either `async/await` or `.then()` everywhere + `map()` instead of `push()`: https://stackoverflow.com/a/31414472/65232 – montrealist Jan 21 '20 at 16:05

2 Answers2

1

The issue is in that you are returning before all the promises are completed, you need to move the return qresults.Items.length; inside the last then.

try with this code:

** UPDATE: Change the snippet with the working code **

// Load the AWS SDK for Node.js
const AWS = require('aws-sdk');
// Set the region
AWS.config.update({ region: 'us-east-2' });
exports.handler = async (event) => {
    // Create DynamoDB service object
    const ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
    const documentClient = new AWS.DynamoDB.DocumentClient({ region: "us-east-2" });
    const tablename = process.env.table_name;

    let params = {
        ExpressionAttributeValues: {
            ':offNum': { S: process.env.cost_center },
            ':s': { N: '2' }
        },
        ExpressionAttributeNames: {
            "#notif_status": "status"
        },
        KeyConditionExpression: 'officeNumber = :offNum',
        TableName: tablename,
        IndexName: 'officeNumberIndex',
        ProjectionExpression: "notificationNumber",
        FilterExpression: '(attribute_not_exists(#notif_status) or #notif_status = :s) and attribute_not_exists(statusTimes)'
    };

    let qresults = await ddb.query(params).promise();
    console.log("Count of notifs again " + qresults.Items.length);

    const dynapromises = qresults.Items.map( async element => {
     let delparams = {
            TableName: tablename,
            ReturnValues: "ALL_OLD",
            Key: {
                notificationNumber: {
                    S: element.notificationNumber.S
                }
            }
        };

        try {
          console.log("deletion parameters " + JSON.stringify(delparams));
         const data = await ddb.deleteItem(delparams).promise();
         console.log( "Deleted Record:"+ JSON.stringify(data) );
        } catch ( err ) {
         console.log(error, error.stack); // an error occurred
        }
    } )

    await Promise.all(dynapromises)

    console.log("All promises done");
    return qresults.Items.length;
};
pepo
  • 1,374
  • 11
  • 14
  • Thank you for the suggestion. I just tried with your modification and I'm still having the same results. It doesn't perform the deletions on the first invocation. – RobCent Jan 21 '20 at 16:12
  • Hi RobCent, can you paste here the logs from the first invocation? – pepo Jan 21 '20 at 17:08
  • The logs are somewhat useless in this case. I don't want to copy/paste since there can potentially be real customer data in the logs, but I'll give you an idea of what the logs show. INFO Count of notifs again 43 INFO 400265091 INFO deletion parameters { "TableName": "NotificationTableQA", "ReturnValues": "ALL_OLD", "Key": { "notificationNumber": { "S": "400265091" } } } These are repeated for each dynamo result, then the logs just end with "END RequestId: XXXXXX"then the lambda report on duration etc No errors are in the logs. – RobCent Jan 21 '20 at 20:44
  • thanks, but you see the lines `Deleted Record:...` inside the `then`? – pepo Jan 21 '20 at 20:53
  • The console.log statements like "Deleted Record:" or the error never show in the logs for the first invocation. Only when I rerun the lambda the second time do i get that. – RobCent Jan 21 '20 at 21:15
  • the second invocation starts with 2020-01-21T21:15:29.018Z b778b0da-2487-43c1-9bfc-423d5aee4bf4 INFO Deleted Record: { "Attributes": { ...etc } All promises done Count of notifs again 0 All promises done Its like it just picked up where it left off. I'd just wish this would all happen in one lambda invocation. – RobCent Jan 21 '20 at 21:22
  • As montrealist note, the is a mix of async/await and then in the code, I refactor a little to use only async/await, can you test this version. https://gist.github.com/pepoviola/820830c0847e52920987c9718b26b2b1 – pepo Jan 22 '20 at 15:58
  • Your code is performing the Dynamo deletions on the first lambda invocation. I really appreciate your help on this! – RobCent Jan 23 '20 at 15:49
  • Awesome!, I'm glad to help you – pepo Jan 24 '20 at 10:55
0

The code that @pepo posted is performing the Dynamo deletions on the first invocation of the Lambda. Thanks for his work and the responses from everyone.

RobCent
  • 11
  • 1