Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@
package software.amazon.lambda.durable;

import com.amazonaws.services.lambda.runtime.Context;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.HexFormat;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.lambda.model.OperationType;
import software.amazon.lambda.durable.execution.ExecutionManager;
import software.amazon.lambda.durable.execution.OperationIdGenerator;
import software.amazon.lambda.durable.execution.ThreadType;
import software.amazon.lambda.durable.logging.DurableLogger;
import software.amazon.lambda.durable.model.OperationIdentifier;
Expand All @@ -32,7 +28,7 @@ public class DurableContext extends BaseContext {
private static final String WAIT_FOR_CALLBACK_SUBMITTER_SUFFIX = "-submitter";
private static final int MAX_WAIT_FOR_CALLBACK_NAME_LENGTH = ParameterValidator.MAX_OPERATION_NAME_LENGTH
- Math.max(WAIT_FOR_CALLBACK_CALLBACK_SUFFIX.length(), WAIT_FOR_CALLBACK_SUBMITTER_SUFFIX.length());
private final AtomicInteger operationCounter;
private final OperationIdGenerator operationIdGenerator;
private volatile DurableLogger logger;

/** Shared initialization — sets all fields. */
Expand All @@ -43,7 +39,7 @@ private DurableContext(
String contextId,
String contextName) {
super(executionManager, durableConfig, lambdaContext, contextId, contextName, ThreadType.CONTEXT);
this.operationCounter = new AtomicInteger(0);
operationIdGenerator = new OperationIdGenerator(contextId);
}

/**
Expand Down Expand Up @@ -490,14 +486,6 @@ public void close() {
* matches the Python SDK's stepPrefix convention and prevents ID collisions in checkpoint batches.
*/
private String nextOperationId() {
var counter = String.valueOf(operationCounter.incrementAndGet());
var rawId = getContextId() != null ? getContextId() + "-" + counter : counter;
try {
var messageDigest = MessageDigest.getInstance("SHA-256");
var hash = messageDigest.digest(rawId.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("failed to get next operation id, SHA-256 not available", e);
}
return operationIdGenerator.nextOperationId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.lambda.durable.execution;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.concurrent.atomic.AtomicInteger;

/** Generates operation IDs for the durable operations. */
public class OperationIdGenerator {
private final AtomicInteger operationCounter;
private final String contextId;

public OperationIdGenerator(String contextId) {
this.operationCounter = new AtomicInteger(0);
this.contextId = contextId;
}

/**
* Get the next operationId. Returns a globally unique operation ID by hashing a sequential operation counter. For
* root contexts, the counter value is hashed directly (e.g. "1", "2", "3"). For child contexts, the values are
* prefixed with the parent hashed contextId (e.g. "<hash>-1", "<hash>-2" inside parent context <hash>). This
* matches the Python SDK's stepPrefix convention and prevents ID collisions in checkpoint batches.
*/
public String nextOperationId() {
var counter = String.valueOf(operationCounter.incrementAndGet());
var rawId = contextId != null ? contextId + "-" + counter : counter;
try {
var messageDigest = MessageDigest.getInstance("SHA-256");
var hash = messageDigest.digest(rawId.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("failed to get next operation id, SHA-256 not available", e);
}
}
}
Loading