All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.serverless.proxy.AsyncInitializationWrapper Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
package com.amazonaws.serverless.proxy;

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * An async implementation of the InitializationWrapper interface. This initializer calls the
 * {@link LambdaContainerHandler#initialize()} in a separate thread. Then uses a latch to wait for the maximum Lambda
 * initialization time of 10 seconds, if the initialize method takes longer than 10 seconds to return, the
 * {@link #start(LambdaContainerHandler)} returns control to the caller and lets the initialization thread continue in
 * the background. The {@link LambdaContainerHandler#proxy(Object, Context)} automatically waits for the latch of the
 * initializer to be released.
 *
 * The constructor of this class expects an epoch long. This is meant to be as close as possible to the time the Lambda
 * function actually started. In most cases, the first action in the constructor of the handler class should be to populate
 * this long value ({@code Instant.now().toEpochMs();}). This class uses the value to estimate how much of the init 10
 * seconds has already been used up.
 */
public class AsyncInitializationWrapper extends InitializationWrapper {
    private static final int INIT_GRACE_TIME_MS = 250;
    private static final int LAMBDA_MAX_INIT_TIME_MS = 10_000;

    private CountDownLatch initializationLatch;
    private long actualStartTime;
    private Logger log = LoggerFactory.getLogger(AsyncInitializationWrapper.class);

    /**
     * Creates a new instance of the async initializer.
     * @param startTime The epoch ms start time of the Lambda function, this should be measured as close as possible to
     *                  the initialization of the function.
     */
    public AsyncInitializationWrapper(long startTime) {
        actualStartTime = startTime;
    }

    @Override
    public void start(LambdaContainerHandler handler) throws ContainerInitializationException {
        initializationLatch = new CountDownLatch(1);
        AsyncInitializer initializer = new AsyncInitializer(initializationLatch, handler);
        Thread initThread = new Thread(initializer);
        initThread.start();
        try {
            long curTime = Instant.now().toEpochMilli();
            // account for the time it took to call the various constructors with the actual start time + a grace of 500ms
            long awaitTime = LAMBDA_MAX_INIT_TIME_MS - (curTime - actualStartTime) - INIT_GRACE_TIME_MS;
            log.info("Async initialization will wait for " + awaitTime + "ms");
            if (!initializationLatch.await(awaitTime, TimeUnit.MILLISECONDS)) {
                log.info("Initialization took longer than " + LAMBDA_MAX_INIT_TIME_MS + ", setting new CountDownLatch and " +
                        "continuing in event handler");
                initializationLatch = new CountDownLatch(1);
                initializer.replaceLatch(initializationLatch);
            }
        } catch (InterruptedException e) {
            // at the moment we assume that this happened because of a timeout since the init thread calls System.exit
            // when an exception is thrown.
            throw new ContainerInitializationException("Container initialization interrupted", e);
        }
    }

    @Override
    public CountDownLatch getInitializationLatch() {
        return initializationLatch;
    }

    private static class AsyncInitializer implements Runnable {
        private LambdaContainerHandler handler;
        private CountDownLatch initLatch;
        private Logger log = LoggerFactory.getLogger(AsyncInitializationWrapper.class);

        AsyncInitializer(CountDownLatch latch, LambdaContainerHandler h) {
            initLatch = latch;
            handler = h;
        }

        synchronized void replaceLatch(CountDownLatch newLatch) {
            initLatch = newLatch;
        }

        @Override
        @SuppressFBWarnings("DM_EXIT")
        public void run() {
            log.info("Starting async initializer");
            try {
                handler.initialize();
            } catch (ContainerInitializationException e) {
                log.error("Failed to initialize container handler", e);
                // we cannot return the exception so we crash the whole kaboodle here
                System.exit(1);
            }
            synchronized(this) {
                initLatch.countDown();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy