Skip to content

How to reduce the cold start time when using MySQL with Lambda?

I am trying to deploy a REST API with Java, using AWS Lambda, API Gateway and Amazon RDS (MySQL). Below is my Lambda class

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.aaaa.beans.AccountingType;
import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.list;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 *
 * @author User
 */
public class GetAllAccountTypesLambda {


   static final String DB_URL = "jdbc:mysql://************";
   static final String USER = "*****";
   static final String PASS = "*****";
   static final String QUERY = "SELECT * from accounting_type";
   static Connection conn = null;
   
   public GetAllAccountTypesLambda()
   {
      
      try {
         Class.forName("com.mysql.jdbc.Driver");  
         conn = DriverManager.getConnection(DB_URL, USER, PASS);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   public APIGatewayProxyResponseEvent getAllAccountTypes(APIGatewayProxyResponseEvent request)
         throws JsonProcessingException, ClassNotFoundException {
      
      AccountingType acc = new AccountingType();
      ObjectMapper objectMapper = new ObjectMapper();
      List<AccountingType> list = new ArrayList<>();


      try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(QUERY);) {
         // Extract data from result set

         while (rs.next()) {
            // Retrieve by column name
            acc.setIdaccountingType(rs.getInt("idaccounting_Type"));
            acc.setType(rs.getString("type"));

            list.add(acc);
         }
      } catch (SQLException e) {
         e.printStackTrace();
      }

      String writeValueAsString = objectMapper.writeValueAsString(list);
      return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody(writeValueAsString);
   }

}

My pom file

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.aaa</groupId>
    <artifactId>aaa-restapi</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <name>aaa REST API</name>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-events</artifactId>
            <version>3.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <configuration></configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Now, checkout the loading time tracked from X Ray, which we usually call as cold start

enter image description here

It looks like the init process takes time. When further observed, I noticed most of the time is taken by the MySQL Connector trying to initialize. If we remove that, this will be done in 500ms to 600ms.

How can I make sure this starts much faster, probably in milliseconds? Anything to do with the MySQL connector?

Answer

Don’t optimize blindly for cold starts. If your Lambda is going to be used in an application that is used regularly (e.g. more than 1 request per 10 seconds), the cold starts could be only 0.1% of the calls, while the other 99.9% will be processed by a warmed up lambda.

If your application is not used so much, you could still force this by e.g. sending a ping to the Lambda every 5 minutes, forcing it to keep warm.

See Is it possible to keep an AWS Lambda function warm? for one way to do it. A Cloud Guru did a more extended version of it (for a javascript Lambda): https://acloudguru.com/blog/engineering/how-to-keep-your-lambda-functions-warm

The pinging does increase Amazon’s overhead cost for a low-use Lambda, since they have to reserve memory for it for X hours, and they can only charge you for a couple of seconds of actual runtime for each ping. But apparently, that’s a cost they are willing to take.