Brooks Brooks - 11 days ago 9
Java Question

Replacement for AWS Lambda invokeAsync (deprecated)

I am trying to call a Java Lambda function asynchronously from within another Java Lambda function. I simply want to fire and forget, but with .invokeAsync(InvokeRequest), I have to call .get() on the Future, which then blocks and breaks the 'fire and forget' use case.

Here is the code I am trying to use:

private void sendToDownloader(String payload) throws InterruptedException, ExecutionException {
log.info(payload);
InvokeRequest invoke = new InvokeRequest();
invoke.withFunctionName("LambdaTwo")
.withPayload(payload)
.withInvocationType(InvocationType.Event);

lambdaClient.invokeAsync(invoke).get();
}


If I remove the .get() method call, it does not actually invoke LambdaTwo.

It should be noted, this lambda function ends immediately after invoking LambdaTwo.

Meanwhile, I tried using the following code:

private void sendToDownloader(String payload) throws InterruptedException, ExecutionException {
log.info(payload);
InvokeAsyncRequest invoke = new InvokeAsyncRequest();
invoke.withFunctionName("LambdaTwo")
.withInvokeArgs(payload);

lambdaClient.invokeAsync(invoke);
}


This code works, however .invokeAsync is deprecated and it says to see "Invoke API", for which I cannot find any documentation.

Can anyone lead me to the right way to invoke a Lambda function in a purely 'fire and forget' fashion?

EDIT 1
Per Mark's suggestion, I have rewritten the code below, however it seems to block while the LambdaTwo function executes.

public void routeEvent(String lambdaName, String cacheName) throws InterruptedException, ExecutionException, JsonProcessingException {
ObjectNode obj = new ObjectNode(JsonNodeFactory.instance);
obj.put("nodeName", cacheName);
InvokeRequest invoke = new InvokeRequest();
invoke.withFunctionName(lambdaName)
.withInvocationType(InvocationType.Event)
.withPayload(OBJECT_MAPPER.writeValueAsString(obj));

log.info(System.currentTimeMillis());
lambdaClient.invoke(invoke);
log.info(System.currentTimeMillis());
}


It prints out the following milliseconds.


2016-11-28 03:41:35 INFO LambdaFunction:97 - 1480304495867

2016-11-28 03:41:41 INFO LambdaFunction:99 - 1480304501432


There is a difference of about 5.5 seconds between the two and the Lambda duration confirms this.

EDIT 2
Just to clarify, I was trying the AWSLambdaAsyncClient calling the .invokeAsync(InvokeRequest) and .invoke(InvokeRequest) and neither worked. When calling invokeAsync, it does not actually invoke the Lambda function unless I call .get() on it, which then blocks while the second Lambda function executes.

What finally worked was calling .invokeAsync(InvokeRequest), however NOT calling .withInvocationType(InvocationType.Event) on the InvokeRequest object. It's not clear why that caused the asynchronous behavior, but that's what worked and not using a deprecated method.

public void routeEventAsync2(String lambdaName, String cacheName) throws InterruptedException, ExecutionException, JsonProcessingException {
ObjectNode obj = new ObjectNode(JsonNodeFactory.instance);
obj.put("nodeName", cacheName);
InvokeRequest invoke = new InvokeRequest();
invoke.withFunctionName(lambdaName)
.withPayload(OBJECT_MAPPER.writeValueAsString(obj));

lambdaAsyncClient.invokeAsync(invoke);
}

Answer

The documentation regarding the deprecation of this method is confusing. What they are trying to say is to use AWSLambdaClient.invoke(request). You will need to set the InvocationType to Event on the request object in order to invoke the Lambda function without waiting for a response.