Class RaftLog
Entries are 1-indexed (index 0 is a sentinel). The log supports: - append: add new entries at the end - truncate: remove entries from a given index (for conflict resolution) - getEntry: retrieve by index - getEntries: retrieve a range (for AppendEntries batching) - snapshot/compaction: trim prefix when snapshot is taken
Thread-safe via ReadWriteLock.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordImmutable pair of (index, term) from the Raft log. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionlongappend(long term, byte[] command) Append a new entry to the log.voidAppend multiple entries to the log (batch append).longappendEntry(LogEntry entry) Append a pre-built LogEntry to the log.voidcompactUpTo(long snapshotIndex, long snapshotTerm) Compact the log by removing all entries up to and including snapshotIndex.booleancontainsEntry(long index, long term) Check if an entry with the given index and term exists in the log.longEstimate the memory usage of the log in bytes.longfindConflict(List<LogEntry> entries) Find the first conflicting entry when applying a list of new entries.getEntriesFrom(long startIndex) Get all entries from a starting index to the end of the log (inclusive).@Nullable LogEntrygetEntry(long index) Get a log entry at a specific index.getEntryRange(long fromIndex, long toIndex) Retrieve a range of entries from the log.longGet the index of the last entry in the log.Atomically get the last index and term as a pair.longGet the term of the last entry in the log.Get a snapshot of current log statistics.longGet the index of the last snapshot.longGet the term of the last snapshot.longgetTermAt(long index) Get the term at a specific index.voidremoveFrom(long fromIndex) Remove all entries from the given index onwards (inclusive).voidrestoreEntry(LogEntry entry) Restore a log entry during WAL recovery, preserving its persisted index exactly.voidrestoreSnapshotState(long snapshotIndex, long snapshotTerm) Restore snapshot state during recovery.intsize()Get the total number of entries in the log.voidtruncateFrom(long fromIndex) Truncate log from the given index onwards (inclusive).
-
Constructor Details
-
RaftLog
Creates a new RaftLog for a given node.- Parameters:
nodeId- the ID of the node (non-null, used for logging)- Throws:
NullPointerException- if nodeId is null
-
-
Method Details
-
append
public long append(long term, byte[] command) Append a new entry to the log.This method is used by the leader to add new committed entries, and also by followers to reconstruct their log during recovery from the WAL.
The new entry is assigned a monotonically increasing index and added to the end of the log. All log indices are 1-based (index 0 is a sentinel).
- Parameters:
term- The Raft term when this entry is created (non-negative)command- The serialized state machine command (may be null or empty for NO_OP)- Returns:
- The index assigned to the new entry (positive)
- Throws:
IllegalStateException- If entry index monotonicity would be violatedIllegalArgumentException- If term is negative
-
appendEntry
Append a pre-built LogEntry to the log.This is used for entries that have a specific type (e.g., CONFIG_CHANGE, NO_OP) where the caller has already constructed the LogEntry with the correct type.
Thread-safe via write lock.
- Parameters:
entry- The LogEntry to append (non-null)- Returns:
- The index assigned to the entry
- Throws:
NullPointerException- If entry is nullIllegalStateException- If entry index monotonicity would be violatedIllegalArgumentException- If term is negative
-
restoreEntry
Restore a log entry during WAL recovery, preserving its persisted index exactly.Unlike
appendEntry(LogEntry)which reassigns index tolastIndex + 1, this method trusts the entry's original index from the WAL. It validates that the entry index is contiguous with the current log tail and aborts on gaps or duplicates.Thread-safe via write lock. Must only be called during recovery before the node starts accepting new operations.
- Parameters:
entry- The LogEntry to restore with its persisted index- Throws:
IllegalStateException- If the entry index is not exactly lastIndex + 1 (gap or duplicate)IllegalArgumentException- If term is negativeNullPointerException- If entry is null
-
appendAll
Append multiple entries to the log (batch append).This is used by followers during AppendEntries RPC from the leader. If an entry conflicts with an existing entry (same index, different term), the conflicting entry and all subsequent entries are truncated before appending.
Thread-safe via write lock. Entries are added in order.
- Parameters:
newEntries- List of entries to append (non-null, may be empty)- Throws:
NullPointerException- If newEntries is null
-
getEntry
Get a log entry at a specific index.Returns the entry at the given index if it exists in the log. The log is 1-indexed; index 0 is a sentinel and cannot be retrieved.
Thread-safe via read lock.
- Parameters:
index- The log index (1-based)- Returns:
- The LogEntry at the given index, or null if the index is out of bounds or before the snapshot index
-
getEntriesFrom
Get all entries from a starting index to the end of the log (inclusive).This is used by the leader when replicating entries to followers via AppendEntries RPC. The entries are returned as a copy to prevent external mutations.
Thread-safe via read lock.
- Parameters:
startIndex- The starting index (1-based). If startIndex is beyond the log end, returns an empty list- Returns:
- An immutable copy of entries from startIndex to the end of the log (inclusive), or an empty list if startIndex is out of bounds
-
getLastIndex
public long getLastIndex()Get the index of the last entry in the log.This returns the highest index in the replicated log, accounting for snapshots. If the log is empty but a snapshot exists, returns the snapshot index.
Thread-safe via read lock.
- Returns:
- The last log index (1-based), or the last snapshot index if log is empty, or 0 if both log and snapshot are empty
-
getLastTerm
public long getLastTerm()Get the term of the last entry in the log.Thread-safe via read lock.
- Returns:
- The term of the last log entry, or the last snapshot term if log is empty, or 0 if both log and snapshot are empty
-
getLastIndexAndTerm
Atomically get the last index and term as a pair.This avoids TOCTOU issues when callers need both values to correspond to the same log state (e.g., RequestVote RPCs where index and term must match).
- Returns:
- a record containing the last index and last term, read under a single lock
-
getTermAt
public long getTermAt(long index) Get the term at a specific index.Note: This method acquires the read lock via
withReadLock, then internally callsgetEntry()which also acquires the read lock. This is safe becauseReentrantReadWriteLockis reentrant. -
removeFrom
public void removeFrom(long fromIndex) Remove all entries from the given index onwards (inclusive).This is a convenience method used to roll back entries that failed WAL persistence. Delegates to
truncateFrom(long).- Parameters:
fromIndex- The index from which to start removing (1-based, inclusive)
-
truncateFrom
public void truncateFrom(long fromIndex) Truncate log from the given index onwards (inclusive).Removes all entries at or after fromIndex. This is used when a follower's log conflicts with the leader's log during AppendEntries RPC to restore consistency.
Thread-safe via write lock.
- Parameters:
fromIndex- The index from which to start truncating (1-based, inclusive). If fromIndex is beyond the log end, this is a no-op
-
compactUpTo
public void compactUpTo(long snapshotIndex, long snapshotTerm) Compact the log by removing all entries up to and including snapshotIndex.This is called after the state machine is snapshotted to bound memory usage. Entries before snapshotIndex are discarded since they are no longer needed (the state machine state is captured in the snapshot).
If snapshotIndex is older than the current snapshot, this operation is a no-op. Thread-safe via write lock.
- Parameters:
snapshotIndex- The index of the last entry included in the snapshot (1-based)snapshotTerm- The term of the entry at snapshotIndex
-
restoreSnapshotState
public void restoreSnapshotState(long snapshotIndex, long snapshotTerm) Restore snapshot state during recovery. Resets the in-memory log to start from the snapshot index (entries will be replayed from WAL afterward). This differs from compactUpTo which expects entries to already exist.- Parameters:
snapshotIndex- The snapshot's last included indexsnapshotTerm- The snapshot's last included term
-
size
public int size()Get the total number of entries in the log.This count includes the sentinel entry at index 0, so it is always >= 1. Does not include entries removed by compaction (snapshots).
Thread-safe via read lock.
- Returns:
- The number of log entries in memory (including sentinel)
-
getSnapshotIndex
public long getSnapshotIndex()Get the index of the last snapshot.The snapshot index represents the last entry that was included in the most recent snapshot of the state machine. Entries up to and including this index have been removed from the log.
Thread-safe via read lock.
- Returns:
- The snapshot index, or 0 if no snapshot exists
-
getSnapshotTerm
public long getSnapshotTerm()Get the term of the last snapshot.This is the Raft term of the entry at the snapshot index.
Thread-safe via read lock.
- Returns:
- The snapshot term, or 0 if no snapshot exists
-
getLogStats
Get a snapshot of current log statistics.Returns a point-in-time view of log metrics for monitoring and diagnostics. Thread-safe via read lock.
- Returns:
- A LogStats record with current log state
-
getEntryRange
Retrieve a range of entries from the log.This method returns entries within the specified range [fromIndex, toIndex]. Returns entries in order, or an empty list if the range is invalid or out of bounds. Thread-safe via read lock.
- Parameters:
fromIndex- The starting index (1-based, inclusive)toIndex- The ending index (1-based, inclusive)- Returns:
- A list copy of entries in the range, or empty list if range is invalid
-
containsEntry
public boolean containsEntry(long index, long term) Check if an entry with the given index and term exists in the log.This is used to verify log consistency during replication. Thread-safe via read lock.
- Parameters:
index- The entry index (1-based)term- The expected term of the entry- Returns:
- true if an entry at index with the given term exists, false otherwise
-
findConflict
Find the first conflicting entry when applying a list of new entries.Compares entries in the provided list against the log to find the first index where the terms differ. Returns the index of the first conflict, or -1 if no conflict. This is used during replication to determine where the follower's log diverges. Thread-safe via read lock.
- Parameters:
entries- List of entries to check for conflicts (non-null)- Returns:
- The index (1-based) of the first conflicting entry, or -1 if no conflict
- Throws:
NullPointerException- if entries is null
-
estimateSizeInBytes
public long estimateSizeInBytes()Estimate the memory usage of the log in bytes.Provides an approximate size calculation including entry overhead. Formula: ~56 bytes per LogEntry object + command size. Thread-safe via read lock.
- Returns:
- Approximate size in bytes (non-negative)
-