Originally published on:
https://abdellani.dev/posts/2026-03-23-why-raft-cant-safely-commit-old-term-entries/
Raft is a consensus algorithm that allows a group of servers to agree on the order of a sequence of commands.
The objective of this article is not to re-explain RAFT, as this work has already been done in several other places, and the RAFT paper gives enough details to implement it. Instead, my focus will be on two problems that I faced when I voluntarily studied the open distributed systems course from MIT (course code 6584, I didn’t enroll in the course officially, and I wasn’t a student of MIT).
The first issue:
The paper specifies that a node should not commit the commands from previous terms directly. Those commands should be committed indirectly by committing the commands from the leader’s current term.
At that level, I was not convinced by this constraint, especially after seeing that the initial tests were passing. A simple scenario might be:
Suppose we have 3 servers: A, B, and C.
- In term 1, A becomes a leader.
- A adds a command X and replicates it to B.
- A crashes before committing the command.
The command X can’t be removed because the C server can’t become a leader in term 2. After all, its log isn’t as up to date as B's. The B server will refuse to vote for C.
The only valid next step is for B to become a leader and continue replicating the command X to C.
The answer was in a more complex scenario where the leader replicates its entries through several terms. In such a case, even if the entry is replicated on a majority, it still can be lost. This will break the safety rule that says: if a command is committed, it should not be lost.
Let’s take a concrete example and see how this can happen.
Suppose we have three servers: A, B, and C.
- The A server is the leader in term 1
- The A server receives the first command X, persists it, and crashes before replicating it.
- The C server becomes a leader in term 2
- The C server receives a command Y, persists it, and crashes before replicating it
- The B server can’t make any progress until at least one server reconnects. The system tolerates the loss of one server at most at the same time.
- The A server reconnects and takes the leadership in term 3 as its log is more updated than B’s log.
- The A server replicates the command X to B. According to the paper, the command should not be committed.
- The server A crashes again.
- The C server reconnects.
- At this stage, the B server will have command X from term 1, and the C server will have command Y from term 2. This means the C server will take the lead as its last log entry belongs to a more recent term.
- The C server will force the B server to remove the command X, and replace it with command Y
- The A server reconnects again. As a follower, it’ll be forced to remove the command X and replace it with Y
In term 3, the command X was replicated on the majority of servers (A & B). Yet in term 4, the command X was completely erased from logs. So the majority replication alone is not enough to guarantee safety
The following figures illustrate the example:
After understanding how a command replicated on a majority can still be lost after a few terms, that’s why the leader should never commit commands from previous terms.
Now, we can discuss the next question. Raft allows only a leader to consider an entry committed if it is in the current term and replicated on a majority. In term 4, from the previous figure, the server S3 has its entry replicated to a majority, but still can’t commit the command because the latest entry is from term 2.
As the protocol authorizes only to commit entries from the current term, one valid progress path will:
- The C server receives a new command Z from the client. This command will be labeled term 4.
- The C server replicates the new command to a majority (i.e., one additional node to reach a majority)
- Once the C server confirms that the new command is replicated, it can commit the new command confidently. The first command will be automatically committed, as all the entries before the commit index will be considered as committed.
The question that arises at this stage will be: what if the client doesn’t submit a new command? The S3 server will not be able to commit, and the system will not make any progress.
To avoid falling into a situation where the system freezes for a long time, when a node is promoted to a leader, it adds a new command. A command that doesn’t do anything, a no-operation or ”NOOP”. It’ll be replicated as any other commands, it’ll have an index, it’ll be committed, and sent on the apply channel. The higher layer doesn’t need to send it to the application layer. The application layer doesn’t need to learn about it or need to know how to interpret it.
The figure shows how the leader replicates the NOOP to the other node. Since NOOP’s term is the same as the current term of the leader, the leader can commit without facing the risk of losing the command in the future.
In conclusion, this shows why RAFT restricts to the current term, and why the NOOP commands are a practical way for a leader to safely make progress.
References:
http://nil.csail.mit.edu/6.5840/2025/papers/raft-extended.pdf


Top comments (1)
the NOOP trick is elegant but it took me a while to appreciate why it's necessary. the scenario you laid out where X gets replicated to a majority across term 3 but then gets wiped in term 4 is exactly the kind of thing that breaks your intuition. you'd expect "replicated on majority" to mean safe, but the term mismatch creates a window where a higher-term entry from a different leader can override it. one thing worth noting for anyone going deeper: the interaction between NOOP and read-only queries is its own rabbit hole. if a leader serves stale reads before its NOOP commits, you can break linearizability even with a healthy Raft cluster.