protected synchronized void asyncReadEntries(OpReadEntry opReadEntry) {
    if (state == State.Fenced) {
      opReadEntry.failed(new ManagedLedgerFencedException());
      return;
    }

    LedgerHandle ledger = null;

    if (opReadEntry.readPosition.getLedgerId() == -1) {
      if (ledgers.isEmpty()) {
        // The ManagedLedger is completely empty
        opReadEntry.emptyResponse();
        return;
      }

      // Initialize the position on the first entry for the first ledger
      // in the set
      opReadEntry.readPosition = new Position(ledgers.firstKey(), 0);
    }

    long id = opReadEntry.readPosition.getLedgerId();

    if (id == currentLedger.getId()) {
      // Current writing ledger is not in the cache (since we don't want
      // it to be automatically evicted), and we cannot use 2 different
      // ledger handles (read & write)for the same ledger.
      ledger = currentLedger;
    } else {
      ledger = ledgerCache.getIfPresent(id);
      if (ledger == null) {
        // Open the ledger and cache the handle
        log.debug("[{}] Asynchronously opening ledger {} for read", name, id);
        bookKeeper.asyncOpenLedger(
            id, config.getDigestType(), config.getPassword(), this, opReadEntry);
        return;
      }
    }

    internalReadFromLedger(ledger, opReadEntry);
  }