Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1650,7 +1650,7 @@ public static boolean areEntriesOfLedgerStoredInTheBookie(long ledgerId, BookieI
LOG.debug("Ledger: {} has been deleted", ledgerId);
return false;
} else {
LOG.error("Got exception while trying to read LedgerMeatadata of " + ledgerId, e);
LOG.error("Got exception while trying to read LedgerMetadata of " + ledgerId, e);
throw new RuntimeException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* A utility class to check the complete ledger and finds the UnderReplicated fragments if any.
*
Expand Down Expand Up @@ -238,7 +237,13 @@ private void verifyLedgerFragment(LedgerFragment fragment,
if (lastStored != LedgerHandle.INVALID_ENTRY_ID) {
throw new InvalidFragmentException();
Comment thread
dlg99 marked this conversation as resolved.
}
cb.operationComplete(BKException.Code.OK, fragment);

if (bookieWatcher.isBookieUnavailable(fragment.getAddress(bookieIndex))) {
// fragment is on this bookie, but already know it's unavailable, so skip the call
cb.operationComplete(BKException.Code.BookieHandleNotAvailableException, fragment);
} else {
cb.operationComplete(BKException.Code.OK, fragment);
}
} else if (bookieWatcher.isBookieUnavailable(fragment.getAddress(bookieIndex))) {
// fragment is on this bookie, but already know it's unavailable, so skip the call
cb.operationComplete(BKException.Code.BookieHandleNotAvailableException, fragment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
package org.apache.bookkeeper.client;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.bookkeeper.bookie.BookieImpl;
import org.apache.bookkeeper.client.BKException.BKIllegalOpException;
Expand All @@ -29,6 +32,7 @@
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.meta.UnderreplicatedLedger;
import org.apache.bookkeeper.meta.ZkLedgerUnderreplicationManager;
import org.apache.bookkeeper.net.BookieId;
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
import org.junit.Test;

Expand All @@ -44,19 +48,23 @@ public class BookieDecommissionTest extends BookKeeperClusterTestCase {

public BookieDecommissionTest() {
super(NUM_OF_BOOKIES, 480);
baseConf.setOpenLedgerRereplicationGracePeriod(String.valueOf(30000));
baseConf.setOpenLedgerRereplicationGracePeriod(String.valueOf(1000));
setAutoRecoveryEnabled(true);
}

@FlakyTest("https://github.com/apache/bookkeeper/issues/502")
@Test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so basically we were not running this test anymore.

great to see this running again

public void testDecommissionBookie() throws Exception {
ZkLedgerUnderreplicationManager urLedgerMgr = new ZkLedgerUnderreplicationManager(baseClientConf, zkc);
BookKeeperAdmin bkAdmin = new BookKeeperAdmin(zkUtil.getZooKeeperConnectString());

List<Long> ledgerIds = new LinkedList<>();

int numOfLedgers = 2 * NUM_OF_BOOKIES;
int numOfEntries = 2 * NUM_OF_BOOKIES;
for (int i = 0; i < numOfLedgers; i++) {
LedgerHandle lh = bkc.createLedger(3, 2, digestType, PASSWORD.getBytes());
ledgerIds.add(lh.getId());
for (int j = 0; j < numOfEntries; j++) {
lh.addEntry("entry".getBytes());
}
Expand All @@ -67,6 +75,7 @@ public void testDecommissionBookie() throws Exception {
*/
for (int i = 0; i < numOfLedgers; i++) {
LedgerHandle emptylh = bkc.createLedger(3, 2, digestType, PASSWORD.getBytes());
ledgerIds.add(emptylh.getId());
emptylh.close();
}

Expand All @@ -88,7 +97,7 @@ public void testDecommissionBookie() throws Exception {
*/
bkAdmin.decommissionBookie(BookieImpl.getBookieId(killedBookieConf));
bkAdmin.triggerAudit();
Thread.sleep(500);
Thread.sleep(5000);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of the scope of this PR, but in the future it would be better to wait for a specific condition, in order to reduce flakyness

Iterator<UnderreplicatedLedger> ledgersToRereplicate = urLedgerMgr.listLedgersToRereplicate(null);
if (ledgersToRereplicate.hasNext()) {
while (ledgersToRereplicate.hasNext()) {
Expand All @@ -101,7 +110,7 @@ public void testDecommissionBookie() throws Exception {
killedBookieConf = killBookie(0);
bkAdmin.decommissionBookie(BookieImpl.getBookieId(killedBookieConf));
bkAdmin.triggerAudit();
Thread.sleep(500);
Thread.sleep(5000);
ledgersToRereplicate = urLedgerMgr.listLedgersToRereplicate(null);
if (ledgersToRereplicate.hasNext()) {
while (ledgersToRereplicate.hasNext()) {
Expand All @@ -111,6 +120,10 @@ public void testDecommissionBookie() throws Exception {
fail("There are not supposed to be any underreplicatedledgers");
}
bkAdmin.close();

for (Long id: ledgerIds) {
verifyNoFragmentsOnBookie(id, BookieImpl.getBookieId(killedBookieConf));
}
}

@Test
Expand All @@ -130,11 +143,16 @@ public void testDecommissionForLedgersWithMultipleSegmentsAndNotWriteClosed() th
lh4.addEntry(j, "data".getBytes());
}

// avoiding autorecovery fencing the ledger
servers.forEach(srv -> srv.stopAutoRecovery());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure that "stopAutoRecovery" waits for autoRecovery to be totally stopped, maybe there is still some task running?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does wait.
I reduced openLedgerRereplicationGracePeriod, thus autorecovery may fence the ledger and now the test has to stop/start it to avoid flakiness in this case (add to the ledger to force ensemble change)


startNewBookie();

assertEquals("Number of Available Bookies", NUM_OF_BOOKIES + 1, bkAdmin.getAvailableBookies().size());

ServerConfiguration killedBookieConf = killBookie(0);
BookieId killedBookieId = getBookie(0);
log.warn("Killing bookie {}", killedBookieId);
killBookie(0);

/*
* since one of the bookie is killed, ensemble change happens when next
Expand All @@ -152,16 +170,24 @@ public void testDecommissionForLedgersWithMultipleSegmentsAndNotWriteClosed() th
lh1.close();
lh2.close();

servers.forEach(srv -> {
try {
srv.startAutoRecovery();
} catch (Exception e) {
throw new RuntimeException(e);
}
});

/*
* If the last fragment of the ledger is underreplicated and if the
* ledger is not closed then it will remain underreplicated for
* openLedgerRereplicationGracePeriod (by default 30 secs). For more
* openLedgerRereplicationGracePeriod (by default 30 secs, 1 in the test). For more
* info. Check BOOKKEEPER-237 and BOOKKEEPER-325. But later
* ReplicationWorker will fence the ledger.
*/
bkAdmin.decommissionBookie(BookieImpl.getBookieId(killedBookieConf));
bkAdmin.decommissionBookie(killedBookieId);
bkAdmin.triggerAudit();
Thread.sleep(500);
Thread.sleep(5000);
Iterator<UnderreplicatedLedger> ledgersToRereplicate = urLedgerMgr.listLedgersToRereplicate(null);
if (ledgersToRereplicate.hasNext()) {
while (ledgersToRereplicate.hasNext()) {
Expand All @@ -171,6 +197,73 @@ public void testDecommissionForLedgersWithMultipleSegmentsAndNotWriteClosed() th
fail("There are not supposed to be any underreplicatedledgers");
}
bkAdmin.close();

verifyNoFragmentsOnBookie(1L, killedBookieId);
verifyNoFragmentsOnBookie(2L, killedBookieId);
verifyNoFragmentsOnBookie(3L, killedBookieId);
verifyNoFragmentsOnBookie(4L, killedBookieId);
}

@Test
public void testDecommissionForEmptyLedgers() throws Exception {
ZkLedgerUnderreplicationManager urLedgerMgr = new ZkLedgerUnderreplicationManager(baseClientConf, zkc);
BookKeeperAdmin bkAdmin = new BookKeeperAdmin(zkUtil.getZooKeeperConnectString());

LedgerHandle lh1 = bkc.createLedgerAdv(1L, numBookies, numBookies - 1, numBookies - 1,
digestType, PASSWORD.getBytes(), null);
LedgerHandle lh2 = bkc.createLedgerAdv(2L, numBookies, numBookies - 1, numBookies - 1,
digestType, PASSWORD.getBytes(), null);
LedgerHandle lh3 = bkc.createLedgerAdv(3L, numBookies, numBookies - 1, numBookies - 1,
digestType, PASSWORD.getBytes(), null);
LedgerHandle lh4 = bkc.createLedgerAdv(4L, numBookies, numBookies - 1, numBookies - 1,
digestType, PASSWORD.getBytes(), null);

lh1.close();
lh2.close();

startNewBookie();

assertEquals("Number of Available Bookies", NUM_OF_BOOKIES + 1, bkAdmin.getAvailableBookies().size());

BookieId killedBookieId = getBookie(0);
log.warn("Killing bookie {}", killedBookieId);
killBookie(0);
assertEquals("Number of Available Bookies", NUM_OF_BOOKIES, bkAdmin.getAvailableBookies().size());

bkAdmin.decommissionBookie(killedBookieId);
bkAdmin.triggerAudit();
Thread.sleep(5000);
Iterator<UnderreplicatedLedger> ledgersToRereplicate = urLedgerMgr.listLedgersToRereplicate(null);
if (ledgersToRereplicate.hasNext()) {
while (ledgersToRereplicate.hasNext()) {
long ledgerId = ledgersToRereplicate.next().getLedgerId();
log.error("Ledger: {} is underreplicated which is not expected. {}",
ledgerId, ledgersToRereplicate.next().getReplicaList());
}
fail("There are not supposed to be any underreplicatedledgers");
}
bkAdmin.close();

verifyNoFragmentsOnBookie(1L, killedBookieId);
verifyNoFragmentsOnBookie(2L, killedBookieId);
verifyNoFragmentsOnBookie(3L, killedBookieId);
verifyNoFragmentsOnBookie(4L, killedBookieId);

lh3.close();
lh4.close();
}

private void verifyNoFragmentsOnBookie(long ledgerId, BookieId bookieId) throws BKException, InterruptedException {
LedgerHandle lh = bkc.openLedgerNoRecovery(ledgerId, digestType, PASSWORD.getBytes());
log.error("Ledger {} metadata: {}", ledgerId, lh.getLedgerMetadata());

lh.getLedgerMetadata().getAllEnsembles().forEach((num, bookies) -> {
bookies.forEach(id -> {
assertNotEquals(bookieId, id);
});
});

lh.close();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public void testShouldNotGetTheFragmentIfThereIsNoMissedEntry()
LOG.info("unreplicated fragment: {}", r);
}

assertEquals("Should not have any missing fragment", 0, result.size());
assertEquals("Empty fragment should be considered missing", 1, result.size());
}

/**
Expand Down Expand Up @@ -203,7 +203,7 @@ public void testShouldGetTwoFrgamentsIfTwoBookiesFailedInSameEnsemble()
LOG.info("unreplicated fragment: {}", r);
}

assertEquals("There should be 1 fragments", 1, result.size());
assertEquals("Empty fragment should be considered missing", 2, result.size());
assertEquals("There should be 2 failed bookies in the fragment",
2, result.iterator().next().getBookiesIndexes().size());
}
Expand Down Expand Up @@ -314,8 +314,8 @@ public void testShouldGet2FragmentsWithEmptyLedgerButBookiesDead() throws Except
Set<LedgerFragment> result = getUnderReplicatedFragments(lh);
assertNotNull("Result shouldn't be null", result);
assertEquals("There should be 1 fragments.", 1, result.size());
assertEquals("There should be 2 failed bookies in the fragment",
2, result.iterator().next().getBookiesIndexes().size());
assertEquals("There should be 3 failed bookies in the fragment",
3, result.iterator().next().getBookiesIndexes().size());
}

/**
Expand Down Expand Up @@ -421,8 +421,8 @@ public void testClosedEmptyLedger() throws Exception {

Set<LedgerFragment> result = getUnderReplicatedFragments(lh1);
assertNotNull("Result shouldn't be null", result);
assertEquals("There should be 0 fragment. But returned fragments are "
+ result, 0, result.size());
assertEquals("Empty fragment should be considered missing"
+ result, 1, result.size());
}

/**
Expand Down Expand Up @@ -450,8 +450,8 @@ public void testClosedSingleEntryLedger() throws Exception {

Set<LedgerFragment> result = getUnderReplicatedFragments(lh1);
assertNotNull("Result shouldn't be null", result);
assertEquals("There should be 0 fragment. But returned fragments are "
+ result, 0, result.size());
assertEquals("Empty fragment should be considered missing"
+ result, 1, result.size());
lh1.close();

// kill bookie 1
Expand All @@ -469,8 +469,8 @@ public void testClosedSingleEntryLedger() throws Exception {
assertNotNull("Result shouldn't be null", result);
assertEquals("There should be 1 fragment. But returned fragments are "
+ result, 1, result.size());
assertEquals("There should be 1 failed bookies in the fragment",
1, result.iterator().next().getBookiesIndexes().size());
assertEquals("There should be 2 failed bookies in the fragment",
2, result.iterator().next().getBookiesIndexes().size());
lh1.close();

// kill bookie 0
Expand All @@ -488,8 +488,8 @@ public void testClosedSingleEntryLedger() throws Exception {
assertNotNull("Result shouldn't be null", result);
assertEquals("There should be 1 fragment. But returned fragments are "
+ result, 1, result.size());
assertEquals("There should be 2 failed bookies in the fragment",
2, result.iterator().next().getBookiesIndexes().size());
assertEquals("There should be 3 failed bookies in the fragment",
3, result.iterator().next().getBookiesIndexes().size());
lh1.close();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ public void testEmptyLedgerLosesQuorumEventually() throws Exception {
LOG.info("Killing last bookie, {}, in ensemble {}", replicaToKill,
lh.getLedgerMetadata().getAllEnsembles().get(0L));
killBookie(replicaToKill);
startNewBookie();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we changing this existing test ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behavior has changed now.
basically autorecovery used to ignore failed bookie for empty ledgers if it is not in the writeset for entryId 0 when WQ < ES.
e.g. ensemble = (bl1, bk2, bk3), WQ = 2, ledger is empty.
bk3 is down, it is not in the writeset for entryId=0.

Auditor would add it to underreplicated, autorecovery would skip it and simply remove from underreplicated, go back to the start of the sentence.
This is nice until you end up with an empty ledger like that. or a few thousands of them.


getAuditor(10, TimeUnit.SECONDS).submitAuditTask().get(); // ensure auditor runs

Expand Down
Loading