/** * Returns a LogMetadata class with the exact contents of this instance, but the enabled flag set to false. No changes * are performed on this instance. * * @return This instance, if isEnabled() == false, of a new instance of the LogMetadata class which will have * isEnabled() == false, otherwise. */ LogMetadata asDisabled() { return this.enabled ? new LogMetadata(this.epoch, false, this.ledgers, this.truncationAddress, this.updateVersion.get()) : this; }
/** * Returns a LogMetadata class with the exact contents of this instance, but the enabled flag set to true. No changes * are performed on this instance. * * @return This instance, if isEnabled() == true, of a new instance of the LogMetadata class which will have * isEnabled() == true, otherwise. */ LogMetadata asEnabled() { return this.enabled ? this : new LogMetadata(this.epoch, true, this.ledgers, this.truncationAddress, this.updateVersion.get()); }
/** * Creates a new instance of the LogMetadata class which contains all the ledgers after (and including) the given address. * * @param upToAddress The address to truncate to. * @return A new instance of the LogMetadata class. */ LogMetadata truncate(LedgerAddress upToAddress) { Preconditions.checkState(this.enabled, "Log is not enabled. Cannot perform any modifications on it."); // Exclude all those Ledgers that have a LedgerId less than the one we are given. An optimization to this would // involve trimming out the ledger which has a matching ledger id and the entry is is the last one, but that would // involve opening the Ledger in BookKeeper and inspecting it, which would take too long. val newLedgers = this.ledgers.stream().filter(lm -> lm.getLedgerId() >= upToAddress.getLedgerId()).collect(Collectors.toList()); return new LogMetadata(this.epoch, this.enabled, Collections.unmodifiableList(newLedgers), upToAddress, this.updateVersion.get()); }
/** * Creates a new instance of the LogMetadata class which contains an additional ledger. * * @param ledgerId The Id of the Ledger to add. * @return A new instance of the LogMetadata class. */ LogMetadata addLedger(long ledgerId) { Preconditions.checkState(this.enabled, "Log is not enabled. Cannot perform any modifications on it."); // Copy existing ledgers. List<LedgerMetadata> newLedgers = new ArrayList<>(this.ledgers.size() + 1); newLedgers.addAll(this.ledgers); // Create and add metadata for the new ledger. int sequence = this.ledgers.size() == 0 ? INITIAL_LEDGER_SEQUENCE : this.ledgers.get(this.ledgers.size() - 1).getSequence() + 1; newLedgers.add(new LedgerMetadata(ledgerId, sequence)); return new LogMetadata(this.epoch + 1, this.enabled, Collections.unmodifiableList(newLedgers), this.truncationAddress, this.updateVersion.get()); }
/** * Removes LedgerMetadata instances for those Ledgers that are known to be empty. * * @param skipCountFromEnd The number of Ledgers to spare, counting from the end of the LedgerMetadata list. * @return A new instance of LogMetadata with the updated ledger list. */ LogMetadata removeEmptyLedgers(int skipCountFromEnd) { val newLedgers = new ArrayList<LedgerMetadata>(); int cutoffIndex = this.ledgers.size() - skipCountFromEnd; for (int i = 0; i < cutoffIndex; i++) { LedgerMetadata lm = this.ledgers.get(i); if (lm.getStatus() != LedgerMetadata.Status.Empty) { // Not Empty or Unknown: keep it! newLedgers.add(lm); } } // Add the ones from the end, as instructed. for (int i = cutoffIndex; i < this.ledgers.size(); i++) { newLedgers.add(this.ledgers.get(i)); } return new LogMetadata(this.epoch, this.enabled, Collections.unmodifiableList(newLedgers), this.truncationAddress, this.updateVersion.get()); }
/** * Updates the LastAddConfirmed on individual LedgerMetadata instances based on the provided argument. * * @param lastAddConfirmed A Map of LedgerId to LastAddConfirmed based on which we can update the status. * @return This (unmodified) instance if lastAddConfirmed.isEmpty() or a new instance of the LogMetadata class with * the updated LedgerMetadata instances. */ LogMetadata updateLedgerStatus(Map<Long, Long> lastAddConfirmed) { if (lastAddConfirmed.isEmpty()) { // Nothing to change. return this; } val newLedgers = this.ledgers.stream() .map(lm -> { long lac = lastAddConfirmed.getOrDefault(lm.getLedgerId(), Long.MIN_VALUE); if (lm.getStatus() == LedgerMetadata.Status.Unknown && lac != Long.MIN_VALUE) { LedgerMetadata.Status e = lac == Ledgers.NO_ENTRY_ID ? LedgerMetadata.Status.Empty : LedgerMetadata.Status.NotEmpty; lm = new LedgerMetadata(lm.getLedgerId(), lm.getSequence(), e); } return lm; }) .collect(Collectors.toList()); return new LogMetadata(this.epoch, this.enabled, Collections.unmodifiableList(newLedgers), this.truncationAddress, this.updateVersion.get()); }
if (create) { currentMetadata = new LogMetadata(newLedger.getId()); } else { currentMetadata = currentMetadata.addLedger(newLedger.getId());
expectedLedgerIds.add(ledgerId); if (metadata == null) { metadata = new LogMetadata(ledgerId).withUpdateVersion(i); } else { metadata.withUpdateVersion(i);
public void testGetNextAddress() { val empty = new LogMetadata(1).truncate(new LedgerAddress(2, 2, 2)); Assert.assertNull("Unexpected result from empty metadata", empty.getNextAddress(new LedgerAddress(1, 1, 0), Long.MAX_VALUE)); long ledgerId = nextLedgerId.get() * 2; if (m == null) { m = new LogMetadata(ledgerId).withUpdateVersion(i); } else { m.withUpdateVersion(i);
/** * Tests serialization/deserialization. */ @Test(timeout = 5000) public void testSerialization() throws Exception { Supplier<Long> nextLedgerId = new AtomicLong()::incrementAndGet; LogMetadata m1 = null; val lacs = new HashMap<Long, Long>(); for (int i = 0; i < LEDGER_COUNT; i++) { long ledgerId = nextLedgerId.get() * 2; if (m1 == null) { m1 = new LogMetadata(ledgerId).withUpdateVersion(i); } else { m1 = m1.addLedger(ledgerId).withUpdateVersion(i); } if (i % 2 == 0) { // Every other Ledger, update the LastAddConfirmed. lacs.put((long) i, (long) i + 1); } } m1 = m1.updateLedgerStatus(lacs); val serialization = LogMetadata.SERIALIZER.serialize(m1); val m2 = LogMetadata.SERIALIZER.deserialize(serialization); Assert.assertEquals("Unexpected epoch.", m1.getEpoch(), m2.getEpoch()); Assert.assertEquals("Unexpected TruncationAddress.", m1.getTruncationAddress().getSequence(), m2.getTruncationAddress().getSequence()); Assert.assertEquals("Unexpected TruncationAddress.", m1.getTruncationAddress().getLedgerId(), m2.getTruncationAddress().getLedgerId()); AssertExtensions.assertListEquals("Unexpected ledgers.", m1.getLedgers(), m2.getLedgers(), (l1, l2) -> l1.getSequence() == l2.getSequence() && l1.getLedgerId() == l2.getLedgerId() && l1.getStatus() == l2.getStatus()); }