Module openraft::docs::protocol::read

source ·
Expand description

§Read Operations

Read operations within a Raft cluster are guaranteed to be linearizable.

This ensures that for any two read operations, A and B, if B occurs after A by wall clock time, then B will observe the same state as A or any subsequent state changes made by A, regardless of which node within the cluster each operation is performed on.

In Openraft read_index is the same as the read_index in the original raft paper. Openraft also use read_log_id instead of read_index.

§Ensuring linearizability

To ensure linearizability, read operations must perform a get_read_log_id() operation on the leader before proceeding.

This method confirms that this node is the leader at the time of invocation by sending heartbeats to a quorum of followers, and returns (read_log_id, last_applied_log_id):

  • read_log_id represents the log id up to which the state machine should apply to ensure a linearizable read,
  • last_applied_log_id is the last applied log id.

The caller then wait for last_applied_log_id to catch up read_log_id, which can be done by subscribing to Raft::metrics, and at last, proceed with the state machine read.

The above steps are encapsulated in the ensure_linearizable() method.

§Examples

my_raft.ensure_linearizable().await?;
proceed_with_state_machine_read();

The above snippet does the same as the following:

let (read_log_id, applied) = self.get_read_log_id().await?;

if read_log_id.index() > applied.index() {
    self.wait(None).applied_index_at_least(read_log_id.index(), "").await?
}

proceed_with_state_machine_read();

The comparison read_log_id > applied_log_id would also be valid in the above example.

§Ensuring Linearizability with read_log_id

The read_log_id is determined as the maximum of the last_committed_log_id and the log id of the first log entry in the current leader’s term (the “blank” log entry).

Assumes another earlier read operation reads from state machine with up to log id A. Since the leader has all committed entries up to its initial blank log entry, we have: read_log_id >= A.

When the last_applied_log_id meets or exceeds read_log_id, the state machine contains all state upto A. Therefore, a linearizable read is assured when last_applied_log_id >= read_log_id.

§Ensuring Linearizability with read_index

And it is also legal by comparing last_applied_log_id.index() >= read_log_id.index() due to the guarantee that committed logs will not be lost.

Since a previous read could only have observed committed logs, and read_log_id.index() is at least as large as any committed log, once last_applied_log_id.index() >= read_log_id.index(), the state machine is assured to reflect all entries seen by any past read.