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);
   }
 }
Exemple #4
0
 /**
  * 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();
    }
  }