1

I am trying to invoke async lambda function from another lambda function with NodeJS. At the end, I will have 3 lambda function. 1. Function will ask for user list 2. Function will ask details for each user 3. Create a record for that user in DB

First request is working fine, I can get user list but I don't see any sign that second lambda has been invoked.

'use strict';

const AWS = require("aws-sdk");
const rp = require('request-promise');

const lambda = new AWS.Lambda({
  region: "eu-central-1"
});

module.exports.getUserDetailsFromCrowd = async event => {
  console.log("Get User Details Function")
  var crowdUserDetailsOptions = {
    "method": "GET",
    "uri": event,
    'auth': {
      'user': process.env.user,
      'pass': process.env.pass,
      'sendImmediately': false
    },
    "json": true,
    "headers": {
      "Accept": "application/json"
    }
  }
  return rp(crowdUserDetailsOptions).then(function (result) {
    console.log(JSON.stringify(result))
  }).catch(function (err) {
    console.log(JSON.stringify(err))
  })
};

module.exports.getUserListFromCrowd = async event => {
  console.log("GetUserListFunction")
  var crowdUserGroupOptions = {
    "method": "GET",
    "uri": process.env.uri,
    'auth': {
      'user': process.env.user,
      'pass': process.env.pass,
      'sendImmediately': false
    },
    "json": true,
    "headers": {
      "Accept": "application/json"
    }
  };

  return rp(crowdUserGroupOptions)
    .then(function (body) {
      console.log("Request Sent")
      const userList = body;
      var userLinks = userList.users;
      for (var i = 0; i < userLinks.length; i++) {
        var requestLink = userLinks[i].link.href + "&expand=attributes"
        console.log("Request: " + i)
        console.log(requestLink)
        const params = {
          FunctionName: "applicationName-stageName-getUserDetailsFromCrowd",
          InvocationType: "Event",
          Payload: requestLink,
          LogType: "Tail",
        }
        console.log("Calling Lambda")
        lambda.invoke(params, function (err, data) {
          if (err) console.log(err, err.stack); // an error occurred
          else console.log(data); // successful response
        });
        console.log("Called Lambda")
      }
    })
    .catch(function (err) {
      console.log(JSON.stringify(err));
    });
};

I got the log below:

2019-11-08T08:38:29.892Z    1da97723-f792-408a-b6dd-24c7d1b343d4    INFO    Request: 9
2019-11-08T08:38:29.892Z    1da97723-f792-408a-b6dd-24c7d1b343d4    INFO    https://aaa.com/user/details
2019-11-08T08:38:29.892Z    1da97723-f792-408a-b6dd-24c7d1b343d4    INFO    Calling Lambda
2019-11-08T08:38:29.894Z    1da97723-f792-408a-b6dd-24c7d1b343d4    INFO    Called Lambda

I am at least expecting to see something in between "Calling Lambda" and "Called Lambda" but nothing happens. Also, I don't see any log on the console for second function that shows it has been invoked.

1 Answer 1

2

The Lambdas will be firing, the issue is you don't wait for the invocations to finish. Your Lambda is async, but you don't actually use await, and given the lambda.invoke is an I/O bound call there is nothing in your code that would force the Lambda to wait for each invocation to finish.

Here's a way you can queue up the internal calls and make your Lambda wait for the results:

module.exports.getUserListFromCrowd = async event => {
  console.log('GetUserListFunction')
  const crowdUserGroupOptions = {
    method: 'GET',
    uri: process.env.uri,
    auth: {
      user: process.env.user,
      pass: process.env.pass,
      sendImmediately: false
    },
    json: true,
    headers: {
      Accept: 'application/json'
    }
  };
  const { users } = await rp(crowdUserGroupOptions);
  console.log('Request Sent')
  const requests = users.map(u => 
    new Promise((resolve, reject) => {
      const url = `${u.link.href}&expand=attributes`;
      const params = {
        FunctionName: 'applicationName-stageName-getUserDetailsFromCrowd',
        InvocationType: 'Event',
        Payload: requestLink,
        LogType: 'Tail',
      }
      console.log('Calling Lambda');
      lambda.invoke(params, (err, data) => err ? reject(err) : resolve(data));
    })
  );
  console.log(`Waiting for ${requests.length} requests to finish...`);
  const results = await Promise.all(requests);
  console.log('Lambda results');
  results.forEach((x, i) => console.log(`Request ${i+1} returned ${x}`));
};
Sign up to request clarification or add additional context in comments.

3 Comments

Note that it won't actually be waiting for the results of the other Lambda function(s), since a Lambda function invoked via InvocationType: 'Event' will never return results. This code will simply be waiting on the call to the AWS API to invoke the Lambda function to return a success status.
Per Mark's comment, this is why step functions does well for lambda orchestration, rather than using a lambda to do it. Save yourself some headache and do step functions :)
@MarkB no worries, I can adjust the answer to suit - I'm not all that familiar with the variations of AWS Lamdas (more an Azure Dev), the results part was secondary to the actual problem itself which appeared to be the Lambda wasn't hanging about long enough to make sure it had fired all invocations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.