At QCon New York John Chapin presented "Fearless AWS Lambdas", and not only argued that the JVM is a good platform on which to deploy serverless code, but also provided guidance on extracting the best performance from Java-based AWS Lambda functions. Key takeaways included that developers should focus on reducing and amortising the impact of a "cold start" of a function, benchmark functions extensively to determine real-world performance, and use full-featured logging and metrics libraries rather than the native options.
Chapin, co-founder at Symphonia, began the talk by discussing core characteristics of "serverless" computing: there is no management of long-lived host (servers) or application instances; self auto-scaling/provisioning is based on load; costs are based on precise usage, with zero usage resulting in zero cost; performance capability is defined in terms other than host size/count; and the platform offers implicit high availability. Further exploration of this topic is provided in a free O'Reilly minibook "What is Serverless?" that was recently written by Chapin and Mike Roberts.
AWS Lambda was introduced as one implementation of the Function as a Service (FaaS) serverless platform, and although language support is offered for JavaScript (node.js), Python and C#, this presentation focused purely on Java and the JVM. The Lambda runtime environment is configurable (and priced per 100ms of execution time) based on function memory, from 128MB to 1.5GB of RAM, and other resources like CPU and network I/O are allocated proportionally. Access to a 500MB /tmp directory on the underlying filesystem is provided, and STDOUT and STDERR are redirected to AWS Cloudwatch logs (which can be viewed via the AWS Console). Although fully abstracted away from the developer, Lambas run within Linux containers on AWS EC2, and are created on demand and reaped when idle, old, or obsolete.
Attempting to characterise performance of the underlying Java AWS Lambda platform is difficult due to the layers of abstraction, event handling and retries, and automated scaling. However, Chapin has created a benchmark Java Lambda that utilises two threads to calculate Fibonacci sequences. This can be run with varying memory settings, with the initial hypothesis being that a Lambda with 1.5GB of memory configured should run twelve times faster than a Lambda with only 128MB configured. The results of a recent run of the benchmark were presented, and the hypothesis was proved (although it was noted that other previous runs had occasionally generated inconsistent performance).
The concept of "cold starts" was discussed next, and this can result in the slowest function response times as the underlying runtime container, JVM, and application have to initialise due to an existing function instance not being ready to serve an event trigger (either through lack of event triggers or underlying platform operations, such as hardware restarts). Results of long running experiments that monitored cold starts were discussed, and two days of data from running Lambdas in the us-west-2 region showed that a Lambda with 128MB RAM configured was restarted 1.8% of the time, and a Lambda with 1.5GB RAM configured was restarted 0.97% of the time.
Deploying a Java Lambda function simply requires the upload of a zipped artifact containing the application code and all dependencies. Chapin recommends creating an uberjar using a tool like the maven-shade-plugin. Multi-Lambda projects can also be created using multi-module Maven projects, and the AWS Bill-Of-Materials (BOM) can be used to import compatible AWS SDK module versions when depending on multiple AWS libraries. In addition to creating deployable artifacts, other associated runtime cloud infrastructure can be specified using the AWS Serverless Application Model (SAM). AWS SAM extends AWS CloudFormation to provide a simplified way of defining the Amazon API Gateway APIs, AWS Lambda functions, and Amazon DynamoDB tables needed by serverless applications.
The penultimate section of the talk explored the JVM runtime on AWS Lambda. Interrogating the runtime underlying Lambda reveals that the JVM is OpenJDK 1.8 Server VM with the following flags set:
- -XX:MaxHeapSize = 85% of configured Lambda memory
- -XX:+UseSerialGC
- -XX:+TieredCompilation
- -Xshare:on
A cold start of a Java Lambda follows the typical lifecycle of a Java applications, with class loading, constructor and static block initialisation, alternative language runtime loading (e.g. Clojure), and JIT compilation. Chapin stated that for best startup times a "Lambda diet" of ruthlessly removing unneeded dependencies should be applied.
The final topic of the presentation, logging and metrics, began with a discussion that System.out/err output is directed to Cloudwatch, with one "log group" per Lambda by default. Using System.out.println is discouraged for the same reasons as with any Java application - using a logging library allows for much more control and the inclusion of metadata. The Lambda runtime can currently add RequestId to Log4J logs but the current aws-lambda-java-log4j library uses Log4J 1.2, which is an old, unsupported version. Instead, it was recommended to use the open source lambda-logging created by Symphonia, which utilises a current version of SLF4J and Logback.
The AWS Lambda platform does not currently support the easy capture of "business metrics", and instead focuses on Lambda platform metrics, such as errors, duration, invocations, and throttles. Chapin cautioned that a native Cloudwatch approach to metrics collection is dangerous, as Cloudwatch has account-level API limits that can easily be exceeded (which can cause all account logging to fail!). Cloudwatch Metric Filters can be used, but Cloudwatch Logs data must be scraped using "special (finicky) patterns" in order to generate and post Cloudwatch metrics. As an alternative, Chapin and the Symphonia team have created the open source lambda-metrics library that consists of Codahale metrics and lambda-logging, and uses a Maven plugin to build Metric Filters that will scrape the Cloudwatch logs and post to Cloudwatch metrics.
Chapin summarised the talk by stating that he recommends using Lambda's JVM runtime to build business applications, and developers should focus on reducing and amortising cold start impact, benchmark functions extensively, and use real logging and metrics. Additional information on John Chapin's presentation, "Fearless AWS Lambdas", can be found on the QCon New York website, and the video recording will be made available on InfoQ in the coming months.