Skip to content

Commit 1df82c3

Browse files
dcabibclaude
andcommitted
feat(idempotency): add automatic priming for DynamoDB persistence store
Implements automatic class preloading via CRaC hooks for the idempotency-dynamodb module to improve SnapStart cold start performance. Changes: - Add ClassPreLoader.preloadClasses() call to DynamoDBPersistenceStore.beforeCheckpoint() - Generate and include classesloaded.txt with 8,726 classes - Add powertools-common dependency for ClassPreLoader - Add Maven profile generate-classesloaded-file - Add unit tests for CRaC hooks - Update Priming.md with idempotency-dynamodb example - Add null check and logging for defensive error handling This combines both invoke priming (warming DynamoDB SDK paths) and automatic priming (preloading classes) for optimal performance. Follows the same pattern as implemented in powertools-metrics (#1861), powertools-tracing (#2345), and powertools-kafka (#2145). Fixes #1997 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 61fd80a commit 1df82c3

File tree

5 files changed

+8787
-3
lines changed

5 files changed

+8787
-3
lines changed

Priming.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,6 @@ In order to generate the `classloaded.txt` file for a Java module in this projec
5656
- `classesloaded.txt` file includes test classes as well because the file is generated while running tests. This is not a problem because all the classes that are not found are ignored by `ClassPreLoader.preloadClasses()`. Also `beforeCheckpoint()` hook is not time-sensitive, it only runs once when a new Lambda version gets published.
5757
5858
## Reference Implementation
59-
Working example is available in the [powertools-metrics](powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java).
59+
Working examples are available in:
60+
- [powertools-metrics](powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java) - Automatic priming with `ClassPreLoader`
61+
- [powertools-idempotency-dynamodb](powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java) - Invoke priming + Automatic priming

powertools-idempotency/powertools-idempotency-dynamodb/pom.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@
6565
<groupId>org.crac</groupId>
6666
<artifactId>crac</artifactId>
6767
</dependency>
68+
<dependency>
69+
<groupId>software.amazon.lambda</groupId>
70+
<artifactId>powertools-common</artifactId>
71+
</dependency>
6872

6973
<!-- Test dependencies -->
7074
<dependency>
@@ -106,6 +110,24 @@
106110
</dependencies>
107111

108112
<profiles>
113+
<profile>
114+
<id>generate-classesloaded-file</id>
115+
<build>
116+
<plugins>
117+
<plugin>
118+
<groupId>org.apache.maven.plugins</groupId>
119+
<artifactId>maven-surefire-plugin</artifactId>
120+
<configuration>
121+
<argLine>
122+
-Xlog:class+load=info:classesloaded.txt
123+
--add-opens java.base/java.util=ALL-UNNAMED
124+
--add-opens java.base/java.lang=ALL-UNNAMED
125+
</argLine>
126+
</configuration>
127+
</plugin>
128+
</plugins>
129+
</build>
130+
</profile>
109131
<profile>
110132
<id>generate-graalvm-files</id>
111133
<!-- The AWS DynamoDBClient uses com.amazonaws.xray.interceptors.TracingInterceptor if available on the

powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
3131
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
3232
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
33+
import software.amazon.lambda.powertools.common.internal.ClassPreLoader;
3334
import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator;
3435
import software.amazon.lambda.powertools.idempotency.Constants;
3536
import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException;
@@ -123,7 +124,13 @@ private DynamoDBPersistenceStore(String tableName,
123124
*/
124125
@Override
125126
public void beforeCheckpoint(org.crac.Context<? extends Resource> context) throws Exception {
127+
// Skip priming if DynamoDB client is not available
128+
if (dynamoDbClient == null) {
129+
return;
130+
}
131+
126132
try {
133+
// Invoke priming - exercise DynamoDB SDK paths
127134
String primingRecordKey = "__invoke_prime__";
128135
Instant now = Instant.now();
129136
long expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond();
@@ -137,8 +144,12 @@ public void beforeCheckpoint(org.crac.Context<? extends Resource> context) throw
137144
putRecord(primingDataRecord, Instant.now());
138145
getRecord(primingRecordKey);
139146
deleteRecord(primingRecordKey);
140-
} catch (Exception unknown) {
141-
// This is unexpected but we must continue without any interruption
147+
148+
// Automatic priming - preload classes from classesloaded.txt
149+
ClassPreLoader.preloadClasses();
150+
} catch (Exception e) {
151+
// Log the exception for debugging but continue to ensure checkpoint succeeds
152+
LOG.warn("Priming failed during checkpoint, continuing anyway", e);
142153
}
143154
}
144155

0 commit comments

Comments
 (0)