Dart AOT + AWS API Gateway + Lambda

06/15/2019

In my previous post, Run Dart VM with AWS Lambda Custom Runtime, I covered how I got Dart running on AWS Lambda Custom Runtime. With startup time, memory consumption and execution time critical in a serverless environment I turned my focus on Dart’s dart2aot, see docs, to get a speed boost.

Api Gateway

I like to use AWS Lambda with API Gateway in my mobile apps. For this reason, I made a small refactor to squareNumber to add Api Gateway support. Now apiGatewaySquareNumber takes in an ApiGatewayEvent. This is just a parsed version of the Map<String, dynamic> from before. ApiGatewayEvent’s body remains a string and still needs to be parsed since this will be anything your HTTPS request sends in. However, with an ApiGatewayEvent, we have a typed input and I dig that!

The second change I made was to add the return type of ApiGatewayResponse. Nothing too special there, it has the required response properties Api Gateway requires and again keeping us honest with some type safety.

Future<ApiGatewayResponse> apiGatewaySquareNumber(
  ApiGatewayEvent apiRequest,
  Context context,
) async {
  final Map<String, dynamic> asJson =
      json.decode(apiRequest.body) as Map<String, dynamic>;
  final squaredNumber = asJson['number'] * asJson['number'];

  return ApiGatewayResponse(
    body: json.encode({'result': squaredNumber}),
    isBase64Encoded: false,
    statusCode: 200,
  );
}

Cold Starts

A downside of serverless is cold starts. A cold start occurs when your function hasn’t been invoked for an extended period of time. AWS evicts your function to make room for others. The next time you invoke your function AWS starts a new process to handle your request. If your process sits idle for a period of time AWS, again, evicts your process. Rinse and repeat. It’s great that you only pay for 100ms compute intervals, but in my experience using Dart JIT and NodeJS, a cold start can take 1-1.5 seconds. This isn’t a huge deal in my apps but who wouldn’t want that experience to be as close to 0 as possible. I decided to give Dart AOT a shot since I’d already been down the Dart JIT path.

The AOT Conclusion

TL;DR: Dart AOT is a fantastic fit for serverless workloads!

I used the same aws_lambda_dart code that I used within my JIT post but instead, I used the dart2aot tool to compile my Dart before uploading to Lambda. AOT fixed some my complaints about the JIT approach.

  • Cold starts improved from ~1-1.5 second(s) to ~500ms. After the cold start, I was seeing response times of <100ms, compared to JIT’s ~250-800ms.
  • AOT memory consumption dropped from ~150-175MB to ~50MB. This means I can consistently use the 128MB memory configuration.
  • BONUS: You only have to ship one compiled file! For me this was main.dart.aot. For a JIT Lambda you have to zip and upload each file that is used since the Dart VM reads all of them at startup, much like using NodeJS. With AOT everything required to run your main.dart is included in main.dart.aot!

Unfortunately, as of this post, there is no official or unofficial AWS SDK for Dart. This means while running in Lambda and you want to use other AWS services you have to handcraft API calls to AWS using the Signature Version 4 Signing . Hopefully aws-amplify and/or aws-sdk get implemented in Dart!

Questions or improvements, PR’s welcome at aws_lambda_dart!