serverless-adapter
serverless-adapter copied to clipboard
Long execution time caused by `callbackWaitsForEmptyEventLoop=true` on AWS Stream
Feature Request
We needed to set callbackWaitsForEmptyEventLoop to false for our use-case; without this, our lambda invocations were taking 30s instead of 8ms, so it's a huge deal for us.
It was a bit difficult to figure out how to access and modify the AWS context object, so I wanted to share our solution here for others to use in the future:
import {
type BinarySettings,
type ILogger,
ServerlessAdapter,
} from "@h4ad/serverless-adapter";
import {
type AWSStreamContext,
AwsStreamHandler,
} from "@h4ad/serverless-adapter/lib/handlers/aws";
// Extend AwsStreamHandler to access the context object
class CustomAwsStreamHandler<TApp> extends AwsStreamHandler<TApp> {
protected onReceiveRequest(
log: ILogger,
event: APIGatewayProxyEventV2,
context: AWSStreamContext,
binarySettings: BinarySettings,
respondWithErrors: boolean,
): void {
super.onReceiveRequest(
log,
event,
context,
binarySettings,
respondWithErrors,
);
context.context.callbackWaitsForEmptyEventLoop = false;
}
}
// set the stream handler on the adapter
export const handler = ServerlessAdapter.new(app)
.setHandler(new CustomAwsStreamHandler())
...
.build();
You can call this function and you will have access to the context and the event:
https://github.com/H4ad/serverless-adapter/blob/53d431b25a57622173d26b6ddbb6baca389a8c1d/src/core/current-invoke.ts#L28-L42
Are you using PromiseResolver or CallbackResolver?
🤔 Where would I call this function? Our entire handler creation looks like this:
export const handler = ServerlessAdapter.new(app)
.setFramework(new ExpressFramework())
.setHandler(new CustomAwsStreamHandler())
.setResolver(new DummyResolver())
.addAdapter(new ApiGatewayV2Adapter())
.build();
This is following the documentation, which states that only DummyResolver is needed for streaming. We are not using CallbackResolver or PromiseResolver.
Oh, I got it, I didn't see you were using AWS Stream Handler.
Hm... in this case, I think is worthy to have a flag to control this behavior, I'm suspect you have something in your event loop that makes the lambda keep alive for that amount of time but since resolving the promise is not enough, having that flag always false should be good.
Thanks for reporting this, I will probably add a option to customize that value in the future, and in a breaking change, I will try to make it default to false always to avoid these kind of issues.
That is wonderful, thank you!
Yes, we clearly have some DB connections or something that aren't getting closed. We always used this setting with the non-streaming API, so we just needed a way to continue using it.
Just to let you know, I released a new version with the flag to make callbackWaits to false, see more: https://serverless-adapter.viniciusl.com.br/docs/main/handlers/aws#my-execution-is-taking-too-long
Wow, thank you very much!