Tuesday, August 24, 2021

Choosing a language for AWS Serverless

Choosing a language that best suits for AWS serverless application solely depends on your level of comfort and skills with each of the language/supported runtimes. However, language selection becomes a key consideration when application runtime is exepected to have great performance characteristics. We can compare performace of language runtimes by dividing them into 2 major categories;
  1. Compiled languages (Java and .NET)
  2. Has the largest Cold-Start overhead, therefore, container's first invocation is known to be slow but shows best performance for subsequent invocations.
  3. Interpreted languages (Node.js and Python)
  4. The interpreted languages have very fast initial invocation times compared to the compiled languages, but can’t reach the same level of maximum performance as the compiled languages.

What is Cold Start?
“Cold start” in the serverless world means that serverless application is started and initialized to handle the request. In here, “serverless application” term represents both application and container itself where user code runs. Initialization adds extra latency to the execution of the request since they need to be done before handling the request.

Fortunately, this initialization doesn’t occur at every request as almost all the serverless platforms are smart to reuse containers as much as possible. However depending on the serverless platform itself, existing containers can be destroyed and new ones can be created at any time due to many internal (resource scheduling/sharing, fixes/patches on the host environment, etc …) or external (new application deploy, configuration change, etc …) reasons.
In AWS Lambda platform, the following causes trigger new container starts which results in cold starts:
  • There is no container alive
  • There are containers alive but none of them are available as all of them are busy with handling other requests
  • This happens if your application use case has very spiky traffic.
  • New application was deployed so new containers must start with the newer version of the application
  • Configuration (env. variable, memory limit, etc …) are changed so new containers must start with new configurations

How to reduce cold start overhead?
  • Sending periodic warmup requests
  • AWS Lambda Custom Runtime support that bootstrapping the runtime
  • AWS can optimize the runtime bootstrapping phase to start faster (for ex. at Java runtime, by tweaking JVM arguments) This happens if your application use case has very spiky traffic.
  • Use latest AWS SDKs
  • For example, AWS Java SDK 2 has 500–750 ms less initialization overhead than AWS Java SDK1.

Saturday, August 21, 2021

How to use dynamic DynamoDB table name for query

In my current project, it requires to initialize the names of DynamoDB tables using environment variables during runtime. So far so good but the problem is that the API requires the annotation @DynamoDBTable on my POJO. This is a compile-time annotation requires a table name parameter and so would restrict me using the POJO in a dynamic manner.

Solution
Looking a bit closer at the DynamoMapper API, I found a class called DynamoDBMapperConfig. This class allows us to specify a so called TableNameOverride that overrides the table name defined in the DynamoDBTable annotation in Java class. You can supply a DynamoDBMapperConfig as a second parameter to several methods in the DynamoMapper API. For example if you want to override the table name defined in the following class;
       

    @DynamoDBTable(tableName = "temp_name")
    public static class IDRecord {
        private String id;
        private String StartDate;
        private String EndDate;

        @DynamoDBHashKey(attributeName = "ID")
        public String geID() {
            return id;
        }

        public void setID(String id) {
            this.id = id;
        }

        @DynamoDBAttribute(attributeName = "StartDate")
        public String getStartDate() {
            return StartDate;
        }

        public void setStartDate(String StartDate) {
            this.StartDate = StartDate;
        }

        @DynamoDBAttribute(attributeName = "EndDate")
        public String getEndDate() {
            return EndDate;
        }

        public void setEndDate(String EndDate) {
            this.EndDate = EndDate;
        }

        @Override
        public String toString() {
            return "IDRecord [ID=" + id + ", StartDate=" + StartDate  + ", EndDate=" + EndDate+ "]";
        }

    }
  

       
 
when querying the table, do the following. This will ignore the table name defined by the @DynamoDBTable annotation in IDRecord class and instead use the tableName that we read from environment variable.
       

private static DynamoDBMapperConfig dynamoConfig = new DynamoDBMapperConfig(new DynamoDBMapperConfig.TableNameOverride(System.getenv("ID_TABLE")));

QueryResultPage queryResult = mapper.queryPage(IDRecord.class, queryExpression, dynamoConfig);