aws lambda execution after callback guaranteed? - node.js

My node4 lambda function called via API GW makes a sequence of slow API calls.
In order to not let users wait until everything completes, I'm planning to have my code look like this:
function(event, context, callback) {
...
// Return users API GW call now
callback(null, data);
// Do the heavy lifting afterwards.
longApiCall().then(otherLongApiCalls)
}
But now I read in the AWS docs:
"the callback will wait until the Node.js runtime event loop is empty before freezing the process and returning the results to the caller"
Does that mean the API GW returns the response data before or after the longApiCalls complete?
If after, is there a suggested way for how to "return early" before everything is finished?

In your current configuration API Gateway will wait until the Lambda function has finished executing before sending a response. Your options are:
Change the API Gateway endpoint's integration type to AWS Service and have API Gateway invoke the Lambda function asynchronously. This is documented here.
Have the Lambda function that API Gateway invokes do nothing but invoke another Lambda function asynchronously and then return.
Have API Gateway, or a Lambda function called by API Gateway, send a message to an SNS topic. Then have the SNS topic trigger a Lambda function that handles the long API calls. This would decouple your microservices a bit.
Have API Gateway, or a Lambda function called by API Gateway, trigger an AWS Step Function that is configured to handle the long API calls via one or multiple Lambda functions. I would suggest this approach if the long API calls run the risk of running over a single Lambda function's execution time limit of 5 minutes.

Option 5. Let your lambda function queue a message to SQS and poll the queue from another lambda or ec2 or wherer you want to do the heavy lifting.

Related

200 ok response to request before work is done in aws lambda

Im trying to make a 200 ok response to a request before the work is done, however the work i need to do takes longer than the 3 seconds i need to make the response in.
Im working in aws lambda and the way i approached this was through threading:
t = threading.Thread(target=worker, args=(xml,))
t.start()
# So that you can return before worker is done
return response(200)
However, even when I threaded the work to be done in the background, it seems that aws lambda won't finish the work. It seems that as soon as the response is made, lambda just shuts down. For example, if the work takes 2 seconds to be done, then the following will not work:
t = threading.Thread(target=worker, args=(xml,))
t.start()
# So that you can return before worker is done
return response(200)
but if we sleep for 2 seconds, the work will be done:
t = threading.Thread(target=worker, args=(xml,))
t.start()
time.sleep(2)
# So that you can return before worker is done
return response(200)
If so, what can I do to make a 200 ok response to the request with aws lambda, but also have the work be done in the same lambda function?
A threading solution won't work, because once you send a response your Lambda environment will be shutdown. You need to trigger an AWS Lambda function in async mode instead of event. See this guide for one possible solution.
Alternatively you could have your current Lambda function simply call another Lambda function with an async event type that does all the actual work, and then return the 200 response code.
I will assume you're using AWS API Gateway to call your lambda function (hence the 200 status code return).
There are two ways to call a lambda, the sync way and the event or async way. API Gateway by default uses the first way unless you explicitly tell it otherwise. In order to make your lambda integration async by default you should add a custom header:
X-Amz-Invocation-Type: Event
In your API Gateway console. You can also add a
InvocationType: Event
header in you client calls

Error "ReferenceError: request is not defined" from basic Firebase Functions run

Trying to send off a webhook to Slack whenever onWrite() is triggered directed toward my Firebase DB. Going off a few other posts/guides I was able to deploy the below code, but get the ReferenceError: Request is not defined error on execution. I can't figure out how to fix the Request is not defined.
const functions = require('firebase-functions');
const webhookURL = "https://hooks.slack.com/services/string/string";
exports.firstTest = functions.database.ref('first').onWrite( event => {
return request.post(
webhookURL,
{json: {text: "Hello"}}
);
});
Calling your Cloud Function via an URL and sending back a response
By doing exports.firstTest = functions.database.ref('first').onWrite() you trigger your firstTest Cloud Function when data is created, updated, or deleted in the Realtime Database. It is called a background trigger, see https://firebase.google.com/docs/functions/database-events?authuser=0
With this trigger, everything happens in the back-end and you do not have access to a Request (or a Response) object. The Cloud Function doesn't have any notion of a front-end: for example it can be triggered by another back-end process that writes to the database. If you want to detect, in your front-end, the result of the Cloud Function (for example the creation of a new node) you would have to set a listener to listen to this new node location.
If you want to call your function through an HTTP request (possibly from your front-end, or from another "API consumer") and receive a response to the HTTP Request, you need to use another type of Cloud Function, the HTTP Cloud Function, see https://firebase.google.com/docs/functions/http-events. See also the other type of Cloud Function that you can call directly: the Callable Cloud Functions.
Finally, note that:
With .onWrite( event => {}), you are using the old syntax, see https://firebase.google.com/docs/functions/beta-v1-diff?authuser=0
The Firebase video series on Cloud Function is a good point to start to learn more on all these concepts, see https://firebase.google.com/docs/functions/video-series?authuser=0
Calling, from your Cloud Function, an external URL
If you want, from a Cloud Function, to call an external URL (the Slack webhook mentioned in your question, for example) you need to use a library like request-promise (https://github.com/request/request-promise).
See How to fetch a URL with Google Cloud functions? request? or Google Cloud functions call URL hosted on Google App Engine for some examples
Important: Note that you need to be on the "Flame" or "Blaze" pricing plan.
As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google.com/pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)

Firebase cloud functions - what happens with multiple HTTP triggers at once

I have a firebase cloud function that is an endpoint for an external API, and it handles a POST request.
This external API POSTS data to my cloud function endpoint at random intervals (this cloud function gets pinged with a POST request based on when a result is returned from this external API, and there can be multiple at once and its unpredictable)
exports.handleResults = functions.https.onRequest((req, res) => {
if (req.method === 'POST') {
// run code here that handles the POST payload
}
})
What happens when there is more than one POST request that come in at the same time?
Is there a queue? Does it finish the first request before moving on to the next?
Or if another request comes in while the function is running, does it block/ignore the request until the function is done?
Cloud Functions will automatically scale up the server instances running your functions when it determines that more capacity is needed. Those instances will run your function concurrently. The instances will be scaled down when they are no longer needed. The exact behavior is not documented - it should be considered an implementation detail that may change over time.
To learn more about this, watch my video about Cloud Functions scaling and isolation.

Azure Function hangs on error

If you have a error in code and did not callback, Azure Function just hangs.
AWS by default detects for empty loop and exits the lambda and logs the error, return the call to the caller with 5xx. It can also be controlled with callbackWaitsForEmptyEventLoop = false.
Is there is anything in Azure Functions that we can control how the function should exist in case of unhandled exceptions.
This currently isn't supported on Azure Functions.
It's the first I've heard this request, but it's a good idea. If you open a GitHub issue for it, I can take a look. (I'm the dev responsible for Node.js on Azure Functions) https://github.com/Azure/azure-functions
If you are using Azure Functions beta instead of using callbacks you may also export your functions as async which should allow the node worker to handle the exception without further intervention.
When exporting async functions calling the callback is redundant, and unnecessary.
Example:
module.exports = async function (context, req) {
throw new Error('Error')
}
Returns:
Request URL:http://localhost:7071/api/HttpTriggerJS
Request Method:GET
Status Code:500 Internal Server Error
Remote Address:[::1]:7071
Referrer Policy:no-referrer-when-downgrade

Inform browser clients when Lambda function is done using Amazon SQS

In my scenario I'm trying to implement server less backend that runs pretty long time consuming calculations. This calculations is managed by Lambda that refers to some external API.
In oder to request this I'm using Amazon API Gateway which has 10 seconds execution limitation. However Lambda runs about 100 seconds.
To avoid this limitation I'm using 2nd Lambda function to execute this time consuming calculation & report that calculation is started.
I looks very similar to this:
var AWS = require('aws-sdk');
var colors = require('colors');
var functionName = 'really-long'
var lambda = new AWS.Lambda({apiVersion: '2015-03-31'});
var params = {
FunctionName: functionName,
InvocationType: 'Event'
};
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(functionName.green + " was successfully executed and returned:\n" + JSON.stringify(data, null, 2).gray); // successful response
});
console.log("All done!".rainbow);
This code is executed over AWS API Gateway by thousands of clients browsers independently.
To inform each particular client that his Lambda function execution was successfully done I'v planed to use AWS SQS (because of long polling and some other useful functionalities out of the box).
So my question is:
How can I determine on the client which message in the queue belongs to this particular client? Or should I iterate over all queue to find proper messages by some request ID parameter in every client browser? I guess that this method will be inefficient when 1000 client will be simultaneously waiting for their results.
I do understand that I can write results to DynamoDB for example and periodically poll DB for the result via some homemade API. But is there any elegant solution to notify browser based client about completion of execution of time consuming Lambda function based on some Amazon PaaS solution?
Honestly the DynamoDB route is probably your best bet. You can generate a uuid in the first Lambda function executed by the API Gateway. Pass that uuid to the long-running Lambda function. Before the second function completes have it write to a DynamoDB table with two columns: uuid and result.
The API Gateway responds to the client with the uuid it generated. The client then long-polls with a getItem request against your DynamoDB table (either via the aws-sdk directly or through another API Gateway request). Once it responds successfully, remove said item from the DynamoDB table.
The context object of the lambda function will have the AWS request ID returned to the client that invoked the Lambda function.
So, client will have the lambda request ID of Lambda 1, Lambda 1 Context object will have the same request Id (irrespective of lambda retries, request ID remains same). So pass this request ID to Lambda 2 there by actual request ID is chained till the end.
Polling using the request id from client is fairly easy on any data store like dynamodb.

Resources