Module openraft::docs::upgrade_guide::upgrade_08_09
source · Expand description
§Guide for upgrading from v0.8 to v0.9:
§Major changes:
-
Removed Compatibility support for v0.7.
-
Add
AsyncRuntime
as an abstraction for async runtime. An application can use a different async runtime other thantokio
by implementing theAsyncRuntime
trait. -
committed
log id can be optionally saved withRaftLogStorage::save_committed()
. -
New errors:
PayloadTooLarge
: to inform Openraft to split append entries request into smaller ones. -
New API: support linearizable read:
Raft::ensure_linearizable()
. -
New API to support generic snapshot data transfer with the following new APIs:
-
New feature flags:
singlethreaded
: Run Openraft in a single threaded environment, i.e., without requiringSend
bound.loosen-follower-log-revert
: Allows cleaning a follower’s data, useful for testing.generic-snapshot-data
: RemoveAsyncRead + AsyncWrite
bound from snapshot data. Let application define its own snapshot data transfer.
§Upgrade RaftTypeConfig
-
Add
AsyncRuntime
toRaftTypeConfig
to specify the async runtime to use. Openraft provides a default implementations:TokioRuntime
. For example:ⓘopenraft::declare_raft_types!( pub TypeConfig: // ... AsyncRuntime = TokioRuntime );
You can just implement the
AsyncRuntime
trait for your own async runtime and use it inRaftTypeConfig
.
§Upgrade log storage: save committed log id
In v0.9, save_committed()
is added to RaftLogStorage
trait to optionally save the committed
log id.
For example:
impl RaftLogStorage for YourStorage {
// ...
fn save_committed(&self, committed: u64) -> Result<(), StorageError> {
// ...
}
}
If committed log id is saved,
the committed log will be applied to state machine via RaftStateMachine::apply()
upon startup,
if they are not already applied.
Not implementing save_committed()
will not affect correctness.
§Upgrade snapshot transmission
In v0.9, an application can use its own snapshot data transfer by enabling generic-snapshot-data
feature flag.
With this flag enabled, the snapshot data can be arbitrary type
and is no longer required to implement AsyncRead + AsyncWrite
trait.
To use arbitrary snapshot data, the application needs to:
-
Specify the snapshot data type in
RaftTypeConfig
:ⓘopenraft::declare_raft_types!( pub TypeConfig: // ... SnapshotData = YourSnapshotData );
-
implement the snapshot transfer in the application’s network layer by implementing the
RaftNetwork
trait. For example:ⓘimpl RaftNetwork for YourNetwork { // ... fn full_snapshot(&self, /*...*/) -> Result<_,_> { // ... } }
full_snapshot()
allows for the transfer of snapshot data using any preferred method. Openraft provides a default implementation inChunked
.Chunked
just delegate the transmission to several calls to the old chunk-based APIRaftNetwork::install_snapshot()
. If you want to minimize the changes in your application, just callChunked::send_snapshot()
infull_snapshot()
:ⓘimpl RaftNetwork for YourNetwork { async fn full_snapshot(&mut self, vote: Vote<C::NodeId>, snapshot: Snapshot<C>, /*...*/ ) -> Result<SnapshotResponse<C::NodeId>, StreamingError<C, Fatal<C::NodeId>>> { let resp = Chunked::send_snapshot(self, vote, snapshot, /*...*/).await?; Ok(resp) } }
Refer to: the default chunk-based snapshot sending
-
On the receiving end, when the application finished receiving the snapshot data, it should call
Raft::install_full_snapshot()
to install it.Chunked::receive_snapshot
provides a default chunk-based implementation for receiving snapshot data. The application can just use it:ⓘasync fn handle_install_snapshot_request( &self, req: InstallSnapshotRequest<C>, ) -> Result<_, _> where C::SnapshotData: AsyncRead + AsyncWrite + AsyncSeek + Unpin, { let my_vote = self.with_raft_state(|state| *state.vote_ref()).await?; let resp = InstallSnapshotResponse { vote: my_vote }; let finished_snapshot = { use crate::network::snapshot_transport::Chunked; use crate::network::snapshot_transport::SnapshotTransport; let mut streaming = self.snapshot.lock().await; Chunked::receive_snapshot(&mut *streaming, self, req).await? }; if let Some(snapshot) = finished_snapshot { let resp = self.install_full_snapshot(req_vote, snapshot).await?; return Ok(resp.into()); } Ok(resp) }
Refer to: the default chunk-based snapshot receiving
Note that the application is responsible for maintaining a streaming state
Streaming
during receiving chunks.