private void initializeBookKeeper(final ManagedLedgerCallback<Void> callback) { log.debug("[{}] initializing bookkeeper; ledgers {}", name, ledgers); final MetaStoreCallback<Void> storeLedgersCb = new MetaStoreCallback<Void>() { public void operationComplete(Void v, Version version) { ledgersVersion = version; initializeCursors(callback); } public void operationFailed(MetaStoreException e) { callback.operationFailed(new ManagedLedgerException(e)); } }; // Create a new ledger to start writing bookKeeper.asyncCreateLedger( config.getEnsembleSize(), config.getQuorumSize(), config.getDigestType(), config.getPassword(), new CreateCallback() { public void createComplete(int rc, LedgerHandle lh, Object ctx) { if (rc == BKException.Code.OK) { state = State.LedgerOpened; currentLedger = lh; ledgers.put(currentLedger.getId(), new LedgerStat(currentLedger.getId(), 0, 0)); // Save it back to ensure all nodes exist store.asyncUpdateLedgerIds(name, ledgers.values(), ledgersVersion, storeLedgersCb); } else { callback.operationFailed(new ManagedLedgerException(BKException.create(rc))); } } }, null); }
@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()); } } }
/** * Open an existing ledger for either reading or recovery and writing. * * @param ledgerId Id of the ledger to open * @param fence If true the ledger will be fenced and may be recovered, otherwise the ledger is * opened in read-only mode. * @return Handle of the opened ledger * @throws IOException If unable to open the specified ledger */ public static LedgerHandle openLedger(long ledgerId, boolean fence) throws IOException { String shouldFenceDesc = ", with fencing " + (fence ? "enabled" : "disabled") + "."; LOG.info("Trying to open ledger id " + ledgerId + shouldFenceDesc); try { LedgerHandle retVal; if (fence) { retVal = bookKeeperClient.openLedger(ledgerId, DigestType.MAC, LEDGER_PW); } else { retVal = bookKeeperClient.openLedgerNoRecovery(ledgerId, DigestType.MAC, LEDGER_PW); } LOG.info("Opened ledger id " + ledgerId + shouldFenceDesc); return retVal; } catch (BKException e) { throw new IOException("Unrecoverable BookKeeper error opening the ledger", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IOException("Interrupted opening the ledger", e); } }
/** * 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); } }
@AfterClass public static void shutDownStatic() throws Exception { try { if (bookKeeperClient != null) { bookKeeperClient.close(); } } finally { if (bookKeeperCluster != null) { bookKeeperCluster.shutdown(); } MiniAvatarCluster.shutDownZooKeeper(); } }
/** * 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); } }
@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(); } }
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(); } }
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); }
/** * Delete this ManagedLedger completely from the system. * * @throws Exception */ protected void delete() throws InterruptedException, ManagedLedgerException { close(); synchronized (this) { checkFenced(); try { for (LedgerStat ls : ledgers.values()) { log.debug("[{}] Deleting ledger {}", name, ls); try { bookKeeper.deleteLedger(ls.getLedgerId()); } catch (BKNoSuchLedgerExistsException e) { log.warn("[{}] Ledger {} not found when deleting it", name, ls.getLedgerId()); } } } catch (BKException e) { throw new ManagedLedgerException(e); } store.removeManagedLedger(name); } }
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); } }
/** * Checks whether there are ledger that have been fully consumed and deletes them * * @throws Exception */ protected void internalTrimConsumedLedgers() { // Ensure only one trimming operation is active List<LedgerStat> ledgersToDelete = Lists.newArrayList(); synchronized (this) { long slowestReaderLedgerId = -1; if (cursors.isEmpty() && currentLedger != null) { // At this point the lastLedger will be pointing to the // ledger that has just been closed, therefore the +1 to // include lastLedger in the trimming. slowestReaderLedgerId = currentLedger.getId() + 1; } else { slowestReaderLedgerId = cursors.getSlowestReaderPosition().getLedgerId(); } for (LedgerStat ls : ledgers.headMap(slowestReaderLedgerId, false).values()) { ledgersToDelete.add(ls); ledgerCache.invalidate(ls.getLedgerId()); } if (ledgersToDelete.isEmpty()) { return; } } // Delete the ledgers _without_ holding the lock on 'this' long removedCount = 0; long removedSize = 0; for (LedgerStat ls : ledgersToDelete) { log.info("[{}] Removing ledger {}", name, ls.getLedgerId()); try { bookKeeper.deleteLedger(ls.getLedgerId()); ++removedCount; removedSize += ls.getSize(); } catch (BKNoSuchLedgerExistsException e) { log.warn("[{}] Ledger was already deleted {}", name, ls.getLedgerId()); } catch (Exception e) { log.error("[{}] Error deleting ledger {}", name, ls.getLedgerId()); return; } } // Update metadata try { synchronized (this) { numberOfEntries.addAndGet(-removedCount); totalSize.addAndGet(-removedSize); for (LedgerStat ls : ledgersToDelete) { ledgers.remove(ls.getLedgerId()); } if (state == State.CreatingLedger) { // The list of ledgers is being modified asynchronously, we // cannot update it now. In case of a client crash, this // will just result in some ledgers to be deleted twice, // without any side consequences. log.info("[{}] Skipped updating ledger list for concurrent modification", name); return; } ledgersVersion = store.updateLedgersIds(name, ledgers.values(), ledgersVersion); } } catch (MetaStoreException e) { log.error("[{}] Failed to update the list of ledgers after trimming", name, e); } }
@Test public void testSyncReadAsyncWriteStringsSingleClient() throws IOException { LOG.info("TEST READ WRITE STRINGS MIXED SINGLE CLIENT"); String charset = "utf-8"; LOG.debug("Default charset: " + Charset.defaultCharset()); 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++) { int randomInt = rng.nextInt(maxInt); byte[] entry = new String(Integer.toString(randomInt)).getBytes(charset); entries.add(entry); bkc.asyncAddEntry(lh, entry, this, sync); } // wait for all entries to be acknowledged synchronized (sync) { if (sync.counter < numEntriesToWrite) { LOG.debug("Entries counter = " + sync.counter); sync.wait(); } } LOG.debug("*** ASYNC WRITE COMPLETED ***"); // close ledger bkc.closeLedger(lh); // *** WRITING PART COMPLETED // READ PART BEGINS *** // open ledger lh = bkc.openLedger(ledgerId, ledgerPassword); LOG.debug("Number of entries written: " + lh.getLast()); assertTrue("Verifying number of entries written", lh.getLast() == numEntriesToWrite); // read entries ls = bkc.readEntries(lh, 0, numEntriesToWrite - 1); assertTrue("Checking number of read entries", ls.size() == numEntriesToWrite); LOG.debug("*** SYNC READ COMPLETED ***"); // at this point, LedgerSequence ls is filled with the returned values int i = 0; while (ls.hasMoreElements()) { byte[] origEntryBytes = entries.get(i++); byte[] retrEntryBytes = ls.nextElement().getEntry(); LOG.debug("Original byte entry size: " + origEntryBytes.length); LOG.debug("Saved byte entry size: " + retrEntryBytes.length); String origEntry = new String(origEntryBytes, charset); String retrEntry = new String(retrEntryBytes, charset); LOG.debug("Original entry: " + origEntry); 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(); } }
@Test public void testReadWriteAsyncSingleClient() 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()); entriesSize.add(entry.array().length); bkc.asyncAddEntry(lh, entry.array(), this, sync); } // wait for all entries to be acknowledged synchronized (sync) { if (sync.counter < numEntriesToWrite) { LOG.debug("Entries counter = " + sync.counter); sync.wait(); } } LOG.debug("*** WRITE COMPLETED ***"); // close ledger bkc.closeLedger(lh); // *** WRITING PART COMPLETED // READ PART BEGINS *** // open ledger lh = bkc.openLedger(ledgerId, ledgerPassword); LOG.debug("Number of entries written: " + lh.getLast()); assertTrue("Verifying number of entries written", lh.getLast() == numEntriesToWrite); // read entries bkc.asyncReadEntries(lh, 0, numEntriesToWrite - 1, this, (Object) sync); synchronized (sync) { while (sync.value == false) { sync.wait(); } } assertTrue("Checking number of read entries", ls.size() == numEntriesToWrite); LOG.debug("*** READ COMPLETED ***"); // at this point, LedgerSequence ls is filled with the returned values int i = 0; while (ls.hasMoreElements()) { ByteBuffer origbb = ByteBuffer.wrap(entries.get(i)); Integer origEntry = origbb.getInt(); byte[] entry = ls.nextElement().getEntry(); ByteBuffer result = ByteBuffer.wrap(entry); 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)); assertTrue( "Checking entry " + i + " for size", entry.length == entriesSize.get(i).intValue()); i++; } bkc.closeLedger(lh); } catch (KeeperException e) { e.printStackTrace(); } catch (BKException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }