/**
   * Checks whether there are ledger that have been fully consumed and deletes them
   *
   * @throws Exception
   */
  protected void internalTrimConsumedLedgers() {
    // Ensure only one trimming operation is active
    List<LedgerStat> ledgersToDelete = Lists.newArrayList();

    synchronized (this) {
      long slowestReaderLedgerId = -1;
      if (cursors.isEmpty() && currentLedger != null) {
        // At this point the lastLedger will be pointing to the
        // ledger that has just been closed, therefore the +1 to
        // include lastLedger in the trimming.
        slowestReaderLedgerId = currentLedger.getId() + 1;
      } else {
        slowestReaderLedgerId = cursors.getSlowestReaderPosition().getLedgerId();
      }

      for (LedgerStat ls : ledgers.headMap(slowestReaderLedgerId, false).values()) {
        ledgersToDelete.add(ls);
        ledgerCache.invalidate(ls.getLedgerId());
      }

      if (ledgersToDelete.isEmpty()) {
        return;
      }
    }

    // Delete the ledgers _without_ holding the lock on 'this'
    long removedCount = 0;
    long removedSize = 0;

    for (LedgerStat ls : ledgersToDelete) {
      log.info("[{}] Removing ledger {}", name, ls.getLedgerId());
      try {
        bookKeeper.deleteLedger(ls.getLedgerId());
        ++removedCount;
        removedSize += ls.getSize();
      } catch (BKNoSuchLedgerExistsException e) {
        log.warn("[{}] Ledger was already deleted {}", name, ls.getLedgerId());
      } catch (Exception e) {
        log.error("[{}] Error deleting ledger {}", name, ls.getLedgerId());
        return;
      }
    }

    // Update metadata
    try {
      synchronized (this) {
        numberOfEntries.addAndGet(-removedCount);
        totalSize.addAndGet(-removedSize);
        for (LedgerStat ls : ledgersToDelete) {
          ledgers.remove(ls.getLedgerId());
        }

        if (state == State.CreatingLedger) {
          // The list of ledgers is being modified asynchronously, we
          // cannot update it now. In case of a client crash, this
          // will just result in some ledgers to be deleted twice,
          // without any side consequences.
          log.info("[{}] Skipped updating ledger list for concurrent modification", name);
          return;
        }

        ledgersVersion = store.updateLedgersIds(name, ledgers.values(), ledgersVersion);
      }
    } catch (MetaStoreException e) {
      log.error("[{}] Failed to update the list of ledgers after trimming", name, e);
    }
  }