AWS Lambda get context message - node.js

I am using the test function from AWS console:
console.log('Loading event');
exports.handler = function(event, context) {
console.log('value1 = ' + event.key1);
console.log('value2 = ' + event.key2);
console.log('value3 = ' + event.key3);
context.done(null, 'Hello World'); // SUCCESS with message
};
And calling it in nodejs as follows:
var params = {
FunctionName: 'MY_FUNCTION_NAME', /* required */
InvokeArgs: JSON.stringify({
"key1": "value1",
"key2": "value2",
"key3": "value3"
})
};
lambda.invokeAsync(params, function(err, data) {
if (err) {
// an error occurred
console.log(err, err.stack);
return cb(err);
}
// successful response
console.log(data);
});
and everything works fine:
//Console Output
{ Status: 202 }
But I was expecting to receive the message from context.done(null, 'Message') as well...
Any idea how to get the message?

As Eric mentioned, currently Lambda doesn't offer a REST endpoint to run the function and return its result, but may in the future.
Right now, your best bet would be to use a library like lambdaws, which wraps the function deployment and execution for you and handles returning results via an SQS queue. If you'd like more control by rolling your own solution, the process is straightforward:
Create an SQS queue
Have your Lambda function write its result to this queue
In your client, poll the queue for a result

You are calling invokeAsync, so your Lambda function is run asynchronously. This mean you get the success means back at the point your function is successfully started, and not after it completes.
As of this writing, AWS Lambda does not yet offer a way to invoke a function synchronously, returning information from the function directly back to the caller. However, this appears to be a common request and Amazon has publicly stated they are considering the feature.

Related

How to asynchronously make HTTP Requests in AWS Lambda Node.js runtime

I have created an API endpoint that returns an integer when it is successfully accessed with an HTTP Post request. I want an AWS CloudWatch scheduled process to run an AWS Lambda function every minute to check the API endpoint so make sure the value is not zero. I have set up the scheduled AWS CloudWatch process and the AWS Lambda function, where the runtime is Node.js 10. However, when I look at the AWS CloudWatch group's stream log, the logs seem to be out of order. I suspect this is because of the HTTP Request to the API endpoint is running asynchronously, but I ultimately do not know. The time that it takes to log the value of this HTTP Request to the API end point seems to be much longer than the actual time to process the request.
Here is the Node.js lambda function that is being run on a minutely basis:
exports.handler = async (event) => {
var datetime = new Date();
var request = require("request");
var options = {
method: 'POST',
url: 'https://website.com/api/getDataPoints',
headers:
{
'cache-control': 'no-cache',
'content-type': 'text/plain'
},
body: '{"token" : "yT7g8urUFmEZwQrJNHgQGRDA9zScpNzPM3rb"}'
};
await request(options, function (error, response, body) {
if (error)
{
throw new Error(error);
// Email and SMS message that this is having an error
}
if (body == 0)
{
// Email and SMS message that this is having an error
// Restart EC2 server
}
console.log(datetime.toString() + " - " + body + " Data Points!");
});
};
Here is the AWS CloudWatch log, where it's easier to see the delay in logging the response from the HTTP Request:
Any insight into the cause of this perceived log delay or suggestions on how to achieve similar results in a more efficient way would be greatly appreciated! Thank you!
Here is what is happening: the await is returning immediately because the request function does not return a promise, so the callback is happening after the lambda function exits. The reason why there is such a long delay in seeing the log is that when your lambda function becomes idle (meaning all handler functions have returned, even if there are callbacks waiting) AWS can suspend any executing code until a new invocation of the function. So in your case when the next minute rolls around and the lambda function is invoked again, AWS will un-suspend any executing code and the callback kicks off immediately and you see the log for the previous invocation.
To solve this you want to make sure the handler function doesn't return until all work has been completed (which is what I think you were intending to do with the await). So wrap the request call in a function that returns a promise which is resolved in the callback and use await on the wrapper function.
function doRequest() {
return new Promise((resolve, reject) => {
request(options, function (error, response, body) {
if (error){
throw new Error(error);
// Email and SMS message that this is having an error
}
if (body == 0){
// Email and SMS message that this is having an error
// Restart EC2 server
}
console.log(datetime.toString() + " - " + body + " Data Points!");
resolve();
});
});
}
await doRequest();

AWS Lambda finish before sending message to SQS

I'm running a "Node.JS" lambda on AWS that sends a message to SQS.
For some reason the SQS callback function get execute only once every couple of calls. It's looks like that the thread that running the lambda finish the run (because it's not a synchronous call to SQS and also can't return a Future) and therefore the lambda doesn't "stay alive" for the callback to get executed.
How can I solve this issue and have the lambda wait for the SQS callback to get execute?
Here is my lambda code:
exports.handler = async (event, context) => {
// Set the region
AWS.config.update({region: 'us-east-1'});
// Create an SQS service object
var sqs = new AWS.SQS({apiVersion: '2012-11-05'});
const SQS_QUEUE_URL = process.env.SQS_QUEUE_URL;
var params = {
MessageGroupId: "cv",
MessageDeduplicationId: key,
MessageBody: "My Message",
QueueUrl: SQS_QUEUE_URL
};
console.log(`Sending notification via SQS: ${SQS_QUEUE_URL}.`);
sqs.sendMessage(params, function(err, data) { //<-- This function get called about one time every 4 lambda calls
if (err) {
console.log("Error", err);
context.done('error', "ERROR Put SQS");
} else {
console.log("Success", data.MessageId);
context.done(null,'');
}
});
};
You should either stick to callback based approach, or to promise based one. I recommend you to use the latter:
exports.handler = async (event, context) => {
// Set the region
AWS.config.update({region: 'us-east-1'});
// Create an SQS service object
var sqs = new AWS.SQS({apiVersion: '2012-11-05'});
const SQS_QUEUE_URL = process.env.SQS_QUEUE_URL;
var params = {
MessageGroupId: "cv",
MessageDeduplicationId: key,
MessageBody: "My Message",
QueueUrl: SQS_QUEUE_URL
};
console.log(`Sending notification via SQS: ${SQS_QUEUE_URL}.`);
try {
await sqs.sendMessage(params).promise(); // since your handler returns a promise, lambda will only resolve after sqs responded with either failure or success
} catch (err) {
// do something here
}
};
P.S. Instantiating aws classes in the handler is not a good idea in lambda environment, since it increases the cold start time. It's better to move new AWS.SQS(...) action out of handler and AWS.config.update() too, since these actions will be executed on each call of the handler, but you really need them to be executed only once.

firebase http function termination and promise

I have the following firebase cloud function
exports.sendToUser = functions.region('europe-west1').https.onRequest((request, response) => {
console.log('start');
const receiverUid = request.body.receiverId;
const requestingUid = getUserIdFromRequest(request);
// Notification details.
const payload = {
"data": {
"type": request.body.type,
"mainData": request.body.mainData
}
};
const options = {
"priority": "high"
}
console.log('check');
requestingUid.then(uid => {
console.log('complete');
payload.data.senderId = uid;
sendMessageToUser(receiverUid, payload, options);
response.status(200).send();
}).catch(err => {
console.error(err);
response.status(401).send(err);
});
});
My problem with this function is that it prints "start" and "check" all fine but doesn't print the "complete" or the "receiver" nor does it execute the "sendMessageToUser" function. However, the status 200 is responded and the function terminates which I can see in the logs in the developer console and on the client device.
Also, when replacing line response.status(200).send(); with response.status(400).send(); or some other response code the correct status (400) is sent but the logs are not printed and the "sendMessageToUser" function not executed.
From time to time, however, the function is properly executed so I'm assuming that the function is somehow prematurely terminated. But how can the correct response code be sent then? I'm aware of the necessity to return a promise from asynchronus functions but as this is a http-triggered (synchronus) function I wouldn't know how to handle this.
Besides, yesterday was the first time this particular error occured. Before that, it worked fine. So can anybody explain what is happening here?

Passing a callback to an event handler

I'm working on an Alexa skill in Node that runs in AWS Lambda, and having trouble getting a callback to execute when I emit an event. The Alexa-skills-kit for Node.JS README demonstrates passing a callback function to an event handler, and recommends using the arrow function to preserve context:
this.emit('JustRight', () => {
this.emit(':ask', guessNum.toString() + 'is correct! Would you like to play a new game?',
'Say yes to start a new game, or no to end the game.');
});
I'm trying to do the same, but find that my callback never appears to be executed. I thought this was because AWS Lambda is limited to Node 4.3.2, and the arrow function is not available, so I tried passing this context back to the callback the old fashioned way:
In New Session handler:
if(!account_id) {
console.log('Access token:' + accessToken);
this.emit('getAccount', accessToken, function (retrieved_id) {
console.log('account id in callback: ' + retrieved_id);
this.emit('welcome');
});
}
In event handler:
accountHandler = {
'getAccount': function (accessToken, cb) {
console.log('fetching account id');
var client = thirdparty.getClient(accessToken);
var r = client.getAccountForToken(client);
r.then(function (data) {
console.log('got it:' + data);
this.attributes['account_id'] = data;
cb.call(this, data);
}).catch(function (err) {
this.emit('handleApiError', err);
});
},
}
I can see that I'm successfully retrieving the account ID, in the logs, but Lambda is executing without error and without invoking my callback function. I'm trying to figure out if there is a problem with invoking the callback within the Promise 'then' function, or if something else is going on.
The exact problem was the lack of context within the promise then function. I fixed this by using the arrow function inside the getAccount handler:
r.then(data => {
console.log('got it:' + data);
this.attributes['account_id'] = data;
this.emit('welcome');
})
Of course this also shows that Lambda Node.JS functions support the arrow function just fine.

Return data from async AWS Lambda

How do I get the data back from a lambda invoked with as an event to the calling function?
Essentially the lambda function I have is:
exports.handler = function(event, context, callback) {
var data = {};
data.foo ='hello';
callback(null, data)
}
and the invoking function looks like this:
var AWS = require('aws-sdk');
var lambda = new AWS.Lambda();
var params = {
FunctionName: 'SomeFunction',
InvocationType: 'Event'
};
lambda.invoke(params, function (err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
console.log(JSON.stringify(data, null, 2));
}
});
However the only thing I get back from the function is
{
"StatusCode": 202,
"Payload": ""
}
I thought the point of the callback parameter was to allow the invoking function to get the data when the function has finished. Am I using it wrong or is what I am asking not possible with Lambdas?
When you invoke the Lambda function you need to set the InvocationType to 'RequestResponse' instead of 'Event'.
When using the Event type your callback is invoked when the payload has been received by AWS's servers. When using the RequestResponse type your callback is invoked only after the Lambda function has completed and you will receive the data it provided to its callback. It is not possible to do what you want with the Event type.
As #MatthewPope commented in this answer, if you do need the results from an asynchronous Lambda execution, you should consider having the Lambda function write its results to S3 (or something to that effect) at a known location where clients of the Lambda function can periodically check for the existence of the result.

Resources