BUG REPORT
Describe the bug
#3239 addressed the replication problem of empty ledger, which means the first entry id is INVALID_ENTRY_ID.
The replicationWorker will replicate these empty ledgers, LINE 441 opened these empty ledgers with openLedgerNoRecovery. In method openLedgerNoRecovery, readLastConfirmed request will be send to server if the fragment is opened.
|
@SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE") |
|
private boolean rereplicate(long ledgerIdToReplicate) throws InterruptedException, BKException, |
|
UnavailableException { |
|
if (LOG.isDebugEnabled()) { |
|
LOG.debug("Going to replicate the fragments of the ledger: {}", ledgerIdToReplicate); |
|
} |
|
|
|
boolean deferLedgerLockRelease = false; |
|
|
|
try (LedgerHandle lh = admin.openLedgerNoRecovery(ledgerIdToReplicate)) { |
|
Set<LedgerFragment> fragments = getUnderreplicatedFragments(lh, |
|
conf.getAuditorLedgerVerificationPercentage()); |
|
|
|
if (LOG.isDebugEnabled()) { |
|
LOG.debug("Founds fragments {} for replication from ledger: {}", fragments, ledgerIdToReplicate); |
|
} |
|
|
|
boolean foundOpenFragments = false; |
|
long numFragsReplicated = 0; |
|
for (LedgerFragment ledgerFragment : fragments) { |
|
if (!ledgerFragment.isClosed()) { |
|
foundOpenFragments = true; |
|
continue; |
|
} |
|
if (!tryReadingFaultyEntries(lh, ledgerFragment)) { |
|
LOG.error("Failed to read faulty entries, so giving up replicating ledgerFragment {}", |
|
ledgerFragment); |
|
continue; |
|
} |
|
try { |
|
admin.replicateLedgerFragment(lh, ledgerFragment, onReadEntryFailureCallback); |
|
numFragsReplicated++; |
|
} catch (BKException.BKBookieHandleNotAvailableException e) { |
|
LOG.warn("BKBookieHandleNotAvailableException while replicating the fragment", e); |
|
} catch (BKException.BKLedgerRecoveryException e) { |
|
LOG.warn("BKLedgerRecoveryException while replicating the fragment", e); |
|
} catch (BKException.BKNotEnoughBookiesException e) { |
|
LOG.warn("BKNotEnoughBookiesException while replicating the fragment", e); |
|
} |
|
} |
|
|
|
if (numFragsReplicated > 0) { |
|
numLedgersReplicated.inc(); |
|
} |
|
|
|
if (foundOpenFragments || isLastSegmentOpenAndMissingBookies(lh)) { |
|
deferLedgerLockRelease = true; |
|
deferLedgerLockRelease(ledgerIdToReplicate); |
|
return false; |
|
} |
Let us check how the server processes the readLastConfirmed request. LINE 547 tries to getLastEntry if the request entry id is LAST_ADD_CONFIRMED
|
private ByteBuf doGetEntry(long ledgerId, long entryId) throws IOException, BookieException { |
|
if (log.isDebugEnabled()) { |
|
log.debug("Get Entry: {}@{}", ledgerId, entryId); |
|
} |
|
|
|
if (entryId == BookieProtocol.LAST_ADD_CONFIRMED) { |
|
return getLastEntry(ledgerId); |
|
} |
Finally, it goes to here. Try to get the last entry id from RocksDB, For a non-existed entry, it searched the a range of ledgers from current ledger id to Long.MAX_VALUE. This search will run for 5mins in my prod env. This caused the slowly process of read request and filled up the read request queue finally.

|
private long getLastEntryInLedgerInternal(long ledgerId) throws IOException { |
|
LongPairWrapper maxEntryId = LongPairWrapper.get(ledgerId, Long.MAX_VALUE); |
|
|
|
// Search the last entry in storage |
|
Entry<byte[], byte[]> entry = locationsDb.getFloor(maxEntryId.array); |
|
maxEntryId.recycle(); |
|
|
|
if (entry == null) { |
|
throw new Bookie.NoEntryException(ledgerId, -1); |
|
} else { |
|
long foundLedgerId = ArrayUtil.getLong(entry.getKey(), 0); |
|
long lastEntryId = ArrayUtil.getLong(entry.getKey(), 8); |
|
|
|
if (foundLedgerId == ledgerId) { |
|
if (log.isDebugEnabled()) { |
|
log.debug("Found last page in storage db for ledger {} - last entry: {}", ledgerId, lastEntryId); |
|
} |
|
return lastEntryId; |
|
} else { |
|
throw new Bookie.NoEntryException(ledgerId, -1); |
|
} |
|
} |
|
} |
BUG REPORT
Describe the bug
#3239 addressed the replication problem of empty ledger, which means the first entry id is
INVALID_ENTRY_ID.The replicationWorker will replicate these empty ledgers, LINE 441 opened these empty ledgers with
openLedgerNoRecovery. In methodopenLedgerNoRecovery,readLastConfirmedrequest will be send to server if the fragment is opened.bookkeeper/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/ReplicationWorker.java
Lines 432 to 481 in c7cc668
Let us check how the server processes the readLastConfirmed request. LINE 547 tries to getLastEntry if the request entry id is
LAST_ADD_CONFIRMEDbookkeeper/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/SingleDirectoryDbLedgerStorage.java
Lines 542 to 549 in c7cc668
Finally, it goes to here. Try to get the last entry id from RocksDB, For a non-existed entry, it searched the a range of ledgers from current ledger id to
Long.MAX_VALUE. This search will run for 5mins in my prod env. This caused the slowly process of read request and filled up the read request queue finally.bookkeeper/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/storage/ldb/EntryLocationIndex.java
Lines 116 to 138 in c7cc668