55import com .amazonaws .lambda .durable .client .DurableExecutionClient ;
66import java .util .ArrayList ;
77import java .util .List ;
8+ import java .util .Objects ;
89import java .util .concurrent .BlockingQueue ;
910import java .util .concurrent .CompletableFuture ;
1011import java .util .concurrent .LinkedBlockingQueue ;
1112import java .util .concurrent .atomic .AtomicBoolean ;
12- import java .util .function .Supplier ;
1313import org .slf4j .Logger ;
1414import org .slf4j .LoggerFactory ;
15+ import software .amazon .awssdk .services .lambda .model .Operation ;
1516import software .amazon .awssdk .services .lambda .model .OperationUpdate ;
1617
1718/**
@@ -30,31 +31,31 @@ class CheckpointBatcher {
3031 private static final Logger logger = LoggerFactory .getLogger (CheckpointBatcher .class );
3132
3233 private final CheckpointCallback callback ;
33- private final Supplier <String > tokenSupplier ;
3434 private final String durableExecutionArn ;
3535 private final DurableExecutionClient client ;
3636 private final BlockingQueue <CheckpointRequest > queue = new LinkedBlockingQueue <>();
3737 private final AtomicBoolean isProcessing = new AtomicBoolean (false );
38+ private String checkpointToken ;
3839
3940 record CheckpointRequest (OperationUpdate update , CompletableFuture <Void > completion ) {}
4041
4142 CheckpointBatcher (
4243 DurableExecutionClient client ,
4344 String durableExecutionArn ,
44- Supplier < String > tokenSupplier ,
45+ String checkpointToken ,
4546 CheckpointCallback callback ) {
4647 this .client = client ;
4748 this .durableExecutionArn = durableExecutionArn ;
48- this .tokenSupplier = tokenSupplier ;
4949 this .callback = callback ;
50+ this .checkpointToken = checkpointToken ;
5051 }
5152
5253 CompletableFuture <Void > checkpoint (OperationUpdate update ) {
5354 logger .debug (
5455 "Checkpoint request received: Action {}" ,
5556 update != null ? update .action () : "NULL (Checkpoint request)" );
5657 var future = new CompletableFuture <Void >();
57- queue .offer (new CheckpointRequest (update , future ));
58+ queue .add (new CheckpointRequest (update , future ));
5859
5960 if (isProcessing .compareAndSet (false , true )) {
6061 InternalExecutor .INSTANCE .execute (this ::processQueue );
@@ -70,32 +71,46 @@ void shutdown() {
7071 req -> req .completion ().completeExceptionally (new IllegalStateException ("CheckpointManager shutdown" )));
7172 }
7273
74+ public List <Operation > fetchAllPages (List <Operation > initialOperations , String nextMarker ) {
75+ List <Operation > operations = new ArrayList <>();
76+ if (initialOperations != null ) {
77+ operations .addAll (initialOperations );
78+ }
79+ while (nextMarker != null && !nextMarker .isEmpty ()) {
80+ var response = client .getExecutionState (durableExecutionArn , checkpointToken , nextMarker );
81+ logger .debug ("DAR getExecutionState called: {}." , response );
82+ operations .addAll (response .operations ());
83+ nextMarker = response .nextMarker ();
84+ }
85+ return operations ;
86+ }
87+
7388 private void processQueue () {
7489 try {
7590 var batch = collectBatch ();
7691 if (!batch .isEmpty ()) {
7792 // Filter out null updates (empty checkpoints for polling)
7893 var updates = batch .stream ()
7994 .map (CheckpointRequest ::update )
80- .filter (u -> u != null )
95+ .filter (Objects :: nonNull )
8196 .toList ();
8297
83- var response = client .checkpoint (durableExecutionArn , tokenSupplier . get () , updates );
84- logger .debug ("DAR backend called: {}." , response );
98+ var response = client .checkpoint (durableExecutionArn , checkpointToken , updates );
99+ logger .debug ("DAR checkpointDurableExecution called: {}." , response );
85100
86101 // Notify callback of completion
87102 // TODO: sam local backend returns no new execution state when called with zero
88103 // updates. WHY?
89104 // This means the polling will never receive an operation update and complete
90105 // the Phaser.
91- if ( response .newExecutionState () != null
92- && response .newExecutionState (). operations () != null
93- && ! response . newExecutionState (). operations (). isEmpty ()) {
94- callback . onComplete (
95- response .checkpointToken (),
96- response . newExecutionState (). operations ());
97- } else {
98- callback . onComplete ( response . checkpointToken (), List . of ());
106+ checkpointToken = response .checkpointToken ();
107+ if ( response .newExecutionState () != null ) {
108+ var operations = fetchAllPages (
109+ response . newExecutionState (). operations (),
110+ response .newExecutionState (). nextMarker ());
111+ if (! operations . isEmpty ()) {
112+ callback . onComplete ( operations );
113+ }
99114 }
100115
101116 batch .forEach (req -> req .completion ().complete (null ));
@@ -122,7 +137,7 @@ private List<CheckpointRequest> collectBatch() {
122137 var itemSize = estimateSize (req .update ());
123138
124139 if (currentSize + itemSize > MAX_BATCH_SIZE_BYTES && !batch .isEmpty ()) {
125- queue .offer (req );
140+ queue .add (req );
126141 break ;
127142 }
128143
0 commit comments