Search code examples
amazon-web-serviceslambdaamazon-snsamazon-ses

AWS - Sending 1000's of emails from Lambda / Node.js


I have a "main" Lambda function that gets triggered by SNS. It pulls a list of recipients from the database and it needs to send each of them a message based on a template, replacing things like first name and such.

The way I have it setup is I created another Lambda function called "email-send" which is subscribed to "email-send" topic. The "main" Lambda then loops through the recipients list and publishes messages to "email-send" with a proper payload (from, to, subject, message). This might eventually need to process 1000's of emails in a single batch.

Is this a good approach to my requirements? Perhaps Lambda/SNS is not a way to go? If so, what would you recommend.

With this setup I am running into issues when my "main" function finishes running and somehow "sns.publish" does not get triggered in my loop. I assume because I am not letting it finish. But I am not sure how to fix it, being a loop.

Here is the snippet from my Lambda function:

exports.handler = (event, context, callback) => {
        // code is here to pull data into "data" array

        // process records
        for (var i = 0; i < data.length; i++) {
            var sns = new aws.SNS();
            sns.publish({
              Message: JSON.stringify({ from: data[i].from, to: data[i].to, subject: subject, body: body }),
              TopicArn: 'arn:aws:sns:us-west-2:XXXXXXXX:email-send'
            }, function(err, data) {
              if (err) {
                console.log(err.stack);
              } else {
                console.log('SNS pushed!');
              }
            });  
        }
        context.succeed("success");
};

Thanks for any assistance.


Solution

  • Your code is doing this...

    1. Begin calling sns.publish() 1000 times
    2. Return (through context.succeed())

    You didn't wait for those 1000 calls to finish!

    What your code should do is...

    1. Begin calling sns.publish() 1000 times
    2. When all calls to sns.publish() has returned, then return. (context.succeed is old so we should use callback() instead).

    Something like this...

    // Instantiate the client only once instead of data.length times
    const sns = new aws.SNS();
    
    exports.handler = (event, context, callback) => {
      const snsCalls = []
      for (var i = 0; i < data.length; i++) {
        snsCalls.push(sns.publish({
          Message: JSON.stringify({
            from: data[i].from,
            to: data[i].to,
            subject: subject,
            body: body
          }),
          TopicArn: 'arn:aws:sns:us-west-2:XXXXXXXX:email-send'
        }).promise();
      }
    
      return Promise.all(snsCalls)
        .then(() => callback(null, 'Success'))
        .catch(err => callback(err));
    };