/** * 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); }
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; } } }
/** * 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(); } }
protected synchronized long getNumberOfEntries(Position position) { long count = 0; // First count the number of unread entries in the ledger pointed by // position if (position.getLedgerId() >= 0) count += ledgers.get(position.getLedgerId()).getEntriesCount() - position.getEntryId(); // Then, recur all the next ledgers and sum all the entries they contain for (LedgerStat ls : ledgers.tailMap(position.getLedgerId(), false).values()) { count += ls.getEntriesCount(); } // Last add the entries in the current ledger if (state != State.ClosedLedger) { count += currentLedger.getLastAddConfirmed() + 1; } return count; }
/** * 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); } }
/** * 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); } }