/** * Tests that Replication worker should clean the leadger under replication node of the ledger * already deleted */ @Test(timeout = 3000) public void testRWShouldCleanTheLedgerFromUnderReplicationIfLedgerAlreadyDeleted() throws Exception { LedgerHandle lh = bkc.createLedger(2, 2, BookKeeper.DigestType.CRC32, TESTPASSWD); for (int i = 0; i < 10; i++) { lh.addEntry(data); } lh.close(); BookieSocketAddress replicaToKill = LedgerHandleAdapter.getLedgerMetadata(lh).getEnsembles().get(0L).get(0); LOG.info("Killing Bookie", replicaToKill); killBookie(replicaToKill); int startNewBookie = startNewBookie(); BookieSocketAddress newBkAddr = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), startNewBookie); LOG.info("New Bookie addr :" + newBkAddr); ReplicationWorker rw = new ReplicationWorker(zkc, baseConf, newBkAddr); rw.start(); try { bkc.deleteLedger(lh.getId()); // Deleting the ledger // Also mark ledger as in UnderReplication underReplicationManager.markLedgerUnderreplicated(lh.getId(), replicaToKill.toString()); while (isLedgerInUnderReplication(lh.getId(), basePath)) { Thread.sleep(100); } } finally { rw.shutdown(); } }
@Test(timeout = 60000) public void testAllocation() throws Exception { String allocationPath = "/allocation1"; SimpleLedgerAllocator allocator = createAllocator(allocationPath); allocator.allocate(); ZKTransaction txn = newTxn(); LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER)); logger.info("Try obtaining ledger handle {}", lh.getId()); byte[] data = zkc.get().getData(allocationPath, false, null); assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8))); txn.addOp(DefaultZKOp.of(Op.setData("/unexistedpath", "data".getBytes(UTF_8), -1))); try { FutureUtils.result(txn.execute()); fail("Should fail the transaction when setting unexisted path"); } catch (ZKException ke) { // expected logger.info("Should fail on executing transaction when setting unexisted path", ke); } data = zkc.get().getData(allocationPath, false, null); assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8))); // Create new transaction to obtain the ledger again. txn = newTxn(); // we could obtain the ledger if it was obtained LedgerHandle newLh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER)); assertEquals(lh.getId(), newLh.getId()); FutureUtils.result(txn.execute()); data = zkc.get().getData(allocationPath, false, null); assertEquals(0, data.length); Utils.close(allocator); }
private void internalReadFromLedger(LedgerHandle ledger, OpReadEntry opReadEntry) { // Perform the read long firstEntry = opReadEntry.readPosition.getEntryId(); if (firstEntry > ledger.getLastAddConfirmed()) { log.debug( "[{}] No more messages to read from ledger={} lastEntry={} readEntry={}", va(name, ledger.getId(), ledger.getLastAddConfirmed(), firstEntry)); if (ledger.getId() != currentLedger.getId()) { // Cursor was placed past the end of one ledger, move it to the // beginning of the next ledger Long nextLedgerId = ledgers.ceilingKey(ledger.getId() + 1); opReadEntry.nextReadPosition = new Position(nextLedgerId, 0); } opReadEntry.emptyResponse(); return; } long lastEntry = min(firstEntry + opReadEntry.count - 1, ledger.getLastAddConfirmed()); long expectedEntries = lastEntry - firstEntry + 1; opReadEntry.entries = Lists.newArrayListWithExpectedSize((int) expectedEntries); log.debug( "[{}] Reading entries from ledger {} - first={} last={}", va(name, ledger.getId(), firstEntry, lastEntry)); ledger.asyncReadEntries(firstEntry, lastEntry, this, opReadEntry); }
@After public void tearDown() throws Exception { if (createdLedgers != null && bookKeeperClient != null) { for (LedgerHandle ledger : createdLedgers) { LOG.info("Deleting ledger with id " + ledger.getId()); bookKeeperClient.deleteLedger(ledger.getId()); } } }
/** * Tests that replication worker1 should take one fragment replication and other replication * worker also should compete for the replication. */ @Test(timeout = 90000) public void test2RWsShouldCompeteForReplicationOf2FragmentsAndCompleteReplication() throws Exception { LedgerHandle lh = bkc.createLedger(2, 2, BookKeeper.DigestType.CRC32, TESTPASSWD); for (int i = 0; i < 10; i++) { lh.addEntry(data); } lh.close(); BookieSocketAddress replicaToKill = LedgerHandleAdapter.getLedgerMetadata(lh).getEnsembles().get(0L).get(0); LOG.info("Killing Bookie", replicaToKill); ServerConfiguration killedBookieConfig = killBookie(replicaToKill); killAllBookies(lh, null); // Starte RW1 int startNewBookie1 = startNewBookie(); BookieSocketAddress newBkAddr1 = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), startNewBookie1); LOG.info("New Bookie addr :" + newBkAddr1); ReplicationWorker rw1 = new ReplicationWorker(zkc, baseConf, newBkAddr1); // Starte RW2 int startNewBookie2 = startNewBookie(); BookieSocketAddress newBkAddr2 = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), startNewBookie2); LOG.info("New Bookie addr :" + newBkAddr2); ZooKeeper zkc1 = ZooKeeperClient.createConnectedZooKeeperClient(zkUtil.getZooKeeperConnectString(), 10000); ReplicationWorker rw2 = new ReplicationWorker(zkc1, baseConf, newBkAddr2); rw1.start(); rw2.start(); try { underReplicationManager.markLedgerUnderreplicated(lh.getId(), replicaToKill.toString()); int counter = 10; while (counter-- > 0) { assertTrue( "Expecting that replication should not complete", isLedgerInUnderReplication(lh.getId(), basePath)); Thread.sleep(100); } // restart killed bookie bs.add(startBookie(killedBookieConfig)); bsConfs.add(killedBookieConfig); while (isLedgerInUnderReplication(lh.getId(), basePath)) { Thread.sleep(100); } // Should be able to read the entries from 0-9 verifyRecoveredLedgers(lh, 0, 9); } finally { rw1.shutdown(); rw2.shutdown(); zkc1.close(); } }
/** * Tests that ReplicationWorker should not have identified for postponing the replication if * ledger is in open state and lastFragment is not in underReplication state. Note that RW should * not fence such ledgers. */ @Test(timeout = 30000) public void testRWShouldReplicateTheLedgersAfterTimeoutIfLastFragmentIsNotUR() throws Exception { LedgerHandle lh = bkc.createLedger(3, 3, BookKeeper.DigestType.CRC32, TESTPASSWD); for (int i = 0; i < 10; i++) { lh.addEntry(data); } BookieSocketAddress replicaToKill = LedgerHandleAdapter.getLedgerMetadata(lh).getEnsembles().get(0L).get(0); LOG.info("Killing Bookie", replicaToKill); killBookie(replicaToKill); int startNewBookie = startNewBookie(); // Reform ensemble...Making sure that last fragment is not in // under-replication for (int i = 0; i < 10; i++) { lh.addEntry(data); } BookieSocketAddress newBkAddr = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), startNewBookie); LOG.info("New Bookie addr :" + newBkAddr); ReplicationWorker rw = new ReplicationWorker(zkc, baseConf, newBkAddr); LedgerManagerFactory mFactory = LedgerManagerFactory.newLedgerManagerFactory(baseClientConf, zkc); LedgerUnderreplicationManager underReplicationManager = mFactory.newLedgerUnderreplicationManager(); rw.start(); try { underReplicationManager.markLedgerUnderreplicated(lh.getId(), replicaToKill.toString()); while (isLedgerInUnderReplication(lh.getId(), basePath)) { Thread.sleep(100); } killAllBookies(lh, newBkAddr); // Should be able to read the entries from 0-9 verifyRecoveredLedgers(lh, 0, 9); lh = bkc.openLedgerNoRecovery(lh.getId(), BookKeeper.DigestType.CRC32, TESTPASSWD); // Ledger should be still in open state assertTrue("Ledger must have been closed by RW", ClientUtil.isLedgerOpen(lh)); } finally { rw.shutdown(); underReplicationManager.close(); } }
protected synchronized boolean hasMoreEntries(Position position) { if (position.getLedgerId() == currentLedger.getId()) { // If we are reading from the last ledger, use the // LedgerHandle metadata return position.getEntryId() <= currentLedger.getLastAddConfirmed(); } else if (currentLedger.getLastAddConfirmed() >= 0) { // We have entries in the current ledger and we are reading from an // older ledger return true; } else { // At this point, currentLedger is empty, we need to check in the // older ledgers for entries past the current position LedgerStat ls = ledgers.get(position.getLedgerId()); if (ls == null) { // The cursor haven't been initialized yet checkArgument(position.getLedgerId() == -1); return true; } else if (position.getEntryId() < ls.getEntriesCount()) { // There are still entries to read in the current reading ledger return true; } else { for (LedgerStat stat : ledgers.tailMap(position.getLedgerId(), false).values()) { if (stat.getEntriesCount() > 0) return true; } return false; } } }
/* * (non-Javadoc) * * @see * org.apache.bookkeeper.mledger.impl.MetaStore.UpdateLedgersIdsCallback * #updateLedgersIdsComplete * (org.apache.bookkeeper.mledger.ManagedLedgerException.MetaStoreException) */ public synchronized void updateLedgersIdsComplete(Version version) { state = State.LedgerOpened; ledgersVersion = version; // Process all the pending addEntry requests while (!pendingAddEntries.isEmpty()) { OpAddEntry op = pendingAddEntries.poll(); op.setLedger(currentLedger); ++currentLedgerEntries; currentLedgerSize += op.data.length; if (currentLedgerIsFull()) { state = State.ClosingLedger; op.setCloseWhenDone(true); op.initiate(); log.debug( "[{}] Stop writing into ledger {} queue={}", va(name, currentLedger.getId(), pendingAddEntries.size())); break; } else { op.initiate(); } } }
/** * Skip a specified number of entries and return the resulting position. * * @param startPosition the current position * @param entriesToSkip the numbers of entries to skip * @return the new position */ protected synchronized Position skipEntries(Position startPosition, int entriesToSkip) { log.debug("[{}] Skipping {} entries from position {}", va(name, entriesToSkip, startPosition)); long ledgerId = startPosition.getLedgerId(); entriesToSkip += startPosition.getEntryId(); while (entriesToSkip > 0) { if (currentLedger != null && ledgerId == currentLedger.getId()) { checkArgument(entriesToSkip <= (currentLedger.getLastAddConfirmed() + 1)); return new Position(ledgerId, entriesToSkip); } else { LedgerStat ledger = ledgers.get(ledgerId); if (ledger == null) { checkArgument(!ledgers.isEmpty()); ledgerId = ledgers.ceilingKey(ledgerId); continue; } if (entriesToSkip < ledger.getEntriesCount()) { return new Position(ledgerId, entriesToSkip); } else { // Move to next ledger entriesToSkip -= ledger.getEntriesCount(); ledgerId = ledgers.ceilingKey(ledgerId + 1); } } } return new Position(ledgerId, 0); }
@Test(timeout = 60000) public void testCloseAllocatorDuringObtaining() throws Exception { String allocationPath = "/allocation2"; SimpleLedgerAllocator allocator = createAllocator(allocationPath); allocator.allocate(); ZKTransaction txn = newTxn(); // close during obtaining ledger. LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER)); Utils.close(allocator); byte[] data = zkc.get().getData(allocationPath, false, null); assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8))); // the ledger is not deleted bkc.get() .openLedger( lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes(UTF_8)); }
/** * Verify read last confirmed op, it shouldn't cause any deadlock as new bookie connection is * being established and returning the connection notification in the same caller thread. It would * be simulated by delaying the future.addlistener() in PerChannelBookieClient after the * connection establishment. Now the future.addlistener() will notify back in the same thread and * simultaneously invoke the pendingOp.operationComplete() event. * * <p>BOOKKEEPER-326 */ @Test(timeout = 60000) public void testReadLastConfirmedOp() throws Exception { startNewBookie(); startNewBookie(); // Create a ledger LedgerHandle beforelh = bkc.createLedger(numBookies + 2, numBookies + 2, digestType, "".getBytes()); int numEntries = 10; String tmp = "BookKeeper is cool!"; for (int i = 0; i < numEntries; i++) { beforelh.addEntry(tmp.getBytes()); } // shutdown first bookie server killBookie(0); startNewBookie(); // create new bookie client, and forcing to establish new // PerChannelBookieClient connections for recovery flows. BookKeeperTestClient bkc1 = new BookKeeperTestClient(baseClientConf); // try to open ledger with recovery LedgerHandle afterlh = bkc1.openLedger(beforelh.getId(), digestType, "".getBytes()); assertEquals("Entries got missed", beforelh.getLastAddPushed(), afterlh.getLastAddConfirmed()); bkc1.close(); }
public void testReadWriteZero() throws IOException { try { // Create a BookKeeper client and a ledger bkc = new BookKeeper("127.0.0.1"); lh = bkc.createLedger(ledgerPassword); bkc.initMessageDigest("SHA1"); ledgerId = lh.getId(); LOG.info("Ledger ID: " + lh.getId()); for (int i = 0; i < numEntriesToWrite; i++) { bkc.addEntry(lh, new byte[0]); } /* * Write a non-zero entry */ ByteBuffer entry = ByteBuffer.allocate(4); entry.putInt(rng.nextInt(maxInt)); entry.position(0); entries.add(entry.array()); bkc.addEntry(lh, entry.array()); bkc.closeLedger(lh); lh = bkc.openLedger(ledgerId, ledgerPassword); LOG.debug("Number of entries written: " + lh.getLast()); assertTrue("Verifying number of entries written", lh.getLast() == (numEntriesToWrite + 1)); ls = bkc.readEntries(lh, 0, numEntriesToWrite - 1); int i = 0; while (ls.hasMoreElements()) { ByteBuffer result = ByteBuffer.wrap(ls.nextElement().getEntry()); LOG.debug("Length of result: " + result.capacity()); assertTrue("Checking if entry " + i + " has zero bytes", result.capacity() == 0); } bkc.closeLedger(lh); } catch (KeeperException e) { e.printStackTrace(); } catch (BKException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }
public void testReadWriteSyncSingleClient() throws IOException { try { // Create a BookKeeper client and a ledger bkc = new BookKeeper("127.0.0.1"); lh = bkc.createLedger(ledgerPassword); bkc.initMessageDigest("SHA1"); ledgerId = lh.getId(); LOG.info("Ledger ID: " + lh.getId()); for (int i = 0; i < numEntriesToWrite; i++) { ByteBuffer entry = ByteBuffer.allocate(4); entry.putInt(rng.nextInt(maxInt)); entry.position(0); entries.add(entry.array()); bkc.addEntry(lh, entry.array()); } bkc.closeLedger(lh); lh = bkc.openLedger(ledgerId, ledgerPassword); LOG.debug("Number of entries written: " + lh.getLast()); assertTrue("Verifying number of entries written", lh.getLast() == numEntriesToWrite); ls = bkc.readEntries(lh, 0, numEntriesToWrite - 1); int i = 0; while (ls.hasMoreElements()) { ByteBuffer origbb = ByteBuffer.wrap(entries.get(i++)); Integer origEntry = origbb.getInt(); ByteBuffer result = ByteBuffer.wrap(ls.nextElement().getEntry()); LOG.debug("Length of result: " + result.capacity()); LOG.debug("Original entry: " + origEntry); Integer retrEntry = result.getInt(); LOG.debug("Retrieved entry: " + retrEntry); assertTrue("Checking entry " + i + " for equality", origEntry.equals(retrEntry)); } bkc.closeLedger(lh); } catch (KeeperException e) { e.printStackTrace(); } catch (BKException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }
/** * Tests that replication worker should retry for replication until enough bookies available for * replication */ @Test(timeout = 60000) public void testRWShouldRetryUntilThereAreEnoughBksAvailableForReplication() throws Exception { LedgerHandle lh = bkc.createLedger(1, 1, BookKeeper.DigestType.CRC32, TESTPASSWD); for (int i = 0; i < 10; i++) { lh.addEntry(data); } lh.close(); BookieSocketAddress replicaToKill = LedgerHandleAdapter.getLedgerMetadata(lh).getEnsembles().get(0L).get(0); LOG.info("Killing Bookie", replicaToKill); ServerConfiguration killedBookieConfig = killBookie(replicaToKill); int startNewBookie = startNewBookie(); BookieSocketAddress newBkAddr = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), startNewBookie); LOG.info("New Bookie addr :" + newBkAddr); killAllBookies(lh, newBkAddr); ReplicationWorker rw = new ReplicationWorker(zkc, baseConf, newBkAddr); rw.start(); try { underReplicationManager.markLedgerUnderreplicated(lh.getId(), replicaToKill.toString()); int counter = 100; while (counter-- > 0) { assertTrue( "Expecting that replication should not complete", isLedgerInUnderReplication(lh.getId(), basePath)); Thread.sleep(100); } // restart killed bookie bs.add(startBookie(killedBookieConfig)); bsConfs.add(killedBookieConfig); while (isLedgerInUnderReplication(lh.getId(), basePath)) { Thread.sleep(100); } // Should be able to read the entries from 0-9 verifyRecoveredLedgers(lh, 0, 9); } finally { rw.shutdown(); } }
/** * Validate whether a specified position is valid for the current managed ledger. * * @param position the position to validate * @return true if the position is valid, false otherwise */ protected synchronized boolean isValidPosition(Position position) { if (position.getLedgerId() == currentLedger.getId()) { return position.getEntryId() <= currentLedger.getLastAddConfirmed(); } else { // Look in the ledgers map LedgerStat ls = ledgers.get(position.getLedgerId()); if (ls == null) return false; return position.getEntryId() < ls.getEntriesCount(); } }
private void verifyRecoveredLedgers(LedgerHandle lh, long startEntryId, long endEntryId) throws BKException, InterruptedException { LedgerHandle lhs = bkc.openLedgerNoRecovery(lh.getId(), BookKeeper.DigestType.CRC32, TESTPASSWD); Enumeration<LedgerEntry> entries = lhs.readEntries(startEntryId, endEntryId); assertTrue("Should have the elements", entries.hasMoreElements()); while (entries.hasMoreElements()) { LedgerEntry entry = entries.nextElement(); assertEquals("TestReplicationWorker", new String(entry.getEntry())); } }
/** * Tests that replication worker should replicate the failed bookie fragments to target bookie * given to the worker. */ @Test(timeout = 30000) public void testRWShouldReplicateFragmentsToTargetBookie() throws Exception { LedgerHandle lh = bkc.createLedger(3, 3, BookKeeper.DigestType.CRC32, TESTPASSWD); for (int i = 0; i < 10; i++) { lh.addEntry(data); } BookieSocketAddress replicaToKill = LedgerHandleAdapter.getLedgerMetadata(lh).getEnsembles().get(0L).get(0); LOG.info("Killing Bookie", replicaToKill); killBookie(replicaToKill); int startNewBookie = startNewBookie(); for (int i = 0; i < 10; i++) { lh.addEntry(data); } BookieSocketAddress newBkAddr = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), startNewBookie); LOG.info("New Bookie addr :" + newBkAddr); ReplicationWorker rw = new ReplicationWorker(zkc, baseConf, newBkAddr); rw.start(); try { underReplicationManager.markLedgerUnderreplicated(lh.getId(), replicaToKill.toString()); while (isLedgerInUnderReplication(lh.getId(), basePath)) { Thread.sleep(100); } killAllBookies(lh, newBkAddr); // Should be able to read the entries from 0-9 verifyRecoveredLedgers(lh, 0, 9); } finally { rw.shutdown(); } }
/* * (non-Javadoc) * * @see * org.apache.bookkeeper.client.AsyncCallback.OpenCallback#openComplete(int, * org.apache.bookkeeper.client.LedgerHandle, java.lang.Object) */ @Override public void openComplete(int rc, LedgerHandle ledger, Object ctx) { OpReadEntry opReadEntry = (OpReadEntry) ctx; if (rc != BKException.Code.OK) { opReadEntry.failed(new ManagedLedgerException(BKException.create(rc))); return; } log.debug("[{}] Successfully opened ledger {} for reading", name, ledger.getId()); internalReadFromLedger(ledger, opReadEntry); }
/** * Test that if the local bookie turns out to be readonly, then no point in running RW. So RW * should shutdown. */ @Test(timeout = 20000) public void testRWShutdownOnLocalBookieReadonlyTransition() throws Exception { LedgerHandle lh = bkc.createLedger(3, 3, BookKeeper.DigestType.CRC32, TESTPASSWD); for (int i = 0; i < 10; i++) { lh.addEntry(data); } BookieSocketAddress replicaToKill = LedgerHandleAdapter.getLedgerMetadata(lh).getEnsembles().get(0L).get(0); LOG.info("Killing Bookie", replicaToKill); killBookie(replicaToKill); int newBkPort = startNewBookie(); for (int i = 0; i < 10; i++) { lh.addEntry(data); } BookieSocketAddress newBkAddr = new BookieSocketAddress(InetAddress.getLocalHost().getHostAddress(), newBkPort); LOG.info("New Bookie addr :" + newBkAddr); ReplicationWorker rw = new ReplicationWorker(zkc, baseConf, newBkAddr); rw.start(); try { BookieServer newBk = bs.get(bs.size() - 1); bsConfs.get(bsConfs.size() - 1).setReadOnlyModeEnabled(true); newBk.getBookie().doTransitionToReadOnlyMode(); underReplicationManager.markLedgerUnderreplicated(lh.getId(), replicaToKill.toString()); while (ReplicationTestUtil.isLedgerInUnderReplication(zkc, lh.getId(), basePath) && rw.isRunning()) { Thread.sleep(100); } assertFalse("RW should shutdown if the bookie is readonly", rw.isRunning()); } finally { rw.shutdown(); } }
/** * Open a read-only LedgerHandle for reading log in no recovery mode from start entry * * @return * @throws BKException * @throws InterruptedException */ public synchronized LedgerCursor getCursor(long startEntry) throws BKException, InterruptedException { if (closed) { LedgerHandle lh = bookKeeper.openLedgerNoRecovery( handle.getId(), DigestType.CRC32, conf.getPassword().getBytes()); long last = lh.getLastAddConfirmed(); return new LedgerCursor(startEntry, last, conf.getCursorBatchSize(), lh); } else { return new LedgerCursor( startEntry, this.handle.getLastAddConfirmed(), conf.getCursorBatchSize(), handle); } }
@Test(timeout = 60000) public void testCloseAllocatorAfterAbort() throws Exception { String allocationPath = "/allocation3"; SimpleLedgerAllocator allocator = createAllocator(allocationPath); allocator.allocate(); ZKTransaction txn = newTxn(); // close during obtaining ledger. LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER)); txn.addOp(DefaultZKOp.of(Op.setData("/unexistedpath", "data".getBytes(UTF_8), -1))); try { FutureUtils.result(txn.execute()); fail("Should fail the transaction when setting unexisted path"); } catch (ZKException ke) { // expected } Utils.close(allocator); byte[] data = zkc.get().getData(allocationPath, false, null); assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8))); // the ledger is not deleted. bkc.get() .openLedger( lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes(UTF_8)); }
/** * Creates a new BookKeeper ledger * * @return The newly BookKeeper ledger * @throws IOException If unable to create the ledger */ public final LedgerHandle createLedger() throws IOException { try { LedgerHandle ledger = bookKeeperClient.createLedger(ENSEMBLE_SIZE, QUORUM_SIZE, DigestType.MAC, LEDGER_PW); createdLedgers.add(ledger); LOG.info("Created a new ledger with id " + ledger.getId()); return ledger; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IOException("Interrupted creating the ledger", e); } catch (BKException e) { throw new IOException("Unrecoverable BookKeeper error", e); } }
@Test(timeout = 60000) public void testLedgerOpenAfterBKCrashed() throws Exception { // Create a ledger LedgerHandle beforelh = bkc.createLedger(numBookies, numBookies, digestType, "".getBytes()); int numEntries = 10; String tmp = "BookKeeper is cool!"; for (int i = 0; i < numEntries; i++) { beforelh.addEntry(tmp.getBytes()); } // shutdown first bookie server killBookie(0); startNewBookie(); // try to open ledger no recovery LedgerHandle afterlh = bkc.openLedger(beforelh.getId(), digestType, "".getBytes()); assertEquals(beforelh.getLastAddPushed(), afterlh.getLastAddConfirmed()); LedgerHandle beforelh2 = bkc.createLedger(numBookies, 1, digestType, "".getBytes()); for (int i = 0; i < numEntries; i++) { beforelh2.addEntry(tmp.getBytes()); } // shutdown first bookie server killBookie(0); // try to open ledger no recovery try { bkc.openLedger(beforelh2.getId(), digestType, "".getBytes()); fail("Should have thrown exception"); } catch (BKException.BKLedgerRecoveryException e) { // correct behaviour } }
/* * (non-Javadoc) * * @see * org.apache.bookkeeper.client.AsyncCallback.ReadCallback#readComplete(int, * org.apache.bookkeeper.client.LedgerHandle, java.util.Enumeration, * java.lang.Object) */ @Override public void readComplete( int rc, LedgerHandle lh, Enumeration<LedgerEntry> entriesEnum, Object ctx) { OpReadEntry opReadEntry = (OpReadEntry) ctx; if (rc != BKException.Code.OK) { log.warn( "[{}] read failed from ledger {} at position:{}", va(name, lh.getId(), opReadEntry.readPosition)); opReadEntry.failed(new ManagedLedgerException(BKException.create(rc))); return; } List<Entry> entries = opReadEntry.entries; while (entriesEnum.hasMoreElements()) entries.add(new EntryImpl(entriesEnum.nextElement())); long lastEntry = entries.get(entries.size() - 1).getPosition().getEntryId(); // Get the "next read position", we need to advance the position taking // care of ledgers boundaries Position nextReadPosition; if (lastEntry < lh.getLastAddConfirmed()) { nextReadPosition = new Position(lh.getId(), lastEntry + 1); } else { // Move to next ledger Long nextLedgerId = ledgers.ceilingKey(lh.getId() + 1); if (nextLedgerId == null) { // We are already in the last ledger nextReadPosition = new Position(lh.getId(), lastEntry + 1); } else { nextReadPosition = new Position(nextLedgerId, 0); } } opReadEntry.nextReadPosition = nextReadPosition; opReadEntry.succeeded(); }
protected synchronized void ledgerClosed(LedgerHandle lh) { checkArgument(lh.getId() == currentLedger.getId()); state = State.ClosedLedger; log.debug( "[{}] Ledger has been closed id={} entries={}", va(name, lh.getId(), lh.getLastAddConfirmed() + 1)); ledgers.put(lh.getId(), new LedgerStat(lh)); trimConsumedLedgersInBackground(); if (!pendingAddEntries.isEmpty()) { // Need to create a new ledger to write pending entries log.debug("[{}] Creating a new ledger", name); state = State.CreatingLedger; bookKeeper.asyncCreateLedger( config.getEnsembleSize(), config.getQuorumSize(), config.getDigestType(), config.getPassword(), this, null); } }
/* * (non-Javadoc) * * @see * org.apache.bookkeeper.client.AsyncCallback.CreateCallback#createComplete * (int, org.apache.bookkeeper.client.LedgerHandle, java.lang.Object) */ @Override public synchronized void createComplete(int rc, LedgerHandle lh, Object ctx) { log.debug("[{}] createComplete rc={} ledger={}", va(name, rc, lh != null ? lh.getId() : -1)); if (rc != BKException.Code.OK) { state = State.ClosedLedger; log.error("[{}] Error creating ledger rc={} {}", va(name, rc, BKException.getMessage(rc))); ManagedLedgerException status = new ManagedLedgerException(BKException.create(rc)); // Empty the list of pending requests and make all of them fail while (!pendingAddEntries.isEmpty()) { pendingAddEntries.poll().failed(status); } } else { log.debug("[{}] Successfully created new ledger {}", name, lh.getId()); ledgers.put(lh.getId(), new LedgerStat(lh.getId(), 0, 0)); currentLedger = lh; currentLedgerEntries = 0; currentLedgerSize = 0; MetaStoreCallback<Void> cb = new MetaStoreCallback<Void>() { public void operationComplete(Void v, Version version) { updateLedgersIdsComplete(version); } public void operationFailed(MetaStoreException e) { log.warn("Error updating meta data with the new list of ledgers"); while (!pendingAddEntries.isEmpty()) { pendingAddEntries.poll().failed(e); } } }; store.asyncUpdateLedgerIds(name, ledgers.values(), ledgersVersion, cb); } }
@Override public synchronized void asyncAddEntry( final byte[] data, final AddEntryCallback callback, final Object ctx) { checkArgument(state != State.None); log.debug("[{}] asyncAddEntry size={} state={}", va(name, data.length, state)); if (state == State.Fenced) { callback.addComplete(new ManagedLedgerFencedException(), null, ctx); return; } OpAddEntry addOperation = new OpAddEntry(this, data, callback, ctx); if (state == State.ClosingLedger || state == State.CreatingLedger) { // We don't have a ready ledger to write into // We are waiting for a new ledger to be created log.debug("[{}] Queue addEntry request", name); pendingAddEntries.add(addOperation); } else if (state == State.ClosedLedger) { // No ledger and no pending operations. Create a new one pendingAddEntries.add(addOperation); log.debug("[{}] Creating a new ledger", name); state = State.CreatingLedger; bookKeeper.asyncCreateLedger( config.getEnsembleSize(), config.getQuorumSize(), config.getDigestType(), config.getPassword(), this, ctx); } else { checkArgument(state == State.LedgerOpened); checkArgument(!currentLedgerIsFull()); // Write into lastLedger log.debug("[{}] Write into current ledger lh={}", name, currentLedger.getId()); addOperation.setLedger(currentLedger); ++currentLedgerEntries; currentLedgerSize += data.length; if (currentLedgerIsFull()) { // This entry will be the last added to current ledger addOperation.setCloseWhenDone(true); state = State.ClosingLedger; } addOperation.initiate(); } }
@Test(timeout = 60000) public void testBadVersionOnTwoAllocators() throws Exception { String allocationPath = "/allocation-bad-version"; zkc.get() .create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = new Stat(); byte[] data = zkc.get().getData(allocationPath, false, stat); Versioned<byte[]> allocationData = new Versioned<byte[]>(data, new ZkVersion(stat.getVersion())); SimpleLedgerAllocator allocator1 = new SimpleLedgerAllocator( allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc); SimpleLedgerAllocator allocator2 = new SimpleLedgerAllocator( allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc); allocator1.allocate(); // wait until allocated ZKTransaction txn1 = newTxn(); LedgerHandle lh = FutureUtils.result(allocator1.tryObtain(txn1, NULL_LISTENER)); allocator2.allocate(); ZKTransaction txn2 = newTxn(); try { FutureUtils.result(allocator2.tryObtain(txn2, NULL_LISTENER)); fail( "Should fail allocating on second allocator as allocator1 is starting allocating something."); } catch (ZKException zke) { assertEquals(KeeperException.Code.BADVERSION, zke.getKeeperExceptionCode()); } FutureUtils.result(txn1.execute()); Utils.close(allocator1); Utils.close(allocator2); long eid = lh.addEntry("hello world".getBytes()); lh.close(); LedgerHandle readLh = bkc.get() .openLedger(lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes()); Enumeration<LedgerEntry> entries = readLh.readEntries(eid, eid); int i = 0; while (entries.hasMoreElements()) { LedgerEntry entry = entries.nextElement(); assertEquals("hello world", new String(entry.getEntry(), UTF_8)); ++i; } assertEquals(1, i); }
/* * (non-Javadoc) * * @see org.apache.bookkeeper.mledger.ManagedLedger#close() */ @Override public synchronized void close() throws InterruptedException, ManagedLedgerException { checkFenced(); for (LedgerHandle ledger : ledgerCache.asMap().values()) { log.debug("Closing ledger: {}", ledger.getId()); try { ledger.close(); } catch (BKException e) { throw new ManagedLedgerException(e); } } ledgerCache.invalidateAll(); log.info("Invalidated {} ledgers in cache", ledgerCache.size()); factory.close(this); }
/* * (non-Javadoc) * * @see org.apache.bookkeeper.mledger.ManagedLedger#openCursor(java. * lang.String) */ @Override public synchronized ManagedCursor openCursor(String cursorName) throws InterruptedException, ManagedLedgerException { checkFenced(); ManagedCursor cursor = cursors.get(cursorName); if (cursor == null) { // Create a new one and persist it Position position = new Position(currentLedger.getId(), currentLedger.getLastAddConfirmed()); cursor = new ManagedCursorImpl(this, cursorName, position); store.updateConsumer(name, cursorName, position); cursors.add(cursor); } log.debug("[{}] Opened new cursor: {}", this.name, cursor); return cursor; }