55import static software .amazon .lambda .durable .execution .ExecutionManager .isTerminalStatus ;
66
77import java .nio .charset .StandardCharsets ;
8+ import java .util .concurrent .atomic .AtomicBoolean ;
89import java .util .function .Function ;
910import software .amazon .awssdk .services .lambda .model .ContextOptions ;
1011import software .amazon .awssdk .services .lambda .model .ErrorObject ;
@@ -46,7 +47,7 @@ public class ChildContextOperation<T> extends SerializableDurableOperation<T> {
4647
4748 private final Function <DurableContext , T > function ;
4849 private final ConcurrencyOperation <?> parentOperation ;
49- private boolean replayChildContext ;
50+ private final AtomicBoolean replayChildren = new AtomicBoolean ( false ) ;
5051 private T reconstructedResult ;
5152
5253 public ChildContextOperation (
@@ -86,7 +87,7 @@ protected void replay(Operation existing) {
8687 if (existing .contextDetails () != null
8788 && Boolean .TRUE .equals (existing .contextDetails ().replayChildren ())) {
8889 // Large result: re-execute child context to reconstruct result
89- replayChildContext = true ;
90+ replayChildren . set ( true ) ;
9091 executeChildContext ();
9192 } else {
9293 markAlreadyCompleted ();
@@ -100,11 +101,6 @@ protected void replay(Operation existing) {
100101 }
101102 }
102103
103- @ Override
104- protected void markAlreadyCompleted () {
105- super .markAlreadyCompleted ();
106- }
107-
108104 private void executeChildContext () {
109105 // The operationId is already globally unique (prefixed by parent context path via
110106 // DurableContext.nextOperationId), so we use it directly as the contextId.
@@ -137,7 +133,7 @@ private void executeChildContext() {
137133 }
138134
139135 private void handleChildContextSuccess (T result ) {
140- if (replayChildContext ) {
136+ if (replayChildren . get () ) {
141137 // Replaying a SUCCEEDED child with replayChildren=true — skip checkpointing.
142138 // Mark the completableFuture completed so get() doesn't block waiting for a checkpoint response.
143139 this .reconstructedResult = result ;
@@ -151,8 +147,6 @@ private void checkpointSuccess(T result) {
151147 // Skip checkpointing if parent ConcurrencyOperation has already completed —
152148 // prevents race conditions where a child finishes after the parent has already completed.
153149 if (parentOperation != null && parentOperation .isOperationCompleted ()) {
154- this .reconstructedResult = result ;
155- markAlreadyCompleted ();
156150 return ;
157151 }
158152
@@ -187,7 +181,6 @@ private void handleChildContextFailure(Throwable exception) {
187181 // Skip checkpointing if parent ConcurrencyOperation has already completed —
188182 // prevents race conditions where a child finishes after the parent has already succeeded.
189183 if (parentOperation != null && parentOperation .isOperationCompleted ()) {
190- markAlreadyCompleted ();
191184 return ;
192185 }
193186
0 commit comments