@Test public void simpleTest() { PositionImpl pos = new PositionImpl(1, 2); assertEquals(pos.getLedgerId(), 1); assertEquals(pos.getEntryId(), 2); assertEquals(pos, new PositionImpl(1, 2)); assertFalse(pos.equals(new PositionImpl(1, 3))); assertFalse(pos.equals(new PositionImpl(3, 2))); assertFalse(pos.equals("1:2")); }
PositionImpl getNextValidPosition(final PositionImpl position) { PositionImpl nextPosition = position.getNext(); while (!isValidPosition(nextPosition)) { Long nextLedgerId = ledgers.ceilingKey(nextPosition.getLedgerId() + 1); if (nextLedgerId == null) { return null; } nextPosition = PositionImpl.get(nextLedgerId.longValue(), 0); } return nextPosition; }
/** * Checks given position is part of deleted-range and returns next position of upper-end as all the messages are * deleted up to that point. * * @param position * @return next available position */ public PositionImpl getNextAvailablePosition(PositionImpl position) { Range<PositionImpl> range = individualDeletedMessages.rangeContaining(position); if (range != null) { PositionImpl nextPosition = range.upperEndpoint().getNext(); return (nextPosition != null && nextPosition.compareTo(position) > 0) ? nextPosition : position.getNext(); } return position.getNext(); }
PositionImpl startReadOperationOnLedger(PositionImpl position) { long ledgerId = ledgers.ceilingKey(position.getLedgerId()); if (ledgerId != position.getLedgerId()) { // The ledger pointed by this position does not exist anymore. It was deleted because it was empty. We need // to skip on the next available ledger position = new PositionImpl(ledgerId, 0); } return position; }
@Test(timeOut = 20000) void seekPosition() throws Exception { ManagedLedger ledger = factory.open("my_test_ledger", new ManagedLedgerConfig().setMaxEntriesPerLedger(10)); ManagedCursor cursor = ledger.openCursor("c1"); ledger.addEntry("dummy-entry-1".getBytes(Encoding)); ledger.addEntry("dummy-entry-2".getBytes(Encoding)); ledger.addEntry("dummy-entry-3".getBytes(Encoding)); PositionImpl lastPosition = (PositionImpl) ledger.addEntry("dummy-entry-4".getBytes(Encoding)); cursor.seek(new PositionImpl(lastPosition.getLedgerId(), lastPosition.getEntryId() - 1)); }
if (newMarkDeletePosition.compareTo(markDeletePosition) < 0) { throw new IllegalArgumentException("Mark deleting an already mark-deleted position"); if (!newMarkDeletePosition.equals(oldMarkDeletePosition)) { long skippedEntries = 0; if (newMarkDeletePosition.getLedgerId() == oldMarkDeletePosition.getLedgerId() && newMarkDeletePosition.getEntryId() == oldMarkDeletePosition.getEntryId() + 1) { markDeletePosition = PositionImpl.get(newMarkDeletePosition); individualDeletedMessages.remove(Range.atMost(markDeletePosition)); if (readPosition.compareTo(newMarkDeletePosition) <= 0) {
public static EntryImpl create(PositionImpl position, ByteBuf data) { EntryImpl entry = RECYCLER.get(); entry.ledgerId = position.getLedgerId(); entry.entryId = position.getEntryId(); entry.data = data; entry.data.retain(); entry.setRefCnt(1); return entry; }
private void internalReadFromLedger(ReadHandle ledger, OpReadEntry opReadEntry) { long firstEntry = opReadEntry.readPosition.getEntryId(); long lastEntryInLedger; final ManagedCursorImpl cursor = opReadEntry.cursor; if (ledger.getId() == lastPosition.getLedgerId()) { lastEntryInLedger = lastPosition.getEntryId(); } else { opReadEntry.updateReadPosition(new PositionImpl(nextLedgerId, 0)); } else { opReadEntry.updateReadPosition(new PositionImpl(ledger.getId() + 1, 0)); final PositionImpl lastReadPosition = PositionImpl.get(ledger.getId(), lastEntry); discardEntriesFromCache(cursor, lastReadPosition);
@Test public void hashes() throws Exception { PositionImpl p1 = new PositionImpl(5, 15); PositionImpl p2 = new PositionImpl(PositionInfo.parseFrom(p1.getPositionInfo().toByteArray())); assertEquals(p2.getLedgerId(), 5); assertEquals(p2.getEntryId(), 15); assertEquals(new PositionImpl(5, 15).hashCode(), p2.hashCode()); } }
/** * Get the entry position that come before the specified position in the message stream, using information from the * ledger list and each ledger entries count. * * @param position * the current position * @return the previous position */ PositionImpl getPreviousPosition(PositionImpl position) { if (position.getEntryId() > 0) { return PositionImpl.get(position.getLedgerId(), position.getEntryId() - 1); } // The previous position will be the last position of an earlier ledgers NavigableMap<Long, LedgerInfo> headMap = ledgers.headMap(position.getLedgerId(), false); if (headMap.isEmpty()) { // There is no previous ledger, return an invalid position in the current ledger return PositionImpl.get(position.getLedgerId(), -1); } // We need to find the most recent non-empty ledger for (long ledgerId : headMap.descendingKeySet()) { LedgerInfo li = headMap.get(ledgerId); if (li.getEntries() > 0) { return PositionImpl.get(li.getLedgerId(), li.getEntries() - 1); } } // in case there are only empty ledgers, we return a position in the first one return PositionImpl.get(headMap.firstEntry().getKey(), -1); }
public MarkDeleteEntry(PositionImpl newPosition, Map<String, Long> properties, MarkDeleteCallback callback, Object ctx) { this.newPosition = PositionImpl.get(newPosition); this.properties = properties; this.callback = callback; this.ctx = ctx; } }
private void recoveredCursor(PositionImpl position, Map<String, Long> properties, LedgerHandle recoveredFromCursorLedger) { // if the position was at a ledger that didn't exist (since it will be deleted if it was previously empty), // we need to move to the next existing ledger if (!ledger.ledgerExists(position.getLedgerId())) { Long nextExistingLedger = ledger.getNextValidLedger(position.getLedgerId()); if (nextExistingLedger == null) { log.info("[{}] [{}] Couldn't find next next valid ledger for recovery {}", ledger.getName(), name, position); } position = nextExistingLedger != null ? PositionImpl.get(nextExistingLedger, -1) : position; } if (position.compareTo(ledger.getLastPosition()) > 0) { log.warn("[{}] [{}] Current position {} is ahead of last position {}", ledger.getName(), name, position, ledger.getLastPosition()); position = PositionImpl.get(ledger.getLastPosition()); } log.info("[{}] Cursor {} recovered to position {}", ledger.getName(), name, position); messagesConsumedCounter = -getNumberOfEntries(Range.openClosed(position, ledger.getLastPosition())); markDeletePosition = position; readPosition = ledger.getNextValidPosition(position); lastMarkDeleteEntry = new MarkDeleteEntry(markDeletePosition, properties, null, null); // assign cursor-ledger so, it can be deleted when new ledger will be switched this.cursorLedger = recoveredFromCursorLedger; STATE_UPDATER.set(this, State.NoLedger); }
@Override public PositionImpl getPosition() { return new PositionImpl(ledgerId, entryId); }
public ReadOnlyCursorImpl(BookKeeper bookkeeper, ManagedLedgerConfig config, ManagedLedgerImpl ledger, PositionImpl startPosition, String cursorName) { super(bookkeeper, config, ledger, cursorName); if (startPosition.equals(PositionImpl.earliest)) { readPosition = ledger.getFirstPosition().getNext(); } else { readPosition = startPosition; } if (ledger.getLastPosition().compareTo(readPosition) <= 0) { messagesConsumedCounter = 0; } else { messagesConsumedCounter = -getNumberOfEntries(Range.closed(readPosition, ledger.getLastPosition())); } this.state = State.NoLedger; }
/** * If we fail to recover the cursor ledger, we want to still open the ML and rollback. * * @param info */ private PositionImpl getRollbackPosition(ManagedCursorInfo info) { PositionImpl firstPosition = ledger.getFirstPosition(); PositionImpl snapshottedPosition = new PositionImpl(info.getMarkDeleteLedgerId(), info.getMarkDeleteEntryId()); if (firstPosition == null) { // There are no ledgers in the ML, any position is good return snapshottedPosition; } else if (snapshottedPosition.compareTo(firstPosition) < 0) { // The snapshotted position might be pointing to a ledger that was already deleted return firstPosition; } else { return snapshottedPosition; } }
void updateCursor(ManagedCursorImpl cursor, PositionImpl newPosition) { Pair<PositionImpl, PositionImpl> pair = cursors.cursorUpdated(cursor, newPosition); if (pair == null) { // Cursor has been removed in the meantime trimConsumedLedgersInBackground(); return; } PositionImpl previousSlowestReader = pair.getLeft(); PositionImpl currentSlowestReader = pair.getRight(); if (previousSlowestReader.compareTo(currentSlowestReader) == 0) { // The slowest consumer has not changed position. Nothing to do right now return; } // Only trigger a trimming when switching to the next ledger if (previousSlowestReader.getLedgerId() != newPosition.getLedgerId()) { trimConsumedLedgersInBackground(); } }
@Override public boolean hasMoreEntries() { // If writer and reader are on the same ledger, we just need to compare the entry id to know if we have more // entries. // If they are on different ledgers we have 2 cases : // * Writer pointing to valid entry --> should return true since we have available entries // * Writer pointing to "invalid" entry -1 (meaning no entries in that ledger) --> Need to check if the reader // is // at the last entry in the previous ledger PositionImpl writerPosition = ledger.getLastPosition(); if (writerPosition.getEntryId() != -1) { return readPosition.compareTo(writerPosition) <= 0; } else { // Fall back to checking the number of entries to ensure we are at the last entry in ledger and no ledgers // are in the middle return getNumberOfEntries() > 0; } }
boolean hasMoreEntries(PositionImpl position) { PositionImpl lastPos = lastConfirmedEntry; boolean result = position.compareTo(lastPos) <= 0; if (log.isDebugEnabled()) { log.debug("[{}] hasMoreEntries: pos={} lastPos={} res={}", name, position, lastPos, result); } return result; }
@Override public void operationFailed(ManagedLedgerException exception) { synchronized (pendingMarkDeleteOps) { if (!RESET_CURSOR_IN_PROGRESS_UPDATER.compareAndSet(ManagedCursorImpl.this, TRUE, FALSE)) { log.error("[{}] expected reset position [{}], but another reset in progress on cursor {}", ledger.getName(), newPosition, name); } } callback.resetFailed(new ManagedLedgerException.InvalidCursorPositionException( "unable to persist position for cursor reset " + newPosition.toString()), newPosition); }
NonDurableCursorImpl(BookKeeper bookkeeper, ManagedLedgerConfig config, ManagedLedgerImpl ledger, String cursorName, PositionImpl startCursorPosition) { super(bookkeeper, config, ledger, cursorName); // Compare with "latest" position marker by using only the ledger id. Since the C++ client is using 48bits to // store the entryId, it's not able to pass a Long.max() as entryId. In this case there's no point to require // both ledgerId and entryId to be Long.max() if (startCursorPosition == null || startCursorPosition.getLedgerId() == PositionImpl.latest.getLedgerId()) { // Start from last entry initializeCursorPosition(ledger.getLastPositionAndCounter()); } else if (startCursorPosition.equals(PositionImpl.earliest)) { // Start from invalid ledger to read from first available entry recoverCursor(ledger.getPreviousPosition(ledger.getFirstPosition())); } else { // Since the cursor is positioning on the mark-delete position, we need to take 1 step back from the desired // read-position recoverCursor(startCursorPosition); } log.info("[{}] Created non-durable cursor read-position={} mark-delete-position={}", ledger.getName(), readPosition, markDeletePosition); }