Skip to content

Commit 6780ed0

Browse files
committed
fix anyOf with empty map
1 parent 7678056 commit 6780ed0

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,4 +928,35 @@ void testMultipleMapAsyncInParallel() {
928928
assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
929929
assertEquals("2,4,6|A,B|olleh,dlrow,oof,rab", result.getResult(String.class));
930930
}
931+
932+
@Test
933+
void testMapWithEmptyItems() {
934+
var runner = LocalDurableTestRunner.create(String.class, (input, context) -> {
935+
List<String> items = List.of();
936+
var result = context.map("empty-map", items, String.class, (item, index, ctx) -> item);
937+
938+
assertTrue(result.allSucceeded());
939+
assertEquals(0, result.size());
940+
assertTrue(result.results().isEmpty());
941+
return "done";
942+
});
943+
944+
var result = runner.runUntilComplete("test");
945+
assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
946+
}
947+
948+
@Test
949+
void testAnyOfMapWithEmptyItems() {
950+
var runner = LocalDurableTestRunner.create(String.class, (input, context) -> {
951+
List<String> items = List.of();
952+
var result = context.mapAsync("empty-map", items, String.class, (item, index, ctx) -> item);
953+
954+
DurableFuture.anyOf(result);
955+
956+
return "done";
957+
});
958+
959+
var result = runner.runUntilComplete("test");
960+
assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
961+
}
931962
}

sdk/src/main/java/software/amazon/lambda/durable/context/DurableContextImpl.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import software.amazon.lambda.durable.operation.StepOperation;
4444
import software.amazon.lambda.durable.operation.WaitForConditionOperation;
4545
import software.amazon.lambda.durable.operation.WaitOperation;
46-
import software.amazon.lambda.durable.util.CompletedDurableFuture;
4746
import software.amazon.lambda.durable.util.ParameterValidator;
4847

4948
/**
@@ -256,11 +255,6 @@ public <I, O> DurableFuture<MapResult<O>> mapAsync(
256255
config = config.toBuilder().serDes(getDurableConfig().getSerDes()).build();
257256
}
258257

259-
// Short-circuit for empty collections — no checkpoint overhead
260-
if (items.isEmpty()) {
261-
return new CompletedDurableFuture<>(MapResult.empty());
262-
}
263-
264258
// Convert to List for deterministic index-based access
265259
var itemList = List.copyOf(items);
266260
var operationId = nextOperationId();

sdk/src/main/java/software/amazon/lambda/durable/operation/MapOperation.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ private static Integer getToleratedFailureCount(CompletionConfig completionConfi
111111

112112
@Override
113113
protected void start() {
114+
if (items.isEmpty()) {
115+
markAlreadyCompleted();
116+
return;
117+
}
114118
sendOperationUpdateAsync(OperationUpdate.builder()
115119
.action(OperationAction.START)
116120
.subType(getSubType().getValue()));
@@ -120,6 +124,10 @@ protected void start() {
120124

121125
@Override
122126
protected void replay(Operation existing) {
127+
if (items.isEmpty()) {
128+
markAlreadyCompleted();
129+
return;
130+
}
123131
switch (existing.status()) {
124132
case SUCCEEDED -> {
125133
if (existing.contextDetails() != null
@@ -194,6 +202,9 @@ protected void handleCompletion(ConcurrencyCompletionStatus concurrencyCompletio
194202

195203
@Override
196204
public MapResult<O> get() {
205+
if (items.isEmpty()) {
206+
return MapResult.empty();
207+
}
197208
if (replayFromPayload) {
198209
// Small result replay: deserialize MapResult directly from checkpoint payload
199210
var op = waitForOperationCompletion();

0 commit comments

Comments
 (0)