Skip to content

Serious Performance problem caused by #3239 #3759

Description

@gaozhangmin

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.

image

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);
}
}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions