It's a Java lambda and it's taking 4s to perform each of 2 DynamoDB operations, using the AWS-supplied SDK. I've heard that these typically complete within <20ms, but for me it's 3 orders of magnitude higher. These long operations are (a) creating the DynamoDB object and (b) performing a table scan on a completely empty table (code below).
What should I do to reduce the latency?
Creating the DynamoDB objects
log("Creating AmazonDynamoDB");
AmazonDynamoDB db = AmazonDynamoDBClientBuilder
.standard()
.withRegion(Regions.EU_WEST_1)
.build();
log("Creating DynamoDBMapper");
DynamoDBMapper mapper = new DynamoDBMapper(db);
Performing the scan
log("Scanning table");
List<MyItem> items = dbMapper.scan(MyTable.class, new DynamoDBScanExpression());
for (MyItem item : items) {
// Irrelevant - there aren't any
}
log("Table scan complete");
And here are the logs from a run.
20:07:41 START RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc Version: $LATEST
20:07:41 Received request: APIGatewayRequest(path=/data/foo, httpMethod=POST, body=)
20:07:41 Creating AmazonDynamoDB
20:07:45 Creating DynamoDBMapper
20:07:45 Creating DataHandler
20:07:45 Handling request
20:07:45 Scanning table
20:07:49 Table scan complete
20:07:49 Request handled - response object: []
20:07:49 APIGatewayResponse(isBase64Encoded=false, statusCode=200, body=[], headers={})
20:07:49 END RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc
20:07:49 REPORT RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc Duration: 8256.47 ms Billed Duration: 8300 ms Memory Size: 512 MB Max Memory Used: 85 MB
Use caching: If your traffic is read heavy, consider using a caching service, such as Amazon DynamoDB Accelerator (DAX). DAX is a fully managed, highly available, in-memory cache for DynamoDB that delivers up to a 10x performance improvement, from milliseconds to microseconds, even at millions of requests per second.
You can increase your DynamoDB throughput by several times, by parallelizing reads/writes over multiple partitions. Use DynamoDB as an attribute store rather than as a document store. This will not only reduce the read/write costs but also improve the performance of your operations considerably.
DynamoDB has very low latency (taken to complete an operation) in its operations, with delays ranging from 10 to 20 ms. Sometimes, DynamoDB has high latency in its workload. As a result, it causes delayed responses that slow down your application.
According to this post from an AWS employee on the AWS forums, construction of the AmazonDynamoDB
object is expensive. Moving construction (back) into the static initializer combined with a little extra memory (=CPU) allocation basically sorts the problem.
Data from the logs still shows that each of the 2 slow steps identified above takes about half the time. Therefore, presumably both construction and first use of the AmazonDynamoDB
objects are slow.
Obviously this doesn't help with the first request which still takes the same time as in the question. However, once the lambda is warmed, subsequent requests take ~15ms (well below the minimum billing threshold of 100ms). Addressing the first request problem is well understood - e.g. by using CloudWatch Events to schedule a regular call to the lambda to keep it warm.
Edit in 2020: You can also use Provisioned Currency to deal with the cold start problem.
(isn't a answer, but i hope it helps someone else) I have made the updates posted here and i besides them, i had to made a "dummy" query operation on dynamoDb (to open a connection with it), just in case of if help someone else, my code is as follows:
class MyFunctionHandler : RequestHandler<Map<String, Any>, ApiGatewayResponse> {
//var dbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.defaultClient()
var dbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder
.standard().withRegion("sa-east-1").build()
override fun handleRequest(input: Map<String, Any>, context: Context): ApiGatewayResponse {
LOG.info("received input: $input")
input["wakeup"]?.let {
if (it == true) {
with(EmpresaRepository(dbClient)) {
LOG.info("### Connection was not stablished at this point")
someDynamoQueryHere("dummyParameter")
LOG.info("### The Connection was opened and will keep alived for 1 minute")
}
return buildResponseForWakeUpReq(input)
}
}
val param = input["queryStringParameters"]?.toString()
...
The subsequent operations that will open the dynamoDb connection will occurs in terms of milliseconds!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With