3737import java .util .concurrent .ExecutorService ;
3838import java .util .concurrent .Executors ;
3939import java .util .concurrent .TimeUnit ;
40+ import java .util .concurrent .locks .Lock ;
41+ import java .util .concurrent .locks .ReentrantLock ;
4042import java .util .function .Consumer ;
4143
4244import org .opensearch .dataprepper .plugins .source .dynamodb .model .ShardCheckpointStatus ;
@@ -66,6 +68,7 @@ public class ShardAcknowledgementManager {
6668 private boolean shutdownTriggered ;
6769
6870 private Instant lastCheckpointTime ;
71+ private Lock lock ;
6972
7073 ShardAcknowledgementManager (final AcknowledgementSetManager acknowledgementSetManager ,
7174 final EnhancedSourceCoordinator sourceCoordinator ,
@@ -79,6 +82,7 @@ public class ShardAcknowledgementManager {
7982 this .partitionsToRemove = Collections .synchronizedList (new ArrayList <>());
8083 this .partitionsToGiveUp = Collections .synchronizedList (new ArrayList <>());
8184 this .lastCheckpointTime = Instant .now ();
85+ this .lock = new ReentrantLock ();
8286
8387 executorService .submit (() -> monitorAcknowledgments (stopWorkerConsumer ));
8488 }
@@ -95,6 +99,7 @@ public class ShardAcknowledgementManager {
9599 this .partitionsToRemove = Collections .synchronizedList (new ArrayList <>());
96100 this .partitionsToGiveUp = Collections .synchronizedList (new ArrayList <>());
97101 this .lastCheckpointTime = Instant .now ();
102+ this .lock = new ReentrantLock ();
98103 }
99104
100105 void monitorAcknowledgments (final Consumer <StreamPartition > stopWorkerConsumer ) {
@@ -166,14 +171,21 @@ boolean runMonitorAcknowledgmentLoop(final Consumer<StreamPartition> stopWorkerC
166171
167172 if (latestCheckpointForShard .isFinalAcknowledgmentForPartition ()) {
168173 handleCompletedShard (streamPartition );
169- } else {
174+ } else if (! partitionsToRemove . contains ( streamPartition )) {
170175 streamProgressState .setSequenceNumber (Objects .equals (latestCheckpointForShard .getSequenceNumber (), NULL_SEQUENCE_NUMBER ) ? null : latestCheckpointForShard .getSequenceNumber ());
171- sourceCoordinator .saveProgressStateForPartition (streamPartition , dynamoDBSourceConfig .getShardAcknowledgmentTimeout ());
176+ try {
177+ sourceCoordinator .saveProgressStateForPartition (streamPartition , dynamoDBSourceConfig .getShardAcknowledgmentTimeout ());
178+ } catch (final PartitionUpdateException e ) {
179+ LOG .warn ("Failed to checkpoint shard {}, stop processing shard. This shard will be processed by another worker." , streamPartition .getPartitionKey ());
180+ partitionsToRemove .add (streamPartition );
181+ }
172182 LOG .debug ("Checkpointed shard {} with latest sequence number acknowledged {}" , streamPartition .getShardId (), latestCheckpointForShard .getSequenceNumber ());
173183 }
174184 } catch (final Exception e ) {
175185 LOG .error (NOISY , "Received exception while monitoring acknowledgments for stream partition {}, stop processing shard" , streamPartition .getPartitionKey (), e );
176- markPartitionForRemoval (streamPartition );
186+ if (!partitionsToRemove .contains (streamPartition )) {
187+ partitionsToRemove .add (streamPartition );
188+ }
177189 }
178190 }
179191
@@ -190,11 +202,16 @@ public AcknowledgementSet createAcknowledgmentSet(
190202 // Shard should already be in checkpoints map from call to startUpdatingOwnershipForShard, if it is not in the map
191203 // that means that ShardAcknowledgmentManager stopped tracking it due to some error, and another worker will pick it up
192204 // We throw an error in this case to have the ShardConsumer exit and stop reading data from the shard
193- ConcurrentLinkedQueue <ShardCheckpointStatus > queue = checkpoints .get (streamPartition );
194- if (queue == null ) {
195- throw new ShardNotTrackedException ("The shard {} is not being tracked anymore, stop reading from shard" );
205+ lock .lock ();
206+ try {
207+ ConcurrentLinkedQueue <ShardCheckpointStatus > queue = checkpoints .get (streamPartition );
208+ if (queue == null ) {
209+ throw new ShardNotTrackedException ("The shard {} is not being tracked anymore, stop reading from shard" );
210+ }
211+ queue .add (shardCheckpointStatus );
212+ } finally {
213+ lock .unlock ();
196214 }
197- queue .add (shardCheckpointStatus );
198215
199216 ackStatuses .computeIfAbsent (streamPartition , segment -> new ConcurrentHashMap <>());
200217 ackStatuses .get (streamPartition ).put (sequenceNumberNoNull , shardCheckpointStatus );
@@ -210,7 +227,7 @@ public AcknowledgementSet createAcknowledgmentSet(
210227 streamPartition .getPartitionKey (), sequenceNumberNoNull );
211228 ackCheckpointStatus .setAcknowledged (ShardCheckpointStatus .AcknowledgmentStatus .POSITIVE_ACK );
212229 } else {
213- LOG .warn ( "Negative acknowledgment received for partition {} with sequence number {}" ,
230+ LOG .debug ( NOISY , "Negative acknowledgment received for partition {} with sequence number {}" ,
214231 streamPartition .getPartitionKey (), sequenceNumberNoNull );
215232 ackCheckpointStatus .setAcknowledged (ShardCheckpointStatus .AcknowledgmentStatus .NEGATIVE_ACK );
216233 }
@@ -227,7 +244,7 @@ void updateOwnershipForAllShardPartitions() {
227244 sourceCoordinator .saveProgressStateForPartition (streamPartition , dynamoDBSourceConfig .getShardAcknowledgmentTimeout ());
228245 } catch (final PartitionUpdateException e ) {
229246 LOG .warn (NOISY , "Failed to update progress state for shard {}, will stop tracking this shard as someone else owns it" , streamPartition .getShardId ());
230- markPartitionForRemoval (streamPartition );
247+ partitionsToRemove . add (streamPartition );
231248 }
232249 }
233250 }
@@ -268,15 +285,27 @@ public void shutdown() {
268285 }
269286
270287 private void removePartitions () {
271- partitionsToRemove .forEach (streamPartition -> {
272- checkpoints .remove (streamPartition );
273- ackStatuses .remove (streamPartition );
274- });
275-
276- partitionsToGiveUp .forEach (sourceCoordinator ::giveUpPartition );
288+ lock .lock ();
289+ try {
290+ partitionsToRemove .forEach (streamPartition -> {
291+ checkpoints .remove (streamPartition );
292+ ackStatuses .remove (streamPartition );
293+ });
294+
295+ partitionsToGiveUp .forEach (partition -> {
296+ try {
297+ sourceCoordinator .giveUpPartition (partition );
298+ LOG .info ("Gave up partition for shard {}" , partition .getShardId ());
299+ } catch (final PartitionUpdateException e ) {
300+ LOG .warn ("Received exception giving up shard {}, this shard will be reprocessed once the ownership timeout expires." , partition .getShardId ());
301+ }
302+ });
277303
278- partitionsToRemove .clear ();
279- partitionsToGiveUp .clear ();
304+ partitionsToRemove .clear ();
305+ partitionsToGiveUp .clear ();
306+ } finally {
307+ lock .unlock ();
308+ }
280309 }
281310
282311 public void giveUpPartition (final StreamPartition streamPartition ) {
0 commit comments