-
Notifications
You must be signed in to change notification settings - Fork 8
Home
Welcome to the aws-lambda-snapstart-java-rules wiki!
Lambda SnapStart speeds up applications by re-using a single initialized snapshot to resume multiple execution environments. As a result, unique content included in the snapshot during initialization is reused across execution environments, and so may no longer remain unique. A class of applications where uniqueness of state is a key consideration is cryptographic software, which assumes that the random numbers are truly random (both random and unpredictable). If content such as a random seed is saved in the snapshot during initialization, it is re-used when multiple execution environments resume and may produce predictable random sequences.
To maintain uniqueness, you must verify before using SnapStart that any unique content previously generated during the initialization now gets generated after that initialization. This includes unique IDs, unique secrets, and entropy used to generate pseudo-randomness.
These are some examples that may lead to scenarios where your code does not maintain uniqueness with SnapStart.
Lambda functions that require pseudo-random values can initialize a pseudo-random number generator as below. Lambda SnapStart functions will snapshot the java.util.Random
instance which will cause exactly the same sequence of random numbers to be generated in each execution environment that runs this SnapStart function.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.Random;
public class LambdaWithRandom implements RequestHandler<String, String> {
private final Random random;
public LambdaUsingRandom() {
// This is snapshotted by SnapStart, so it's a bug
random = new Random();
}
@Override
public String handleRequest(String event, Context context) {
// use random to generate a random number here.
return "hello world";
}
}
If this creates an issue for your Lambda application, you can make sure that each execution creates its own sequence of random numbers by using java.security.SecureRandom
instead of java.util.Random
as shown below.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.security.SecureRandom;
public class LambdaWithRandom implements RequestHandler<String, String> {
private final SecureRandom random;
public LambdaUsingRandom() {
random = new SecureRandom();
}
@Override
public String handleRequest(String event, Context context) {
// use random to generate a random number here.
return "hello world";
}
}
A common use case for Lambda functions is to create a unique identifier that’s used by multiple invocations in the same execution environment such as a CloudWatch log stream name. Since SnapStart execution environments can start from the same snapshot, a random value that initializes a Lambda handler class member gets copied to multiple execution environments.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.UUID;
public class LambdaLogger implements RequestHandler<String, String> {
private UUID uniqueLogId;
public LambdaUsingRandom() {
// This is snapshotted by SnapStart, so it's a bug
uniqueLogId = UUID.randomUUID();
}
@Override
public String handleRequest(String event, Context context) {
// get or create a cloudwatch log stream with uniqueLogId
return "hello world";
}
}
In order to avoid these bugs we recommend writing your Lambda function’s code like below:
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.UUID;
public class LambdaLogger implements RequestHandler<String, String> {
private UUID uniqueLogId;
@Override
public String handleRequest(String event, Context context) {
if (uniqueLogId == null) {
uniqueLogId = UUID.randomUUID();
}
// get or create a cloudwatch log stream with uniqueLogId
return "hello world";
}
}
Lambda functions may capture execution environment creation time for various purposes. In this case the time value that initializes a Lambda function’s class member will have the time when snapshot is created.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class LambdaUsingTimestamp implements RequestHandler<String, String> {
private long envCreationTime;
public LambdaUsingUUID() {
// This is snapshotted by SnapStart, so it's a bug
envCreationTime = System.currentTimeMillis();
}
@Override
public String handleRequest(String event, Context context) {
// use envCreationTime to calculate the time difference
return "hello world";
}
}
You can eliminate this timing error by initializing the timestamp during snapshot resume as below.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import org.crac.Resource;
public class LambdaUsingTimestamp implements RequestHandler<String, String>, Resource {
private long envCreationTime;
@Override
public void afterRestore(org.crac.Context<? extends Resource> context) {
envCreationTime = System.currentTimeMillis();
}
@Override
public String handleRequest(String event, Context context) {
// use envCreationTime to calculate the time difference
return "hello world";
}
}