2525import it .unimi .dsi .fastutil .longs .Long2ObjectMap ;
2626import it .unimi .dsi .fastutil .longs .Long2ObjectRBTreeMap ;
2727import it .unimi .dsi .fastutil .longs .Long2ObjectSortedMap ;
28+ import it .unimi .dsi .fastutil .longs .LongLongBiConsumer ;
2829import it .unimi .dsi .fastutil .objects .ObjectBidirectionalIterator ;
2930import java .util .concurrent .locks .Lock ;
3031import java .util .concurrent .locks .ReadWriteLock ;
4344 * running.
4445 */
4546public class PendingAcksMap {
46- static final int PENDING_ACK_NOT_FOUND = -1 ;
47- private static final long STICKY_KEY_HASH_MASK = 0xFFFF_FFFFL ;
48- // The remaining unacked count is a non-negative message count. Reserve a packed negative count as the
49- // Long2LongSortedMap default return value so lookups can distinguish missing entries with a single get.
50- private static final long PACKED_PENDING_ACK_NOT_FOUND =
51- packPendingAckValue (PENDING_ACK_NOT_FOUND , 0 );
47+ static final int PENDING_ACK_NOT_FOUND = PendingAckValues .NOT_FOUND ;
5248
5349 /**
5450 * Callback interface for handling the addition of pending acknowledgments.
@@ -108,6 +104,11 @@ public interface PendingAcksConsumer {
108104 }
109105
110106 private final Consumer consumer ;
107+
108+ /**
109+ * Pending acknowledgments grouped by ledger id. The outer map key is ledgerId and each inner map key is entryId.
110+ * The inner map value is packed by {@link PendingAckValues} with the remaining unacked count and sticky key hash.
111+ */
111112 private final Long2ObjectSortedMap <Long2LongSortedMap > pendingAcks ;
112113 private final Supplier <PendingAcksAddHandler > pendingAcksAddHandlerSupplier ;
113114 private final Supplier <PendingAcksRemoveHandler > pendingAcksRemoveHandlerSupplier ;
@@ -141,7 +142,6 @@ public interface PendingAcksConsumer {
141142 * @return true if the pending ack was added, and it's allowed to send a message, false otherwise
142143 */
143144 public boolean addPendingAckIfAllowed (long ledgerId , long entryId , int remainingUnacked , int stickyKeyHash ) {
144- long packedValue = packPendingAckValue (remainingUnacked , stickyKeyHash );
145145 try {
146146 writeLock .lock ();
147147 // prevent adding sticky hash to pending acks if the PendingAcksMap has already been closed
@@ -158,8 +158,9 @@ public boolean addPendingAckIfAllowed(long ledgerId, long entryId, int remaining
158158 }
159159 Long2LongSortedMap ledgerPendingAcks =
160160 pendingAcks .computeIfAbsent (ledgerId , k -> newLedgerPendingAcks ());
161+ long packedValue = PendingAckValues .pack (remainingUnacked , stickyKeyHash );
161162 long previous = ledgerPendingAcks .put (entryId , packedValue );
162- if (previous == PACKED_PENDING_ACK_NOT_FOUND ) {
163+ if (PendingAckValues . isNotFound ( previous ) ) {
163164 size ++;
164165 }
165166 return true ;
@@ -193,18 +194,12 @@ public void forEach(PendingAcksConsumer processor) {
193194
194195 // iterate all pending acks and process them
195196 private void processPendingAcks (PendingAcksConsumer processor ) {
196- // this code uses for loops intentionally, don't refactor to use forEach
197- // iterate the outer map
198197 for (Long2ObjectMap .Entry <Long2LongSortedMap > entry : pendingAcks .long2ObjectEntrySet ()) {
199198 long ledgerId = entry .getLongKey ();
200- Long2LongSortedMap ledgerPendingAcks = entry .getValue ();
201- // iterate the inner map
202- for (Long2LongMap .Entry e : ledgerPendingAcks .long2LongEntrySet ()) {
203- long entryId = e .getLongKey ();
204- long packedValue = e .getLongValue ();
205- processor .accept (ledgerId , entryId , unpackRemainingUnacked (packedValue ),
206- unpackStickyKeyHash (packedValue ));
207- }
199+ LongLongBiConsumer pendingAckProcessor = (entryId , packedValue ) ->
200+ processor .accept (ledgerId , entryId , PendingAckValues .remainingUnacked (packedValue ),
201+ PendingAckValues .stickyKeyHash (packedValue ));
202+ entry .getValue ().forEach (pendingAckProcessor );
208203 }
209204 }
210205
@@ -294,7 +289,7 @@ public IntIntPair get(long ledgerId, long entryId) {
294289 return null ;
295290 }
296291 long packedValue = ledgerMap .get (entryId );
297- return packedValue == PACKED_PENDING_ACK_NOT_FOUND ? null : unpackPendingAckValue (packedValue );
292+ return PendingAckValues . isNotFound ( packedValue ) ? null : PendingAckValues . toPair (packedValue );
298293 } finally {
299294 readLock .unlock ();
300295 }
@@ -313,8 +308,8 @@ int getRemainingUnacked(long ledgerId, long entryId) {
313308 return PENDING_ACK_NOT_FOUND ;
314309 }
315310 long packedValue = ledgerMap .get (entryId );
316- return packedValue == PACKED_PENDING_ACK_NOT_FOUND
317- ? PENDING_ACK_NOT_FOUND : unpackRemainingUnacked (packedValue );
311+ return PendingAckValues . isNotFound ( packedValue )
312+ ? PENDING_ACK_NOT_FOUND : PendingAckValues . remainingUnacked (packedValue );
318313 } finally {
319314 readLock .unlock ();
320315 }
@@ -333,7 +328,10 @@ public boolean remove(long ledgerId, long entryId, int batchSize, int stickyKeyH
333328 try {
334329 writeLock .lock ();
335330 Long2LongSortedMap ledgerMap = pendingAcks .get (ledgerId );
336- long expectedValue = packPendingAckValue (batchSize , stickyKeyHash );
331+ if (batchSize < 0 ) {
332+ return false ;
333+ }
334+ long expectedValue = PendingAckValues .pack (batchSize , stickyKeyHash );
337335 if (ledgerMap == null || ledgerMap .get (entryId ) != expectedValue ) {
338336 return false ;
339337 }
@@ -366,14 +364,14 @@ public boolean updateRemainingUnacked(long ledgerId, long entryId, int ackedDelt
366364 return false ;
367365 }
368366 long packedValue = ledgerMap .get (entryId );
369- if (packedValue == PACKED_PENDING_ACK_NOT_FOUND ) {
367+ if (PendingAckValues . isNotFound ( packedValue ) ) {
370368 return false ;
371369 }
372- int newRemaining = unpackRemainingUnacked (packedValue ) - ackedDelta ;
370+ int newRemaining = PendingAckValues . remainingUnacked (packedValue ) - ackedDelta ;
373371 if (newRemaining < 0 ) {
374372 return false ;
375373 }
376- ledgerMap .put (entryId , packPendingAckValue (newRemaining , unpackStickyKeyHash (packedValue )));
374+ ledgerMap .put (entryId , PendingAckValues . pack (newRemaining , PendingAckValues . stickyKeyHash (packedValue )));
377375 return true ;
378376 } finally {
379377 writeLock .unlock ();
@@ -395,11 +393,11 @@ public boolean remove(long ledgerId, long entryId) {
395393 return false ;
396394 }
397395 long removedEntry = ledgerMap .remove (entryId );
398- if (removedEntry == PACKED_PENDING_ACK_NOT_FOUND ) {
396+ if (PendingAckValues . isNotFound ( removedEntry ) ) {
399397 return false ;
400398 }
401399 size --;
402- handleRemovePendingAck (ledgerId , entryId , unpackStickyKeyHash (removedEntry ));
400+ handleRemovePendingAck (ledgerId , entryId , PendingAckValues . stickyKeyHash (removedEntry ));
403401 if (ledgerMap .isEmpty ()) {
404402 pendingAcks .remove (ledgerId );
405403 }
@@ -429,15 +427,15 @@ public IntIntPair removeAndGet(long ledgerId, long entryId) {
429427 return null ;
430428 }
431429 long removedEntry = ledgerMap .remove (entryId );
432- if (removedEntry == PACKED_PENDING_ACK_NOT_FOUND ) {
430+ if (PendingAckValues . isNotFound ( removedEntry ) ) {
433431 return null ;
434432 }
435433 size --;
436- handleRemovePendingAck (ledgerId , entryId , unpackStickyKeyHash (removedEntry ));
434+ handleRemovePendingAck (ledgerId , entryId , PendingAckValues . stickyKeyHash (removedEntry ));
437435 if (ledgerMap .isEmpty ()) {
438436 pendingAcks .remove (ledgerId );
439437 }
440- return unpackPendingAckValue (removedEntry );
438+ return PendingAckValues . toPair (removedEntry );
441439 } finally {
442440 writeLock .unlock ();
443441 }
@@ -456,15 +454,15 @@ int removeAndGetRemainingUnacked(long ledgerId, long entryId) {
456454 return PENDING_ACK_NOT_FOUND ;
457455 }
458456 long removedEntry = ledgerMap .remove (entryId );
459- if (removedEntry == PACKED_PENDING_ACK_NOT_FOUND ) {
457+ if (PendingAckValues . isNotFound ( removedEntry ) ) {
460458 return PENDING_ACK_NOT_FOUND ;
461459 }
462460 size --;
463- handleRemovePendingAck (ledgerId , entryId , unpackStickyKeyHash (removedEntry ));
461+ handleRemovePendingAck (ledgerId , entryId , PendingAckValues . stickyKeyHash (removedEntry ));
464462 if (ledgerMap .isEmpty ()) {
465463 pendingAcks .remove (ledgerId );
466464 }
467- return unpackRemainingUnacked (removedEntry );
465+ return PendingAckValues . remainingUnacked (removedEntry );
468466 } finally {
469467 writeLock .unlock ();
470468 }
@@ -533,8 +531,8 @@ private void internalRemoveAllUpTo(long markDeleteLedgerId, long markDeleteEntry
533531 return ;
534532 }
535533 long packedValue = pendingAckEntry .getLongValue ();
536- int batchSize = unpackRemainingUnacked (packedValue );
537- int stickyKeyHash = unpackStickyKeyHash (packedValue );
534+ int batchSize = PendingAckValues . remainingUnacked (packedValue );
535+ int stickyKeyHash = PendingAckValues . stickyKeyHash (packedValue );
538536 if (pendingAcksRemoveHandler != null ) {
539537 if (!batchStarted ) {
540538 pendingAcksRemoveHandler .startBatch ();
@@ -571,28 +569,12 @@ private void internalRemoveAllUpTo(long markDeleteLedgerId, long markDeleteEntry
571569 }
572570 }
573571
574- private static long packPendingAckValue (int remainingUnacked , int stickyKeyHash ) {
575- return ((long ) remainingUnacked << Integer .SIZE ) | (stickyKeyHash & STICKY_KEY_HASH_MASK );
576- }
577-
578572 private static Long2LongSortedMap newLedgerPendingAcks () {
579573 Long2LongRBTreeMap ledgerPendingAcks = new Long2LongRBTreeMap ();
580- ledgerPendingAcks .defaultReturnValue (PACKED_PENDING_ACK_NOT_FOUND );
574+ ledgerPendingAcks .defaultReturnValue (PendingAckValues . PACKED_NOT_FOUND );
581575 return ledgerPendingAcks ;
582576 }
583577
584- private static IntIntPair unpackPendingAckValue (long packedValue ) {
585- return IntIntPair .of (unpackRemainingUnacked (packedValue ), unpackStickyKeyHash (packedValue ));
586- }
587-
588- private static int unpackRemainingUnacked (long packedValue ) {
589- return (int ) (packedValue >> Integer .SIZE );
590- }
591-
592- private static int unpackStickyKeyHash (long packedValue ) {
593- return (int ) packedValue ;
594- }
595-
596578 private void handleRemovePendingAck (long ledgerId , long entryId , int stickyKeyHash ) {
597579 PendingAcksRemoveHandler pendingAcksRemoveHandler = pendingAcksRemoveHandlerSupplier .get ();
598580 if (pendingAcksRemoveHandler != null ) {
0 commit comments