◐ Off-By-One · answer catalog

go-raft-log-replication

2 answer(s)gogo1.26gogo1.26

go-raft-log-replication

📦 Source in repository (JSON)

Answer 1

The implementation is in ~/raft/ with two files:

raft.go — Core Raft Log Replication Engine

Data Structures: - LogEntry{Index, Term, Command} — individual log entries starting with a dummy at index 0 - AppendEntriesRequest — RPC with term, leader ID, prevLogIndex/prevLogTerm (log matching), entries, and leaderCommit - AppendEntriesResponse — includes fast conflict resolution fields (ConflictIndex, ConflictTerm) - RaftNode — encapsulates persistent state (log, currentTerm), volatile state (commitIndex, lastApplied, nextIndex, matchIndex), and role (isLeader)

AppendEntries Handler (HandleAppendEntries) — implements Figure 2 rules: 1. Term check: reject if req.Term < currentTerm; update term if greater 2. Log matching: reject if no entry at prevLogIndex or term mismatch at that index. Returns the first index of the conflicting term for fast backtracking (Section 5.3) 3. Conflict truncation: finds the first diverging entry and truncates the log from there 4. Append new entries: adds any entries not already present 5. Commit advancement: sets commitIndex = min(leaderCommit, lastNewEntry) if leaderCommit > commitIndex

Commit Index Advancement (advanceCommitIndexLocked): - Scans from the end of the log backward - For each index N > commitIndex, counts nodes where matchIndex[i] >= N - Requires log[N].term == currentTerm (safety — only commit current term's entries) - If count ≥ majority, advances commitIndex and applies entries

Fast Conflict Resolution: - When AppendEntries fails, the follower returns the first index of the conflicting term - The leader uses findLastIndexOfTerm to jump nextIndex past all entries of that term in its own log, avoiding O(n²) single-step backtracking

Network Simulation (Simulation): - AddPartition(a,b) / RemovePartition(a,b) — bidirectional partition between nodes - SendAppendEntries — checks partition table before delivery; returns a failure response if partitioned - ReplicateUntilStable — repeatedly replicates until all matchIndex values reach the leader's last index, then sends a final heartbeat to propagate commit index

raft_test.go — 11 Test Cases

Test What it verifies
TestHeartbeat Empty AppendEntries succeed, no phantom commits
TestBasicReplication 5 entries proposed → all followers have identical logs
TestLogMatchingProperty Correct prevLogIndex/term passes; wrong term or out-of-range index fails with conflict hints
TestCommitIndexAdvancement Leader advances commitIndex after majority confirms; followers learn via heartbeat
TestNetworkPartitionMinority Partition 1 node → majority (2/3) still commits; healed node catches up
TestMajorityPartition Partition leader from both followers → no commit advance; after heal, full catch-up
TestConflictResolution Follower with divergent log (higher term entries) gets truncated and converges
TestConcurrentReplication 20 concurrent proposals → all nodes converge, commit index matches
TestFullLogConsistency Multi-phase: replicate → partition → more entries → heal → full consistency check
TestHeartbeatsMaintainConsistency Repeated heartbeats preserve matching logs
TestConflictingEntryTruncation Follower with longer log at a different term gets truncated to leader's entries

Evidence & signatures

```
=== RUN   TestAll (0.53s)
    --- PASS: TestAll/Heartbeat (0.01s)
    --- PASS: TestAll/BasicReplication (0.02s)
    --- PASS: TestAll/LogMatchingProperty (0.02s)
    --- PASS: TestAll/CommitIndexAdvancement (0.04s)
    --- PASS: TestAll/NetworkPartitionMinority (0.05s)
    --- PASS: TestAll/MajorityPartition (0.04s)
    --- PASS: TestAll/ConflictResolution (0.03s)
    --- PASS: TestAll/ConcurrentReplication (0.02s)
    --- PASS: TestAll/FullLogConsistency (0.20s)
    --- PASS: TestAll/HeartbeatsMaintainConsistency (0.07s)
    --- PASS: TestAll/ConflictingEntryTruncation (0.03s)
PASS
ok  raft  1.057s
```

**Edge cases verified:**
- **Divergent logs**: Follower with entries from a higher term (term 2) than the leader (term 1) → conflict resolution truncates and replaces
- **Longer divergent log**: Follower with *more entries* than the leader at a conflicting term → truncation removes the extra entries
- **Majority vs minority partitions**: With 3 nodes, losing 1 still commits; losing 2 (leader isolated) blocks commits until healed
- **Empty heartbeats**: No entries → still success, no log corruption
- **Concurrent proposals**: 20 goroutines calling `Propose` simultaneously → all entries replicated and committed
- **Fast backtracking**: Conflict response includes `ConflictTerm` and `ConflictIndex` so the leader skips entire terms instead of decrementing one at a time

---
{"model": "claude-sonnet-4-20250514", "problem_class": "go-raft-log-replication", "result": "passed", "tests": 11}

Answer 2

The implementation is in ~/raft/ with two files:

raft.go — Core Raft Log Replication Engine

Data Structures: - LogEntry{Index, Term, Command} — individual log entries starting with a dummy at index 0 - AppendEntriesRequest — RPC with term, leader ID, prevLogIndex/prevLogTerm (log matching), entries, and leaderCommit - AppendEntriesResponse — includes fast conflict resolution fields (ConflictIndex, ConflictTerm) - RaftNode — encapsulates persistent state (log, currentTerm), volatile state (commitIndex, lastApplied, nextIndex, matchIndex), and role (isLeader)

AppendEntries Handler (HandleAppendEntries) — implements Figure 2 rules: 1. Term check: reject if req.Term < currentTerm; update term if greater 2. Log matching: reject if no entry at prevLogIndex or term mismatch at that index. Returns the first index of the conflicting term for fast backtracking (Section 5.3) 3. Conflict truncation: finds the first diverging entry and truncates the log from there 4. Append new entries: adds any entries not already present 5. Commit advancement: sets commitIndex = min(leaderCommit, lastNewEntry) if leaderCommit > commitIndex

Commit Index Advancement (advanceCommitIndexLocked): - Scans from the end of the log backward - For each index N > commitIndex, counts nodes where matchIndex[i] >= N - Requires log[N].term == currentTerm (safety — only commit current term's entries) - If count ≥ majority, advances commitIndex and applies entries

Fast Conflict Resolution: - When AppendEntries fails, the follower returns the first index of the conflicting term - The leader uses findLastIndexOfTerm to jump nextIndex past all entries of that term in its own log, avoiding O(n²) single-step backtracking

Network Simulation (Simulation): - AddPartition(a,b) / RemovePartition(a,b) — bidirectional partition between nodes - SendAppendEntries — checks partition table before delivery; returns a failure response if partitioned - ReplicateUntilStable — repeatedly replicates until all matchIndex values reach the leader's last index, then sends a final heartbeat to propagate commit index

raft_test.go — 11 Test Cases

Test What it verifies
TestHeartbeat Empty AppendEntries succeed, no phantom commits
TestBasicReplication 5 entries proposed → all followers have identical logs
TestLogMatchingProperty Correct prevLogIndex/term passes; wrong term or out-of-range index fails with conflict hints
TestCommitIndexAdvancement Leader advances commitIndex after majority confirms; followers learn via heartbeat
TestNetworkPartitionMinority Partition 1 node → majority (2/3) still commits; healed node catches up
TestMajorityPartition Partition leader from both followers → no commit advance; after heal, full catch-up
TestConflictResolution Follower with divergent log (higher term entries) gets truncated and converges
TestConcurrentReplication 20 concurrent proposals → all nodes converge, commit index matches
TestFullLogConsistency Multi-phase: replicate → partition → more entries → heal → full consistency check
TestHeartbeatsMaintainConsistency Repeated heartbeats preserve matching logs
TestConflictingEntryTruncation Follower with longer log at a different term gets truncated to leader's entries

Evidence & signatures

```
=== RUN   TestAll (0.53s)
    --- PASS: TestAll/Heartbeat (0.01s)
    --- PASS: TestAll/BasicReplication (0.02s)
    --- PASS: TestAll/LogMatchingProperty (0.02s)
    --- PASS: TestAll/CommitIndexAdvancement (0.04s)
    --- PASS: TestAll/NetworkPartitionMinority (0.05s)
    --- PASS: TestAll/MajorityPartition (0.04s)
    --- PASS: TestAll/ConflictResolution (0.03s)
    --- PASS: TestAll/ConcurrentReplication (0.02s)
    --- PASS: TestAll/FullLogConsistency (0.20s)
    --- PASS: TestAll/HeartbeatsMaintainConsistency (0.07s)
    --- PASS: TestAll/ConflictingEntryTruncation (0.03s)
PASS
ok  raft  1.057s
```

**Edge cases verified:**
- **Divergent logs**: Follower with entries from a higher term (term 2) than the leader (term 1) → conflict resolution truncates and replaces
- **Longer divergent log**: Follower with *more entries* than the leader at a conflicting term → truncation removes the extra entries
- **Majority vs minority partitions**: With 3 nodes, losing 1 still commits; losing 2 (leader isolated) blocks commits until healed
- **Empty heartbeats**: No entries → still success, no log corruption
- **Concurrent proposals**: 20 goroutines calling `Propose` simultaneously → all entries replicated and committed
- **Fast backtracking**: Conflict response includes `ConflictTerm` and `ConflictIndex` so the leader skips entire terms instead of decrementing one at a time

---
{"model": "claude-sonnet-4-20250514", "problem_class": "go-raft-log-replication", "result": "passed", "tests": 11}
Generated from the verified corpus · MIT licensedBack to the catalog