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 for API changes
The first step for upgrading is to adapt the changes in the API. Follow the following steps to update your application to pass compilation with v0.9.
-
Implementation of
RaftLogReader::get_log_state()
is moved toRaftLogReader::get_log_state()
. -
Generic types parameters
N, LS, SM
are removed fromRaft<C, N, LS, SM>
. -
RaftNetwork::send_xxx()
methods are removed, and should be replaced withRaftNetwork::xxx()
:RaftNetwork::send_append_entries()
toRaftNetwork::append_entries()
;RaftNetwork::send_vote()
toRaftNetwork::vote()
;RaftNetwork::send_install_snapshot()
toRaftNetwork::install_snapshot()
;
-
async
traits in Openraft are declared with#[openraft-macros::add_async_trait]
attribute since 0.9.#[async_trait::async_trait]
are no longer needed when implementingasync
trait.For example, upgrade 0.8 async-trait implementation
ⓘ#[async_trait::async_trait] impl RaftNetwork<TypeConfig> for MyNetwork {}
to
ⓘimpl RaftNetwork<TypeConfig> for MyNetwork {}
§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.