Enhancement: persist commit index in LogStore to accelerate recovery
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 9.1k
- Forks
- 1.1k
- Avg merge
- 3h 27m
- Merged PRs (30d)
- 2
Description
Today I realised my mental model of how our Raft library works is subtly wrong.
I assumed that when a node restarts, it loads the most recent snapshot into the FSM and replays committed logs from disk into the FSM before rejoining the cluster.
In fact, we only load from snapshot since currently we don't persist any information about which logs are known to be committed which means that replaying them all might apply a few uncommitted ones from the end of the log.
In practice this works OK in most cases because if the node is elected leader then it will write a new log which as soon as it's committed will trigger applying all the other committed logs in to the FSM too. If it's a follower it doesn't catch up it's FSM until the first AppendEntries RPC from the leader tells it the current commit index in the cluster.
As we consider reducing snapshot frequency with WAL LogStore being able to store lots more logs performantly, this will become more of an issue because it will mean followers are much more "stale" when they start serving requests.
To fix this correctly, we'd need to persist the last known commit index so that we know which prefix of the logs it's safe to apply on startup. We could periodically persist that to the StableStore but that increases amount of disk transactions and trades that off against how recent the index stays. Currently StableStore performance is not an issue because it's only updated infrequently during elections. This would make it much more critical.
An alternative that would be more efficient but a little more complicate would be:
- Introduce a new optional interface called something like:
type CommitTrackingLogStore interface { SetCommitIndex(idx uint64) error ReadCommitIndex() (uint64, error) } - Have Raft detect that the
LogStorealso implements this just before callingStoreLogsduring replication or leader'sdispatchLogs, if it does, callSetCommitIndexfirst with the most recent commitIndex we know - Implementations may store that index in memory until the next
StoreLogscall and then write it out on disk along with the log data. - When the
LogStorerecovers it's state on open, it would recover the last persisted commit index to and load it into memory - When Raft starts up it can restore snapshot like now then...
- Check if the
LogStoreimplementsCommitTrackingLogStore. If it does, load the commit index from the store and replay all logs fromlastSnapshotIndexup to and including the commit index that was persisted, before completing startup.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the LogStore and StableStore interfaces, the replication paths that call StoreLogs, and startup snapshot recovery. Compare the persistence trade-offs described in the issue, then verify that a recovered node restores the persisted commit index and replays only committed logs before startup completes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- distributed-systems
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100