We have utilized Amazon Serverless Architecture for a few critical parts of our system that needs to be always running. Using the ASP.NWT Core template that's offered by AWS tools to create and deploy the solution is up and running in no time. Now a couple of our operations are time-sensitive, and AWS has indicated that lambda will live for about 15 minutes, this is only for the first instance. Due to the nature of our system, those lambdas will get hit excessively by the outside world through an API Gateway proxy, so because one instance can't handle the load we need 4-5 instances of this lambda to be running simultaneously.

Environment

Net core got a major improvement in v3 with ReadyToRun (AOT) deploy mode, which would boost the cold start of the lambda function, but custom runtimes didn't get much good feedback from the industry so we went for v2.1, latest supported runtime as of this writing.

CloudWatch to ping lambda

The recommended approach to keep a lambda instance alive is to set up a CloudWatch event that will ping the lambda endpoint periodically.

To set up CloudWatch event rule just hit CloudWatch in your AWS console, the open Rules submenu, and click Create.

  • Choose Schedule as the event source
  • Pick target the one lambda you want to keep running
  • Configure Input as Constant (JSON text)
  • Add the payload that will hit the function entry point
{ "Resource": "KeepAliveClickLambda", "Body": 5 }

Lambda FunctionHandler

The default project template comes with two entry points, LocalEntryPoint used for local debugging and the LambdaEntryPoint one used by AWS infrastructure. We need to override the FunctionHandler method behavior to listen for our CloudWatch call and keep up the number of instances defined in the Body JSON payload. Also for the lambda to call itself we need to install AWSSDK.Lambda NuGet package.

public override async Task<APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayProxyRequest request, ILambdaContext lambdaContext)
{
    if (request.Resource == "KeepAliveClickLambda")
    {
        // this is a hit from CloudWatch cron job, or lambda itself
        if (int.TryParse(request.Body, out int totalInstances))
        {
            if (totalInstances > 1)
            {
                // we need to repeat this call for number of instances defined in payload, minus this one
                var client = new AmazonLambdaClient();
                await client.InvokeAsync(new Amazon.Lambda.Model.InvokeRequest
                {
                    FunctionName = lambdaContext.FunctionName,
                    InvocationType = InvocationType.RequestResponse,
                    Payload = JsonConvert.SerializeObject(new APIGatewayProxyRequest
                    {
                        Body = (totalInstances - 1).ToString(),
                        Resource = request.Resource
                    })
                });
            }
        }

        return new APIGatewayProxyResponse();
    }

    return await base.FunctionHandlerAsync(request, lambdaContext);
}

Summary

With this in place, the keep-alive function should be up and running, so we are good for now.