Async function in NodeJS EventEmitter on AWS Lambda - node.js

I have an AWS Lambda application built upon an external library that contains an EventEmitter. On a certain event, I need to make a HTTP request. So I was using this code (simplified):
myEmitter.on("myEvent", async() => {
setup();
await doRequest();
finishingWork();
});
What I understand that happens is this:
My handler is called, but as soon as the doRequest function is called, a Promise is returned and the EventEmitter continues with the next handlers. When all that is done, the work of the handler can continue (finishingWork).
This works locally, because my NodeJS process keeps running and any remaining events on the eventloop are handled. The strange thing is that this doesn't seem to work on AWS Lambda. Even if context.callbackWaitsForEmptyEventLoop is set to true.
In my logging I can see my handler enters the doRequest function, but nothing after I call the library to make the HTTP call (request-promise which uses request). And the code doesn't continue when I make another request (which I would expect if callbackWaitsForEmptyEventLoop is set to false, which it isn't).
Has anyone experienced something similar and know how to perform an ansynchronous HTTP request in the handler of a NodeJS event emitter, on AWS Lambda?

I have similar issue as well, my event emitter logs all events normally until running into async function. It works fine in ECS but not in Lambda, as event emitter runs synchronously but Lambda will exit once the response is returned.
At last, I used await-event-emitter to solve the problem.
await emitter.emit('onUpdate', ...);

If you know how to solve this, feel free to add another answer. But for now, the "solution" for us was to put the eventhandler code elsewhere in our codebase. This way, it is executed asynchronously.
We were able to do that because there is only one place where the event is emitted, but the eventhandler way would have been a cleaner solution. Unfortunately, it doesn't seem like it's possible.

Related

AsyncLocalStorage not working for each request

I am using NestJS as a backend framework in NodeJS +16
I am trying to implement:
https://medium.com/#sascha.wolff/advanced-nestjs-how-to-have-access-to-the-current-user-in-every-service-without-request-scope-2586665741f
My idea is to have a #Injectable() service that will have, among other things, methods like:
hasUserSomeStuff(){
const user = UserStorage.get()
if(user) {
// do magic
}
and then pass this service around as it is usually done in NestJS
To avoid passing the request down the rabbit hole, or bubbling up the request scope so every dependency gets instantiated for each request, but also avoiding to use UserStorage everywhere where I need to get the user from the current request and do stuff
I've gone through the docs many times, it is my understanding that node would take care of instantiating a new storage for each async context (in my case each request), but what seems to happen to me is that when I first run my backend, it works just fine, I've got the user from the current request, but once the first async context / promise is completed, I retrieved data for the consumer, and in the next request UserStorage returns a undefined (as doc states it will if you are outside of the same async context, which I guess it is not what happens, as it should be a brand new async context)
However if I debug, what seems to happen is that this UserStorage is called and a new AsyncLocalStorage is instantiated at init, before the app is ready to be used, and then the very first request returns a undefined user.
I am failing to understand what is going on, can anyone help me on this, or any better approach to achieve my goal?
Thanks in advance

AWS Lambda function times out when I require aws-sdk module

I'm trying to query a DynamoDB table from a Lambda function (for an Alexa skill), but when I send the intent that calls require('aws-sdk'), the skill seems to hang and timeout. The Alexa test page just says "There was a problem with the requested skill's response", and I don't see any errors in the CloudWatch logs. I have the skill set up to catch any errors and return them as a spoken response, so I'm sure it's not an uncaught exception. I've also tried wrapping the require in a try/catch block, but that didn't work either.
This is the module that gets loaded with require if the test database intent request is received:
const AWS = require('aws-sdk');
module.exports = () => {
return 'Success!';
};
If I comment out require('aws-sdk'), the function works properly and Alexa responds with "Success".
Why does my skill break when all I'm doing is requiring the aws-sdk module?
I'm very new to AWS and this is my first experience trying to access a DynamoDB table in a Lambda function.
The Lambda function is uploaded as a zip that contains my source code, package.json (that includes aws-sdk as a dependency), and the node_modules folder.
After hours of debugging, I've found that changing import * as AWS from 'aws-sdk'; to import {DynamoDB} from 'aws-sdk'; (or {CloudFront} or whatever you actually use) made the timeout issue disappear. Mind you, the time to actually connect to DynamoDB was never an issue for me, it was always the import line where the timeout happened.
This can be fixed by either increasing the timeout or the memory allotted to the lambda function.
This is probably because the SDK is too big to be imported by the default timeout value of 3 seconds and the default memory value of 128 MB.
This is why it will probably work if you try importing smaller components like only DynamoDB.
Lambda, when using NodeJS, uses a callback continuation model. Your module should export a function that takes three parameters: event, context, and callback.
Event provides input parameters.
The other two are used for returning control from your handler function, depending on the NodeJS version you are using.
Try adding the three parameters that I mentioned and the, from within your exported handler function, call:
module.export = function(event, context, callback) {
callback(‘success’);
}
Bear in mind, I wrote this on mobile off the top of my mind, so you may need to make small afjustments to the code but the idea is the same. Don’t return directly from the function, but call the callback to supply the response as a continuation. And note that in earlier versions of NodeJS, prior to version 4, you would have to use the context to set success or failure, rather than calling the callback.
For more details, see the Lambda with NodeJS tech docs on AWS.
The other thing to keep in mind is that for Alexa specifically, the response should be in the correct format. That is a JSON response that contains all the necessary elements as explained in the Alexa Skills Kit tech docs.
The Alexa ASK sdk that you’ve included generates those responses but I thought I should point you to the actual docs in case you were going to try building the response by hand to understand how it works.

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

Why does sending multiple responses per one request crash a NodeJS server?

Might be an idiotic question, but I was wondering why does, i.e. invoking Express' res.send() (a subclass of NodeJS' http.ServerResponse) more than once per a single request shut down a NodeJS server? Why doesn't it end the request while sending the first response and simply log the error, without crashing?
Express is just throwing an exception, then node handles it :
The 'uncaughtException' event is emitted when an uncaught JavaScript exception bubbles all the way back to the event loop. By default, Node.js handles such exceptions by printing the stack trace to stderr and exiting. doc
If you want to do something else, implement your own process.on('uncaughtException', (err) => {})
Or you could let it crash and use stuff like forever to bring it back up.

Detecting Socket.IO message delivery error on client side

We need to update the client side UI to indicate that a message fails to deliver. How do I have Socket.IO JS client call a custom callback directly when the message fails to deliver? For example, something like:
socket.emit("event", data).onError(myCallback);
I know Socket.IO provides the Ack mechanism to confirm delivery success. Therefore, one can set up a timer with a handler which calls the failure callback, if the ack is not called after a certain amount of time. But this doesn't seem to be the best way to do.
Also there is the error event provided by Socket.IO, but it doesn't come with info regarding which emit caused the error.
Unfortunately there's no way to get errors from callbacks, the only way is to indeed create your own timeout:
var timeoutId = setTimeout(timeoutErrorFn, 500);
var acknCallbackFn = function(err, userData){
clearTimeout(timeoutId)
//manage UserData
}
socket.emit('getUserData', acknCallbackFn);
Source of the code
And there's another issue about this, open
So for the time being you have to stick with your manual setTimeout.

Resources