5757import java .util .concurrent .ExecutionException ;
5858import java .util .concurrent .ExecutorService ;
5959import java .util .concurrent .ScheduledFuture ;
60+ import java .util .concurrent .Semaphore ;
6061import java .util .concurrent .TimeUnit ;
6162import java .util .concurrent .TimeoutException ;
6263import java .util .concurrent .atomic .AtomicBoolean ;
@@ -354,6 +355,11 @@ public boolean isFenced() {
354355 private long lastEvictOffloadedLedgers ;
355356 private static final int MINIMUM_EVICTION_INTERVAL_DIVIDER = 10 ;
356357
358+ // Semaphore to limit concurrent ledger deletion
359+ private Semaphore deleteLedgerSemaphore = null ;
360+ // Executor service for executing ledger deletion tasks
361+ private ExecutorService deleteLedgerExecutor = null ;
362+
357363 public ManagedLedgerImpl (ManagedLedgerFactoryImpl factory , BookKeeper bookKeeper , MetaStore store ,
358364 ManagedLedgerConfig config , OrderedScheduler scheduledExecutor ,
359365 final String name ) {
@@ -402,6 +408,10 @@ public ManagedLedgerImpl(ManagedLedgerFactoryImpl factory, BookKeeper bookKeeper
402408 this .minBacklogEntriesForCaching = config .getMinimumBacklogEntriesForCaching ();
403409 this .maxBacklogBetweenCursorsForCaching = config .getMaxBacklogBetweenCursorsForCaching ();
404410 this .managedLedgerAttributes = new ManagedLedgerAttributes (this );
411+ if (config .getLedgerDeletionSemaphore () != null ) {
412+ this .deleteLedgerSemaphore = config .getLedgerDeletionSemaphore ();
413+ this .deleteLedgerExecutor = config .getLedgerDeleteExecutor ();
414+ }
405415 }
406416
407417 synchronized void initialize (final ManagedLedgerInitializeLedgerCallback callback , final Object ctx ) {
@@ -569,7 +579,7 @@ protected synchronized void initializeBookKeeper(final ManagedLedgerInitializeLe
569579 public void operationComplete (Void v , Stat stat ) {
570580 ledgersStat = stat ;
571581 emptyLedgersToBeDeleted .forEach (ledgerId -> {
572- bookKeeper . asyncDeleteLedger (ledgerId , (rc , ctx ) -> {
582+ asyncDeleteLedgerWithConcurrencyLimit (ledgerId , (rc , ctx ) -> {
573583 log .info ("[{}] Deleted empty ledger ledgerId={} rc={}" , name , ledgerId , rc );
574584 }, null );
575585 });
@@ -1763,7 +1773,7 @@ public void operationFailed(MetaStoreException e) {
17631773 log .warn ("[{}] Error updating meta data with the new list of ledgers: {}" , name , e .getMessage ());
17641774 handleBadVersion (e );
17651775 mbean .startDataLedgerDeleteOp ();
1766- bookKeeper . asyncDeleteLedger (lh .getId (), (rc1 , ctx1 ) -> {
1776+ asyncDeleteLedgerWithConcurrencyLimit (lh .getId (), (rc1 , ctx1 ) -> {
17671777 mbean .endDataLedgerDeleteOp ();
17681778 if (rc1 != BKException .Code .OK ) {
17691779 log .warn ("[{}] Failed to delete ledger {}: {}" , name , lh .getId (),
@@ -1846,7 +1856,7 @@ protected synchronized void updateLedgersIdsComplete(@Nullable LedgerHandle orig
18461856 STATE_UPDATER .set (this , State .LedgerOpened );
18471857 // Delete original "currentLedger" if it has been removed from "ledgers".
18481858 if (originalCurrentLedger != null && !ledgers .containsKey (originalCurrentLedger .getId ())){
1849- bookKeeper . asyncDeleteLedger (originalCurrentLedger .getId (), (rc , ctx ) -> {
1859+ asyncDeleteLedgerWithConcurrencyLimit (originalCurrentLedger .getId (), (rc , ctx ) -> {
18501860 mbean .endDataLedgerDeleteOp ();
18511861 log .info ("[{}] Delete complete for empty ledger {}. rc={}" , name , originalCurrentLedger .getId (), rc );
18521862 }, null );
@@ -3385,7 +3395,7 @@ private CompletableFuture<Void> asyncDeleteLedger(long ledgerId, long retry) {
33853395 }
33863396
33873397 private void asyncDeleteLedgerWithRetry (CompletableFuture <Void > future , long ledgerId , long retry ) {
3388- bookKeeper . asyncDeleteLedger (ledgerId , (rc , ctx ) -> {
3398+ asyncDeleteLedgerWithConcurrencyLimit (ledgerId , (rc , ctx ) -> {
33893399 if (isNoSuchLedgerExistsException (rc )) {
33903400 log .warn ("[{}] Ledger was already deleted {}" , name , ledgerId );
33913401 future .complete (null );
@@ -3408,6 +3418,33 @@ private void asyncDeleteLedgerWithRetry(CompletableFuture<Void> future, long led
34083418 }, null );
34093419 }
34103420
3421+ /**
3422+ * Delete a ledger asynchronously, applying a concurrency limit if configured.
3423+ * @param ledgerId
3424+ * @param cb
3425+ * @param ctx
3426+ */
3427+ private void asyncDeleteLedgerWithConcurrencyLimit (long ledgerId ,
3428+ org .apache .bookkeeper .client .AsyncCallback .DeleteCallback cb ,
3429+ Object ctx ) {
3430+ if (deleteLedgerSemaphore != null ) {
3431+ AsyncCallback .DeleteCallback cbWrapper = (rc , ctx1 ) -> {
3432+ deleteLedgerSemaphore .release ();
3433+ cb .deleteComplete (rc , ctx1 );
3434+ };
3435+ deleteLedgerExecutor .execute (() -> {
3436+ try {
3437+ deleteLedgerSemaphore .acquire ();
3438+ bookKeeper .asyncDeleteLedger (ledgerId , cbWrapper , ctx );
3439+ } catch (InterruptedException e ) {
3440+ log .error ("[{}] Interrupted while waiting to delete ledger {}" , name , ledgerId );
3441+ }
3442+ });
3443+ } else {
3444+ bookKeeper .asyncDeleteLedger (ledgerId , cb , ctx );
3445+ }
3446+ }
3447+
34113448 @ SuppressWarnings ("checkstyle:fallthrough" )
34123449 private void deleteAllLedgers (DeleteLedgerCallback callback , Object ctx ) {
34133450 List <LedgerInfo > ledgers = Lists .newArrayList (ManagedLedgerImpl .this .ledgers .values ());
@@ -3422,7 +3459,7 @@ private void deleteAllLedgers(DeleteLedgerCallback callback, Object ctx) {
34223459 if (log .isDebugEnabled ()) {
34233460 log .debug ("[{}] Deleting ledger {}" , name , ls );
34243461 }
3425- bookKeeper . asyncDeleteLedger (ls .getLedgerId (), (rc , ctx1 ) -> {
3462+ asyncDeleteLedgerWithConcurrencyLimit (ls .getLedgerId (), (rc , ctx1 ) -> {
34263463 switch (rc ) {
34273464 case Code .NoSuchLedgerExistsException :
34283465 case Code .NoSuchLedgerExistsOnMetadataServerException :
0 commit comments