invoking AWS lambda function, wait until completion

amazon-web-services, aws-lambda

Solution

Compiling your question, (correct me if I'm wrong and I'll edit the answer):

- You have a lambda X, that calls lambda Y.

- Lambda Y returns some value Z for each execution.

- You want lambda X to receive the value Z, when calling lambda Y.

- You want the callback to be called with the return value Z, after lambda Y ends execution.

According to your `params`:

- You request a `synchronous` execution, by specifying `RequestResponse`, which is good for this purpose (receiving the result of lambda Y).

- You request the last `4KB` of logs produced by lambda Y - by specifying `Tail`.

According to the documentation and given I was right in compiling your question, the `LogResult` is not what you're looking for, but the `Payload` is.

The following is from the Response Elements section:

LogResult

It is the base64-encoded logs for the Lambda function invocation. This is present only if the invocation type is `RequestResponse` and the logs were requested.

The response returns the following as the HTTP body.

Payload

It is the JSON representation of the object returned by the Lambda function. This is present only if the invocation type is `RequestResponse`.

API_Invoke Reference

Edit (added example):

I've created two lambdas, according to my above description, X and Y.

Here's X's code:

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

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;

    const params = {
        FunctionName: "Y", 
        InvocationType: "RequestResponse", 
        LogType: "Tail", 
        Payload: '{"name":"jonathan"}'
    };

    lambda.invoke(params, (err, res) => {
        if (err) {
            callback(err);
        }

        console.log(JSON.stringify(res));

        callback(null, res.Payload);
    });
};

Here's Y's code:

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    console.log(JSON.stringify(event));

    setTimeout(() => {
       callback(null, "My name is Jonathan"); 
    }, 1000 * 10); // 10 seconds delay
};

- What you're trying to achieve is do-able.

- Lambda X invokes synchronously Lambda Y and waits for its response (waits 10 seconds).

- You were accessing the wrong property of the callback input, you accessed the `LogResult` property instead of the `Payload` property.

Here's a log of X's arbitrary execution:

START RequestId: a6a98e8d-31b9-11e7-aba1-d5d86092115f Version: $LATEST
2017-05-05T17:38:40.805Z    a6a98e8d-31b9-11e7-aba1-d5d86092115f    {"StatusCode":200,"LogResult":"U1RBUlQgUmVxdWVzdElkOiBhNzVmMjIzZi0zMWI5LTExZTctYmRmYy0xMzJkMDc0Zjc3YzggVmVyc2lvbjogJExBVEVTVAoyMDE3LTA1LTA1VDE3OjM4OjMwLjY4NloJYTc1ZjIyM2YtMzFiOS0xMWU3LWJkZmMtMTMyZDA3NGY3N2M4CXsibmFtZSI6ImpvbmF0aGFuIn0KRU5EIFJlcXVlc3RJZDogYTc1ZjIyM2YtMzFiOS0xMWU3LWJkZmMtMTMyZDA3NGY3N2M4ClJFUE9SVCBSZXF1ZXN0SWQ6IGE3NWYyMjNmLTMxYjktMTFlNy1iZGZjLTEzMmQwNzRmNzdjOAlEdXJhdGlvbjogMTAwMjcuMjkgbXMJQmlsbGVkIER1cmF0aW9uOiAxMDEwMCBtcyAJTWVtb3J5IFNpemU6IDEyOCBNQglNYXggTWVtb3J5IFVzZWQ6IDE3IE1CCQo=","Payload":"\"My name is Jonathan\""}
END RequestId: a6a98e8d-31b9-11e7-aba1-d5d86092115f
REPORT RequestId: a6a98e8d-31b9-11e7-aba1-d5d86092115f  Duration: 11233.34 ms   Billed Duration: 11300 ms   Memory Size: 128 MB Max Memory Used: 30 MB  

Note the structure of the callback's input:

{
  "StatusCode": 200,

  // this is the logs that Y's printed to the "console" (base64 encoded)
  "LogResult": "U1RBUlQgUmVxdWVzdElkOiBhNzVmMjIzZi0zMWI5LTExZTctYmRmYy0xMzJkMDc0Zjc3YzggVmVyc2lvbjogJExBVEVTVAoyMDE3LTA1LTA1VDE3OjM4OjMwLjY4NloJYTc1ZjIyM2YtMzFiOS0xMWU3LWJkZmMtMTMyZDA3NGY3N2M4CXsibmFtZSI6ImpvbmF0aGFuIn0KRU5EIFJlcXVlc3RJZDogYTc1ZjIyM2YtMzFiOS0xMWU3LWJkZmMtMTMyZDA3NGY3N2M4ClJFUE9SVCBSZXF1ZXN0SWQ6IGE3NWYyMjNmLTMxYjktMTFlNy1iZGZjLTEzMmQwNzRmNzdjOAlEdXJhdGlvbjogMTAwMjcuMjkgbXMJQmlsbGVkIER1cmF0aW9uOiAxMDEwMCBtcyAJTWVtb3J5IFNpemU6IDEyOCBNQglNYXggTWVtb3J5IFVzZWQ6IDE3IE1CCQo=",


  "Payload": "\"My name is Jonathan\"" // <--- this is the returned value
}

Hopefully this answers your question.

Problem

I am using the following nodeJS lambda function to invoke a java lambda function. ``` var AWS = require('aws-sdk'); var lambda = new AWS.Lambda({httpOptions:{timeout: 300000}}); exports.handler = function(event, context) { var params = { FunctionName: 'sensi', // the lambda function we are going to invoke InvocationType: 'RequestResponse', Payload: '{"input":"alum_ROW,10,-1"}', LogType:'Tail' }; lambda.invoke(params, function(err, data) { if (err) { context.fail(err); } else { context.succeed(data.LogResult); } }) }; ``` The execution of the caller lambda is successful and the function I am calling is getting executed correctly. The problem is the caller lambda ends before the called lambda function ends and I got `null` as `data.payload` and hexadecimal characters as `data.LogResult`. obviously what is needed is to wait until the called lambda is finished. below the lambda i am trying to call: ``` public boolean run(String input ){ String [] inputs= input.split(","); String systemId=inputs[0]; int iterations=Integer.parseInt(inputs[1]); int output=Integer.parseInt(inputs[2]); //custom code return true; } ``` This function is inside class `Launchers.Launcher` and therefore the `sensi` lambda function has handler `Launchers.Launcher::run` EDIT: SOLUTION PROVIDED BY @JOHN adding: context.callbackWaitsForEmptyEventLoop = false;

Original source