This question is inspired by this other: Is it a good idea to invoke lambda from other lambda in AWS lambda service?
I am using netcoreapp3.1 and C# to develop lambda.
I have one lambda A that accepts HTTP requests from API Gateway. I am using the dotnet template serverless.AspNetCoreWebAPI as per https://aws.amazon.com/blogs/developer/running-serverless-asp-net-core-web-apis-with-amazon-lambda where my lambda function reacts to invokations as if it was a web api.
I have another lambda B that also gets invoked by API Gateway, only this time it gets websocket events (CONNECT, DISCONNECT, MESSAGE).
I am thinking of having lambda B invoking lambda A synchronously.
The flow would be like this: API Gateway > Lambda B > Lambda A
I have here a dilemma:
- Is it a good idea to invoke directly Lambda A from Lambda B?
- Or is it better to have Lambda B send an http request to API Gateway and leave the gateway do its thing invoking Lambda A?
- Or should I forget about exposing Lambda A functionality as "http" and have a single entry point in Lambda A that accepts ANY payload so that Lambda B can invoke it with a custom message?
Cost and performance wise I guess there is no much difference, but invoking Lambda A from Lambda B seems "easier" as the only thing I really need is to know the name of the function.
Here comes my attempt to do that, I use the
public async Task<APIGatewayProxyResponse> FunctionHandler(
APIGatewayProxyRequest apiGatewayProxyRequest,
ILambdaContext context)
{
context.Logger.LogLine("Get Request\n");
var isWebSocketRequest = apiGatewayProxyRequest.HttpMethod is null;
if (!isWebSocketRequest)
{
throw new ArgumentException(
$"Received Http method {apiGatewayProxyRequest.HttpMethod}, not a valid websocket event");
}
var result = await ProcessWebsocketEvent(apiGatewayProxyRequest, context.Logger);
return result;
}
private async Task<APIGatewayProxyResponse> ProcessWebsocketEvent(APIGatewayProxyRequest apiGatewayProxyRequest, ILambdaLogger logger)
{
var eventType = apiGatewayProxyRequest.RequestContext.EventType;
string body = eventType;
if ("CONNECT" == eventType)
{
body += $", connection={apiGatewayProxyRequest.RequestContext.ConnectionId}";
}
else if ("DISCONNECT" == eventType)
{
body += $", connection={apiGatewayProxyRequest.RequestContext.ConnectionId}";
}
else if ("MESSAGE" == eventType)
{
body += $", body={apiGatewayProxyRequest.Body}";
}
logger.LogLine("Creating amazon lambda client..");
using var lambdaClient = new AmazonLambdaClient(RegionEndpoint.EUWest3);
var payload =
new
{
Foo = "bar",
Bar = "foo"
};
var json = JsonSerializer.Serialize(payload);
logger.LogLine($"With payload {payload}");
var request =
new InvokeRequest
{
FunctionName = "lambda-dotnet-webapi-function",
Payload = json
};
var responseInvoke = await lambdaClient.InvokeAsync(request);
using var stream = new StreamReader(responseInvoke.Payload);
var responsePayload = await stream.ReadToEndAsync();
var response = new APIGatewayProxyResponse
{
StatusCode = (int) HttpStatusCode.OK,
Body = responsePayload,
Headers = new Dictionary<string, string> {{"Content-Type", "text/plain"}}
};
return response;
}
First thing I noticed. If I have a 3 seconds timeout, it times out. I have to increase it. If I increase it to 10 seconds I am able to get an invocation as the invoke request's payload is not "correct"
{"statusCode":200,"headers":{"Content-Type":"text/plain"},"body":"{\n \"errorType\": \"NullReferenceException\",\n \"errorMessage\": \"Object reference not set to an instance of an object.\",\n \"stackTrace\": [\n \"at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)\",\n \"at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext)\",\n \"at lambda_method(Closure , Stream , Stream , LambdaContextInternal )\"\n ]\n}\n","isBase64Encoded":false}
The Lambda A runs for 800ms The Lambda B runs for 9000ms (9 seconds, a lot..)
This brings me to another question. If this is an acceptable approach, how can I send the request as if Lambda B was a "proxy"?
I am thinking of invoking Lambda A in an http-ish way, mainly because I really like the library https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.AspNetCoreServer which allows my Lambda to be implemented as a standard aspNet web api, read appsettings, dependency injection, test with test server, etc.
Looking at Lambda A logs I can see the following warning
[Warning] Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction: Request does not contain domain name information but is derived from APIGatewayProxyFunction.
and the exception that I am getting as the response in Lambda B:
Object reference not set to an instance of an object.: NullReferenceException
at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)
at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext)
at lambda_method(Closure , Stream , Stream , LambdaContextInternal )