2

I have no idea if this is to do with the fact that the parent Lambda is node.js runtime and the child is Python 3.8, but I have come across some strange behaviour, and I can't work out what is wrong...

tldr: Child lambda is only invoked if parent lambda 'sleeps' (calls setTimeout()) for 100 milliseconds or more.

I have two simple AWS Lambda functions. One is written in JS (node.js runtime) and the other is written in Python (3.8). The setup is simple. The parent node.js function calls the child python function. The call is asynchronous, and the parent doesn't have to wait for the child to return. The parent must be node.js, and the child must be python 3.8.

My observation is that the child python lambda is not invoked unless the parent lambda 'sleeps' for a minimum of 100 ms. I know this sounds bizarre, but I'm not sure what I am doing wrong and have spent a couple days trying to work this out (no doubt it is really simple...).

I have coded a 'sleep' function (shown at the top of the node.js code) which is called before the response is returned to the caller from the parent function. I am aware that the code in the .invoke callback will be called after the parent function has returned, that's just there for debugging purposes.

Here is the node.js parent Lambda:

var aws = require('aws-sdk')
var lambda = new aws.Lambda()

function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}  


exports.handler = async (event) => {
    const payload = {
        message: "Hey there",
        
    }
    var params = {
    FunctionName: 'ChildLambda', // the lambda function we are going to invoke
    InvocationType: 'Event', // RequestResponse is sync, Event Async
    LogType: 'Tail',
    Payload: JSON.stringify(payload)
  };
    lambda.invoke(params, function(err, data) {
        console.log("Invoked Lambda...")
        if (err) {
            console.log(err)
        } else if (data) {
            console.log('Data: ', data)
        }
        console.log("Lambda done")
        
    })
    // await sleep(100)
    const response = { 
        "isBase64Encoded": false,
        "statusCode": 202,
        "headers": {},
        "body": "HEY FROM LAMBDA"
    }
    return response;
};

And here is the Python child Lambda:

import requests
import json

def main(event, context):
    print("IN THE SECOND LAMBDA")
    response = {
        "statusCode": 200,
        "headers": {"my_header": "my_value"},
        "body": "hello from second lambda",
        "isBase64Encoded": False,
    }
    return response

If you remove the sleep(100) call from the parent function, then the child function is not invoked. I know this because nothing is shown in CloudWatch logs. If you set the parameter passed to sleep() to less than 100 ms, the child lambda is not called. Calling the child function asynchronously or synchronously (by replacing "Event" with "RequestResponse" in params in the parent function does not change anything, the above still holds true.

What is the reason for this strange behaviour? What am I doing wrong?

EDIT @jellycsc linked to an alternative method of calling the invoke command:

var invoke  = lambda.invoke(params).promise()
    invoke.then(function(data) {
        console.log('Success');
    }).catch(function(err) {
        console.log(err);
    });

Unfortunately, this does not solve the problem, and the invoke command will only be called if the timer is set for 100 ms or more.

3
  • 1
    docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/… Commented Jul 8, 2021 at 18:14
  • Hey @jellycsc thanks for your comment. The document that you have linked to only provides an alternative approach to executing the invoke command, it doesn't solve the problem. I have updated my question to show you how I have modified the call. If I am missing something, please let me know! Commented Jul 8, 2021 at 20:39
  • 1
    You need to return a promise to avoid the lambda terminates before all tasks finish. docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html Commented Jul 8, 2021 at 21:11

1 Answer 1

2

Just wait until invoke action finished then finished the lambda function.

exports.handler = async (event) => {
  const payload = {
    message: "Hey there",

  }
  var params = {
    FunctionName: 'ChildLambda', // the lambda function we are going to invoke
    InvocationType: 'Event', // RequestResponse -  to get response from ChildLambda, Event - don't care the response (or error...)
    LogType: 'Tail',
    Payload: JSON.stringify(payload)
  };

  try {
    console.log("Invoked Lambda...")
    const data = await lambda.invoke(params).promise() // wait until invoke successfully
    console.log('Data: ', data) // data maybe empty - {} when InvocationType is Event
    console.log("Lambda done")
  
    return {
      "isBase64Encoded": false,
      "statusCode": 202,
      "headers": {},
      "body": "HEY FROM LAMBDA"
    }
  } catch (error) {
    // handle error
    console.log("Error", error);
    return {
      "isBase64Encoded": false,
      "statusCode": 500, // return error response
      "headers": {},
      "body": error.message
    }
  }
};
Sign up to request clarification or add additional context in comments.

Comments

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.