-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: add
Raft::ensure_linearizable()
to ensure linearizable read
The `Raft::is_leader()` method does not fully ensure linearizable read operations and is deprecated in this version. Instead, applications should use the `Raft::ensure_linearizable()` method to guarantee linearizability. Under the hood, `Raft::ensure_linearizable()` obtains a `ReadIndex` from `RaftCore` if it remains the leader, and blocks until the state machine applies up to the `ReadIndex`. This process ensures that the application observes all state visible to a preceding read operation. - Fix: #965 Upgrade tip: Replace `Raft::is_leader()` with `Raft::ensure_linearizable()`.
- Loading branch information
1 parent
dec6e32
commit 79372b4
Showing
16 changed files
with
464 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# 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 | ||
|
||
```ignore | ||
my_raft.ensure_linearizable().await?; | ||
proceed_with_state_machine_read(); | ||
``` | ||
|
||
The above snippet does the same as the following: | ||
|
||
```ignore | ||
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. | ||
|
||
|
||
[`ensure_linearizable()`]: crate::Raft::ensure_linearizable | ||
[`get_read_log_id()`]: crate::Raft::get_read_log_id | ||
[`Raft::metrics`]: crate::Raft::metrics |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.