/** * @param term the current term * @return a snapshot metadata based on nothing but the term */ public static StateSnapshotMetadata createInitial(int term) { return new StateSnapshotMetadata( INITIAL_LAST_PROCESSED_EVENT_POSITION, INITIAL_LAST_WRITTEN_EVENT_POSITION, term, false); }
@Override public String toString() { return "StateSnapshotMetadata{" + "lastSuccessfulProcessedEventPosition=" + getLastSuccessfulProcessedEventPosition() + ", lastWrittenEventPosition=" + getLastWrittenEventPosition() + ", lastWrittenEventTerm=" + getLastWrittenEventTerm() + ", exists=" + exists() + '}'; }
@Override public boolean equals(Object obj) { if (!(obj instanceof StateSnapshotMetadata)) { return false; } final StateSnapshotMetadata other = (StateSnapshotMetadata) obj; return getLastSuccessfulProcessedEventPosition() == other.getLastSuccessfulProcessedEventPosition() && getLastWrittenEventPosition() == other.getLastWrittenEventPosition() && getLastWrittenEventTerm() == other.getLastWrittenEventTerm(); }
public boolean isInitial() { return getLastSuccessfulProcessedEventPosition() == INITIAL_LAST_PROCESSED_EVENT_POSITION && getLastWrittenEventPosition() == INITIAL_LAST_WRITTEN_EVENT_POSITION; }
private boolean validateSnapshot(final StateSnapshotMetadata metadata) { final boolean wasFound = logStreamReader.seek(metadata.getLastWrittenEventPosition()); boolean isValid = false; if (wasFound && logStreamReader.hasNext()) { final LoggedEvent event = logStreamReader.next(); isValid = event.getRaftTerm() == metadata.getLastWrittenEventTerm(); } return isValid; }
private long recoverFromSnapshot(final long commitPosition, final int term) throws Exception { final StateSnapshotMetadata recovered = snapshotController.recover(commitPosition, term, this::validateSnapshot); final long snapshotPosition = recovered.getLastSuccessfulProcessedEventPosition(); logStreamReader.seekToFirstEvent(); // reset seek position if (!recovered.isInitial()) { final boolean found = logStreamReader.seek(snapshotPosition); if (found && logStreamReader.hasNext()) { logStreamReader.seek(snapshotPosition + 1); } else { throw new IllegalStateException( String.format(ERROR_MESSAGE_RECOVER_FROM_SNAPSHOT_FAILED, getName())); } snapshotController.purgeAllExcept(recovered); } return snapshotPosition; }
/** * Purges old and invalid snapshots. Should be done once we are sure we can discard other * snapshots, e.g. during recovery. * * @param metadata last taken/recovered snapshot metadata */ default void purgeAllExcept(StateSnapshotMetadata metadata) throws Exception { purgeAll(s -> !s.equals(metadata)); } }
public List<StateSnapshotMetadata> listRecoverable(long lastSuccessfulProcessedEventPosition) { return list(s -> s.getLastWrittenEventPosition() <= lastSuccessfulProcessedEventPosition); }
private void writeSnapshot(final StateSnapshotMetadata metadata) { final long start = System.currentTimeMillis(); final String name = streamProcessorContext.getName(); LOG.info( "Write snapshot for stream processor {} at event position {}.", name, metadata.getLastSuccessfulProcessedEventPosition()); try { snapshotController.takeSnapshot(metadata); final long snapshotCreationTime = System.currentTimeMillis() - start; LOG.info("Creation of snapshot {} took {} ms.", name, snapshotCreationTime); metrics.recordSnapshotCreationTime(snapshotCreationTime); snapshotPosition = lastSuccessfulProcessedEventPosition; } catch (final Exception e) { LOG.error("Stream processor '{}' failed. Can not write snapshot.", getName(), e); } }
@Override public StateSnapshotMetadata recover( long commitPosition, int term, Predicate<StateSnapshotMetadata> filter) throws Exception { final File runtimeDirectory = storage.getRuntimeDirectory(); final List<StateSnapshotMetadata> snapshots = storage.listRecoverable(commitPosition); StateSnapshotMetadata recoveredMetadata = null; if (!snapshots.isEmpty()) { recoveredMetadata = snapshots.stream() .sorted(Comparator.reverseOrder()) .filter(filter) .findFirst() .orElse(null); } if (runtimeDirectory.exists()) { FileUtil.deleteFolder(runtimeDirectory.getAbsolutePath()); } if (recoveredMetadata != null) { final File snapshotPath = storage.getSnapshotDirectoryFor(recoveredMetadata); copySnapshot(runtimeDirectory, snapshotPath); } else { recoveredMetadata = StateSnapshotMetadata.createInitial(term); } return recoveredMetadata; }
@Override public boolean equals(Object obj) { if (!(obj instanceof StateSnapshotMetadata)) { return false; } final StateSnapshotMetadata other = (StateSnapshotMetadata) obj; return getLastSuccessfulProcessedEventPosition() == other.getLastSuccessfulProcessedEventPosition() && getLastWrittenEventPosition() == other.getLastWrittenEventPosition() && getLastWrittenEventTerm() == other.getLastWrittenEventTerm(); }
private boolean validateSnapshot(final StateSnapshotMetadata metadata) { final boolean wasFound = logStreamReader.seek(metadata.getLastWrittenEventPosition()); boolean isValid = false; if (wasFound && logStreamReader.hasNext()) { final LoggedEvent event = logStreamReader.next(); isValid = event.getRaftTerm() == metadata.getLastWrittenEventTerm(); } return isValid; }
public boolean isInitial() { return getLastSuccessfulProcessedEventPosition() == INITIAL_LAST_PROCESSED_EVENT_POSITION && getLastWrittenEventPosition() == INITIAL_LAST_WRITTEN_EVENT_POSITION; }
private long recoverFromSnapshot(final long commitPosition, final int term) throws Exception { final StateSnapshotMetadata recovered = snapshotController.recover(commitPosition, term, this::validateSnapshot); final long snapshotPosition = recovered.getLastSuccessfulProcessedEventPosition(); logStreamReader.seekToFirstEvent(); // reset seek position if (!recovered.isInitial()) { final boolean found = logStreamReader.seek(snapshotPosition); if (found && logStreamReader.hasNext()) { logStreamReader.seek(snapshotPosition + 1); } else { throw new IllegalStateException( String.format(ERROR_MESSAGE_RECOVER_FROM_SNAPSHOT_FAILED, getName())); } snapshotController.purgeAllExcept(recovered); } return snapshotPosition; }
/** * Purges old and invalid snapshots. Should be done once we are sure we can discard other * snapshots, e.g. during recovery. * * @param metadata last taken/recovered snapshot metadata */ default void purgeAllExcept(StateSnapshotMetadata metadata) throws Exception { purgeAll(s -> !s.equals(metadata)); } }
public List<StateSnapshotMetadata> listRecoverable(long lastSuccessfulProcessedEventPosition) { return list(s -> s.getLastWrittenEventPosition() <= lastSuccessfulProcessedEventPosition); }
private void writeSnapshot(final StateSnapshotMetadata metadata) { final long start = System.currentTimeMillis(); final String name = streamProcessorContext.getName(); LOG.info( "Write snapshot for stream processor {} at event position {}.", name, metadata.getLastSuccessfulProcessedEventPosition()); try { snapshotController.takeSnapshot(metadata); final long snapshotCreationTime = System.currentTimeMillis() - start; LOG.info("Creation of snapshot {} took {} ms.", name, snapshotCreationTime); metrics.recordSnapshotCreationTime(snapshotCreationTime); snapshotPosition = lastSuccessfulProcessedEventPosition; } catch (final Exception e) { LOG.error("Stream processor '{}' failed. Can not write snapshot.", getName(), e); } }
@Override public StateSnapshotMetadata recover( long commitPosition, int term, Predicate<StateSnapshotMetadata> filter) throws Exception { final File runtimeDirectory = storage.getRuntimeDirectory(); final List<StateSnapshotMetadata> snapshots = storage.listRecoverable(commitPosition); StateSnapshotMetadata recoveredMetadata = null; if (!snapshots.isEmpty()) { recoveredMetadata = snapshots.stream() .sorted(Comparator.reverseOrder()) .filter(filter) .findFirst() .orElse(null); } if (runtimeDirectory.exists()) { FileUtil.deleteFolder(runtimeDirectory.getAbsolutePath()); } if (recoveredMetadata != null) { final File snapshotPath = storage.getSnapshotDirectoryFor(recoveredMetadata); copySnapshot(runtimeDirectory, snapshotPath); } else { recoveredMetadata = StateSnapshotMetadata.createInitial(term); } return recoveredMetadata; }
@Override public String toString() { return "StateSnapshotMetadata{" + "lastSuccessfulProcessedEventPosition=" + getLastSuccessfulProcessedEventPosition() + ", lastWrittenEventPosition=" + getLastWrittenEventPosition() + ", lastWrittenEventTerm=" + getLastWrittenEventTerm() + ", exists=" + exists() + '}'; }
@Override public int compareTo(StateSnapshotMetadata o) { int result = Long.compare( getLastSuccessfulProcessedEventPosition(), o.getLastSuccessfulProcessedEventPosition()); if (result == 0) { result = Integer.compare(getLastWrittenEventTerm(), o.getLastWrittenEventTerm()); if (result == 0) { result = Long.compare(getLastWrittenEventPosition(), o.getLastWrittenEventPosition()); } } return result; } }