Skip to content

Commit 665903e

Browse files
committed
Change operation IDs to hashed values
1 parent 2677372 commit 665903e

6 files changed

Lines changed: 49 additions & 20 deletions

File tree

sdk-integration-tests/src/test/java/software/amazon/lambda/durable/IntegrationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ void testFullWaitOperation() {
110110

111111
assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
112112
assertEquals(3, result.getSucceededOperations().size());
113-
assertEquals("Step 1 done", result.getSucceededOperations().get(0).getStepResult(String.class));
113+
assertEquals("Step 1 done", result.getOperation("step1").getStepResult(String.class));
114114
assertEquals(OperationType.WAIT, result.getSucceededOperations().get(1).getType());
115115
assertEquals(
116116
OperationStatus.SUCCEEDED,
117117
result.getSucceededOperations().get(1).getStatus());
118-
assertEquals("Step 2 done", result.getSucceededOperations().get(2).getStepResult(String.class));
118+
assertEquals("Step 2 done", result.getOperation("step2").getStepResult(String.class));
119119
assertEquals("Step 1 done + Step 2 done", result.getResult(TestOutput.class).result);
120120
}
121121

sdk/src/main/java/software/amazon/lambda/durable/DurableContext.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package software.amazon.lambda.durable;
44

5-
import com.amazonaws.services.lambda.runtime.Context;
5+
import java.nio.charset.StandardCharsets;
6+
import java.security.MessageDigest;
7+
import java.security.NoSuchAlgorithmException;
68
import java.time.Duration;
9+
import java.util.HexFormat;
710
import java.util.Objects;
811
import java.util.concurrent.atomic.AtomicInteger;
912
import java.util.function.Function;
1013
import java.util.function.Supplier;
14+
1115
import org.slf4j.LoggerFactory;
16+
17+
import com.amazonaws.services.lambda.runtime.Context;
18+
1219
import software.amazon.lambda.durable.execution.ExecutionManager;
1320
import software.amazon.lambda.durable.logging.DurableLogger;
1421
import software.amazon.lambda.durable.operation.CallbackOperation;
@@ -327,12 +334,20 @@ public void close() {
327334
}
328335

329336
/**
330-
* Get the next operationId. For root contexts, returns sequential IDs like "1", "2", "3". For child contexts,
331-
* prefixes with the contextId to ensure global uniqueness, e.g. "1-1", "1-2" for operations inside child context
332-
* "1". This matches the JavaScript SDK's stepPrefix convention and prevents ID collisions in checkpoint batches.
337+
* Get the next operationId. Returns a globally unique operation ID by hashing a sequential operation counter. For
338+
* root contexts, the counter value is hashed directly (e.g. "1", "2", "3"). For child contexts, the values are
339+
* prefixed with the parent hashed contextId (e.g. "<hash>-1", "<hash>-2" inside parent context <hash>). This
340+
* matches the JavaScript SDK's stepPrefix convention and prevents ID collisions in checkpoint batches.
333341
*/
334342
private String nextOperationId() {
335343
var counter = String.valueOf(operationCounter.incrementAndGet());
336-
return getContextId() != null ? getContextId() + "-" + counter : counter;
344+
var rawId = getContextId() != null ? getContextId() + "-" + counter : counter;
345+
try {
346+
var messageDigest = MessageDigest.getInstance("SHA-256");
347+
var hash = messageDigest.digest(rawId.getBytes(StandardCharsets.UTF_8));
348+
return HexFormat.of().formatHex(hash);
349+
} catch (NoSuchAlgorithmException e) {
350+
throw new RuntimeException("failed to get next operation id, SHA-256 not available", e);
351+
}
337352
}
338353
}

sdk/src/test/java/software/amazon/lambda/durable/DurableContextTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ class DurableContextTest {
2424
.type(OperationType.EXECUTION)
2525
.status(OperationStatus.STARTED)
2626
.build();
27-
private static final String OPERATION_ID1 = "1";
28-
private static final String OPERATION_ID2 = "2";
29-
private static final String OPERATION_ID3 = "3";
27+
private static final String OPERATION_ID1 = TestUtils.hashOperationId("1");
28+
private static final String OPERATION_ID2 = TestUtils.hashOperationId("2");
29+
private static final String OPERATION_ID3 = TestUtils.hashOperationId("3");
3030

3131
private DurableContext createTestContext() {
3232
return createTestContext(List.of());

sdk/src/test/java/software/amazon/lambda/durable/DurableExecutionTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private DurableConfig configWithMockClient() {
3232
@Test
3333
void testExecuteSuccess() {
3434
var executionOp = Operation.builder()
35-
.id("0")
35+
.id(TestUtils.hashOperationId("0"))
3636
.type(OperationType.EXECUTION)
3737
.status(OperationStatus.STARTED)
3838
.executionDetails(ExecutionDetails.builder()
@@ -62,7 +62,7 @@ void testExecuteSuccess() {
6262
@Test
6363
void testExecutePending() {
6464
var executionOp = Operation.builder()
65-
.id("0")
65+
.id(TestUtils.hashOperationId("0"))
6666
.type(OperationType.EXECUTION)
6767
.status(OperationStatus.STARTED)
6868
.executionDetails(ExecutionDetails.builder()
@@ -95,7 +95,7 @@ void testExecutePending() {
9595
@Test
9696
void testExecuteFailure() {
9797
var executionOp = Operation.builder()
98-
.id("0")
98+
.id(TestUtils.hashOperationId("0"))
9999
.type(OperationType.EXECUTION)
100100
.status(OperationStatus.STARTED)
101101
.executionDetails(ExecutionDetails.builder()
@@ -128,7 +128,7 @@ void testExecuteFailure() {
128128
@Test
129129
void testExecuteReplay() {
130130
var executionOp = Operation.builder()
131-
.id("0")
131+
.id(TestUtils.hashOperationId("0"))
132132
.type(OperationType.EXECUTION)
133133
.status(OperationStatus.STARTED)
134134
.executionDetails(ExecutionDetails.builder()
@@ -137,7 +137,7 @@ void testExecuteReplay() {
137137
.build();
138138

139139
var completedStep = Operation.builder()
140-
.id("1")
140+
.id(TestUtils.hashOperationId("1"))
141141
.name("step1")
142142
.type(OperationType.STEP)
143143
.status(OperationStatus.SUCCEEDED)
@@ -180,7 +180,7 @@ void testValidationNoOperations() {
180180
@Test
181181
void testValidationWrongFirstOperation() {
182182
var stepOp = Operation.builder()
183-
.id("1")
183+
.id(TestUtils.hashOperationId("1"))
184184
.type(OperationType.STEP)
185185
.status(OperationStatus.SUCCEEDED)
186186
.stepDetails(StepDetails.builder().result("\"result\"").build())
@@ -204,7 +204,7 @@ void testValidationWrongFirstOperation() {
204204
@Test
205205
void testValidationMissingExecutionDetails() {
206206
var executionOp = Operation.builder()
207-
.id("0")
207+
.id(TestUtils.hashOperationId("0"))
208208
.type(OperationType.EXECUTION)
209209
.status(OperationStatus.STARTED)
210210
.build();
@@ -234,7 +234,7 @@ void testExecutorNotShutdownAfterMultipleHandlerInvocations() {
234234
assertFalse(sharedExecutor.isShutdown(), "Executor should not be shutdown initially");
235235

236236
var executionOp = Operation.builder()
237-
.id("0")
237+
.id(TestUtils.hashOperationId("0"))
238238
.type(OperationType.EXECUTION)
239239
.status(OperationStatus.STARTED)
240240
.executionDetails(ExecutionDetails.builder()
@@ -262,7 +262,7 @@ void testExecutorNotShutdownAfterMultipleHandlerInvocations() {
262262

263263
// Create second input with different execution operation
264264
var executionOp2 = Operation.builder()
265-
.id("0")
265+
.id(TestUtils.hashOperationId("0"))
266266
.type(OperationType.EXECUTION)
267267
.status(OperationStatus.STARTED)
268268
.executionDetails(ExecutionDetails.builder()

sdk/src/test/java/software/amazon/lambda/durable/ReplayValidationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
class ReplayValidationTest {
2525
public static final String EXECUTION_NAME = "exec-name";
2626
public static final String INVOCATION_ID = "invocation-id";
27-
public static final String OPERATION_ID1 = "1";
27+
public static final String OPERATION_ID1 = TestUtils.hashOperationId("1");
2828

2929
private DurableContext createTestContext(List<Operation> initialOperations) {
3030
var client = TestUtils.createMockClient();

sdk/src/test/java/software/amazon/lambda/durable/TestUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
import static org.mockito.ArgumentMatchers.*;
66
import static org.mockito.Mockito.*;
77

8+
import java.nio.charset.StandardCharsets;
9+
import java.security.MessageDigest;
10+
import java.security.NoSuchAlgorithmException;
811
import java.util.ArrayList;
12+
import java.util.HexFormat;
913
import java.util.List;
1014
import java.util.UUID;
1115
import software.amazon.awssdk.services.lambda.model.*;
@@ -61,4 +65,14 @@ public static DurableExecutionClient createMockClient() {
6165
});
6266
return client;
6367
}
68+
69+
public static String hashOperationId(String rawId) {
70+
try {
71+
var messageDigest = MessageDigest.getInstance("SHA-256");
72+
var hash = messageDigest.digest(rawId.getBytes(StandardCharsets.UTF_8));
73+
return HexFormat.of().formatHex(hash);
74+
} catch (NoSuchAlgorithmException e) {
75+
throw new AssertionError("SHA-256 not available", e);
76+
}
77+
}
6478
}

0 commit comments

Comments
 (0)