258 lines
9 KiB
Rust
258 lines
9 KiB
Rust
use crate::config::NamespaceManager;
|
|
use crate::store::Store;
|
|
use flaredb_proto::raft_server::raft_service_server::RaftService;
|
|
use flaredb_proto::raft_server::{
|
|
FetchRangeRequest, FetchRangeResponse, ForwardEventualRequest, GetMerkleRequest,
|
|
GetMerkleResponse, GetModeRequest, GetModeResponse, ListNamespaceModesRequest,
|
|
ListNamespaceModesResponse, MerkleRange, NamespaceMode, OpenRaftAppendEntriesRequest,
|
|
OpenRaftAppendEntriesResponse, OpenRaftSnapshotRequest, OpenRaftSnapshotResponse,
|
|
OpenRaftVoteRequest, OpenRaftVoteResponse, RaftMessage, RaftResponse,
|
|
UpdateNamespaceModeRequest, UpdateNamespaceModeResponse,
|
|
};
|
|
use flaredb_raft::{FlareNode, FlareNodeId, FlareTypeConfig};
|
|
use openraft::raft::{AppendEntriesRequest, VoteRequest};
|
|
use std::sync::Arc;
|
|
use tonic::{Request, Response, Status};
|
|
|
|
pub struct RaftServiceImpl {
|
|
store: Arc<Store>,
|
|
#[allow(dead_code)]
|
|
local_id: u64,
|
|
}
|
|
|
|
impl RaftServiceImpl {
|
|
pub fn new(store: Arc<Store>, local_id: u64) -> Self {
|
|
Self { store, local_id }
|
|
}
|
|
}
|
|
|
|
#[tonic::async_trait]
|
|
impl RaftService for RaftServiceImpl {
|
|
async fn send(&self, _request: Request<RaftMessage>) -> Result<Response<RaftResponse>, Status> {
|
|
// Legacy raft-rs RPC - no longer supported
|
|
Err(Status::unimplemented(
|
|
"Legacy raft-rs RPC not supported. Use OpenRaft RPCs (vote_v2, append_entries_v2).",
|
|
))
|
|
}
|
|
|
|
async fn get_mode(
|
|
&self,
|
|
request: Request<GetModeRequest>,
|
|
) -> Result<Response<GetModeResponse>, Status> {
|
|
let req = request.into_inner();
|
|
let ns = if req.namespace.is_empty() {
|
|
"default".to_string()
|
|
} else {
|
|
req.namespace
|
|
};
|
|
let mode = self.store.namespace_manager.get_namespace(&ns).mode; // Use namespace_manager
|
|
let mode_str = NamespaceManager::mode_as_str(&mode).to_string();
|
|
Ok(Response::new(GetModeResponse { mode: mode_str }))
|
|
}
|
|
|
|
async fn update_namespace_mode(
|
|
&self,
|
|
request: Request<UpdateNamespaceModeRequest>,
|
|
) -> Result<Response<UpdateNamespaceModeResponse>, Status> {
|
|
let req = request.into_inner();
|
|
if req.namespace.is_empty() {
|
|
return Err(Status::invalid_argument("namespace is required"));
|
|
}
|
|
let mode = match req.mode.to_lowercase().as_str() {
|
|
"strong" => crate::config::ConsistencyMode::Strong,
|
|
"eventual" => crate::config::ConsistencyMode::Eventual,
|
|
_ => {
|
|
return Err(Status::invalid_argument(
|
|
"mode must be 'strong' or 'eventual'",
|
|
))
|
|
}
|
|
};
|
|
let cfg = self
|
|
.store
|
|
.namespace_manager // Use namespace_manager
|
|
.set_namespace_mode(&req.namespace, mode)
|
|
.map_err(Status::failed_precondition)?;
|
|
let mode_str = NamespaceManager::mode_as_str(&cfg.mode).to_string();
|
|
let ns_mode = NamespaceMode {
|
|
namespace: cfg.name.clone(),
|
|
id: cfg.id,
|
|
mode: mode_str,
|
|
from_default: !cfg.explicit,
|
|
};
|
|
Ok(Response::new(UpdateNamespaceModeResponse {
|
|
mode: Some(ns_mode),
|
|
}))
|
|
}
|
|
|
|
async fn forward_eventual(
|
|
&self,
|
|
request: Request<ForwardEventualRequest>,
|
|
) -> Result<Response<RaftResponse>, Status> {
|
|
let req = request.into_inner();
|
|
if let Some(node) = self.store.get_raft_node(req.region_id).await {
|
|
if !node.is_leader().await {
|
|
return Err(Status::failed_precondition("not leader"));
|
|
}
|
|
node.write_kv(req.namespace_id, req.key, req.value, req.ts)
|
|
.await
|
|
.map_err(|e| Status::internal(format!("raft write failed: {e}")))?;
|
|
return Ok(Response::new(RaftResponse {}));
|
|
}
|
|
Err(Status::failed_precondition("region not found"))
|
|
}
|
|
|
|
async fn list_namespace_modes(
|
|
&self,
|
|
_request: Request<ListNamespaceModesRequest>,
|
|
) -> Result<Response<ListNamespaceModesResponse>, Status> {
|
|
let list = self
|
|
.store
|
|
.namespace_manager
|
|
.list_namespaces()
|
|
.into_iter()
|
|
.map(|cfg| NamespaceMode {
|
|
namespace: cfg.name,
|
|
id: cfg.id,
|
|
mode: NamespaceManager::mode_as_str(&cfg.mode).to_string(),
|
|
from_default: !cfg.explicit,
|
|
})
|
|
.collect();
|
|
Ok(Response::new(ListNamespaceModesResponse {
|
|
namespaces: list,
|
|
}))
|
|
}
|
|
|
|
async fn get_merkle(
|
|
&self,
|
|
request: Request<GetMerkleRequest>,
|
|
) -> Result<Response<GetMerkleResponse>, Status> {
|
|
let req = request.into_inner();
|
|
let chunk = if req.chunk_size == 0 {
|
|
256
|
|
} else {
|
|
req.chunk_size as usize
|
|
};
|
|
let (root, leaves) =
|
|
crate::merkle::build_merkle(self.store.engine(), req.namespace_id, chunk)
|
|
.map_err(|e| Status::internal(format!("merkle: {e}")))?;
|
|
let ranges = leaves
|
|
.into_iter()
|
|
.map(|l| MerkleRange {
|
|
start_key: l.start,
|
|
end_key: l.end,
|
|
hash: l.hash,
|
|
})
|
|
.collect();
|
|
Ok(Response::new(GetMerkleResponse {
|
|
root,
|
|
leaves: ranges,
|
|
}))
|
|
}
|
|
|
|
async fn fetch_range(
|
|
&self,
|
|
request: Request<FetchRangeRequest>,
|
|
) -> Result<Response<FetchRangeResponse>, Status> {
|
|
let req = request.into_inner();
|
|
let (keys, values) = crate::merkle::fetch_range(
|
|
self.store.engine(),
|
|
req.namespace_id,
|
|
&req.start_key,
|
|
&req.end_key,
|
|
)
|
|
.await
|
|
.map_err(|e| Status::internal(format!("fetch_range: {e}")))?;
|
|
Ok(Response::new(FetchRangeResponse { keys, values }))
|
|
}
|
|
|
|
// OpenRaft RPCs
|
|
|
|
async fn vote_v2(
|
|
&self,
|
|
request: Request<OpenRaftVoteRequest>,
|
|
) -> Result<Response<OpenRaftVoteResponse>, Status> {
|
|
let req = request.into_inner();
|
|
let node = self
|
|
.store
|
|
.get_raft_node(req.region_id)
|
|
.await
|
|
.ok_or_else(|| Status::failed_precondition("region not found"))?;
|
|
|
|
let vote_req: VoteRequest<FlareNodeId> = serde_json::from_slice(&req.data)
|
|
.map_err(|e| Status::invalid_argument(format!("invalid vote request: {}", e)))?;
|
|
|
|
let resp = node
|
|
.raft
|
|
.vote(vote_req)
|
|
.await
|
|
.map_err(|e| Status::internal(format!("vote failed: {}", e)))?;
|
|
|
|
let data = serde_json::to_vec(&resp)
|
|
.map_err(|e| Status::internal(format!("serialize response: {}", e)))?;
|
|
|
|
Ok(Response::new(OpenRaftVoteResponse { data }))
|
|
}
|
|
|
|
async fn append_entries_v2(
|
|
&self,
|
|
request: Request<OpenRaftAppendEntriesRequest>,
|
|
) -> Result<Response<OpenRaftAppendEntriesResponse>, Status> {
|
|
let req = request.into_inner();
|
|
let node = self
|
|
.store
|
|
.get_raft_node(req.region_id)
|
|
.await
|
|
.ok_or_else(|| Status::failed_precondition("region not found"))?;
|
|
|
|
let append_req: AppendEntriesRequest<FlareTypeConfig> = serde_json::from_slice(&req.data)
|
|
.map_err(|e| {
|
|
Status::invalid_argument(format!("invalid append_entries request: {}", e))
|
|
})?;
|
|
|
|
let resp = node
|
|
.raft
|
|
.append_entries(append_req)
|
|
.await
|
|
.map_err(|e| Status::internal(format!("append_entries failed: {}", e)))?;
|
|
|
|
let data = serde_json::to_vec(&resp)
|
|
.map_err(|e| Status::internal(format!("serialize response: {}", e)))?;
|
|
|
|
Ok(Response::new(OpenRaftAppendEntriesResponse { data }))
|
|
}
|
|
|
|
async fn install_snapshot_v2(
|
|
&self,
|
|
request: Request<OpenRaftSnapshotRequest>,
|
|
) -> Result<Response<OpenRaftSnapshotResponse>, Status> {
|
|
let req = request.into_inner();
|
|
let node = self
|
|
.store
|
|
.get_raft_node(req.region_id)
|
|
.await
|
|
.ok_or_else(|| Status::failed_precondition("region not found"))?;
|
|
|
|
let vote: openraft::Vote<FlareNodeId> = serde_json::from_slice(&req.vote)
|
|
.map_err(|e| Status::invalid_argument(format!("invalid vote: {}", e)))?;
|
|
|
|
let meta: openraft::SnapshotMeta<FlareNodeId, FlareNode> =
|
|
serde_json::from_slice(&req.meta)
|
|
.map_err(|e| Status::invalid_argument(format!("invalid meta: {}", e)))?;
|
|
|
|
let snapshot = openraft::Snapshot {
|
|
meta,
|
|
snapshot: Box::new(std::io::Cursor::new(req.data)),
|
|
};
|
|
|
|
let resp = node
|
|
.raft
|
|
.install_full_snapshot(vote, snapshot)
|
|
.await
|
|
.map_err(|e| Status::internal(format!("install_snapshot failed: {}", e)))?;
|
|
|
|
let data = serde_json::to_vec(&resp)
|
|
.map_err(|e| Status::internal(format!("serialize response: {}", e)))?;
|
|
|
|
Ok(Response::new(OpenRaftSnapshotResponse { data }))
|
|
}
|
|
}
|