MeiliSearch/cluster/src/lib.rs

191 lines
5.6 KiB
Rust
Raw Normal View History

2023-03-14 17:38:21 +01:00
use std::net::ToSocketAddrs;
2023-03-21 18:25:53 +01:00
use std::str::FromStr;
use std::sync::{Arc, RwLock};
2023-03-14 17:38:21 +01:00
2023-03-16 16:31:16 +01:00
use batch::Batch;
use crossbeam::channel::{unbounded, Receiver, Sender};
2023-03-14 17:38:21 +01:00
use ductile::{connect_channel, ChannelReceiver, ChannelSender};
use log::info;
use meilisearch_types::tasks::{KindWithContent, Task};
2023-03-14 17:38:21 +01:00
use serde::{Deserialize, Serialize};
2023-03-16 16:31:16 +01:00
pub mod batch;
2023-03-14 17:38:21 +01:00
mod leader;
pub use leader::Leader;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Network issue occured")]
NetworkIssue,
#[error("Internal error:{0}")]
SerdeJson(#[from] serde_json::Error),
}
2023-03-16 14:31:03 +01:00
#[derive(Debug, Clone, Serialize, Deserialize)]
2023-03-14 17:38:21 +01:00
pub enum LeaderMsg {
// A dump to join the cluster
JoinFromDump(Vec<u8>),
2023-03-14 17:38:21 +01:00
// Starts a new batch
2023-03-16 16:31:16 +01:00
StartBatch { id: u32, batch: Batch },
2023-03-14 17:38:21 +01:00
//Tell the follower to commit the update asap
Commit(u32),
//Tell the follower to commit the update asap
RegisterNewTask { task: Task, update_file: Option<Vec<u8>> },
2023-03-14 17:38:21 +01:00
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FollowerMsg {
// Let the leader knows you're ready to commit
ReadyToCommit(u32),
RegisterNewTask(KindWithContent),
}
2023-03-21 18:25:53 +01:00
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2023-03-22 14:24:53 +01:00
#[serde(rename_all = "lowercase")]
2023-03-14 17:38:21 +01:00
pub enum Consistency {
One,
Two,
Quorum,
2023-03-22 14:24:53 +01:00
#[default]
2023-03-14 17:38:21 +01:00
All,
}
2023-03-21 18:25:53 +01:00
impl std::fmt::Display for Consistency {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2023-03-22 14:24:53 +01:00
match self {
Consistency::One => write!(f, "one"),
Consistency::Two => write!(f, "two"),
Consistency::Quorum => write!(f, "quorum"),
Consistency::All => write!(f, "all"),
}
2023-03-21 18:25:53 +01:00
}
}
impl FromStr for Consistency {
2023-03-22 14:24:53 +01:00
type Err = String;
2023-03-21 18:25:53 +01:00
fn from_str(s: &str) -> Result<Self, Self::Err> {
2023-03-22 14:24:53 +01:00
match s {
"one" => Ok(Consistency::One),
"two" => Ok(Consistency::Two),
"quorum" => Ok(Consistency::Quorum),
"all" => Ok(Consistency::All),
s => Err(format!(
"Unexpected value `{s}`, expected one of `one`, `two`, `quorum`, `all`"
)),
}
2023-03-21 18:25:53 +01:00
}
}
2023-03-22 14:24:53 +01:00
#[derive(Clone)]
pub enum Cluster {
Leader(Leader),
Follower(Follower),
}
#[derive(Clone)]
2023-03-14 17:38:21 +01:00
pub struct Follower {
sender: ChannelSender<FollowerMsg>,
get_batch: Receiver<(u32, Batch)>,
must_commit: Receiver<u32>,
register_new_task: Receiver<(Task, Option<Vec<u8>>)>,
batch_id: Arc<RwLock<u32>>,
2023-03-14 17:38:21 +01:00
}
impl Follower {
pub fn join(leader: impl ToSocketAddrs) -> (Follower, Vec<u8>) {
2023-03-14 17:38:21 +01:00
let (sender, receiver) = connect_channel(leader).unwrap();
info!("Connection to the leader established");
info!("Waiting for the leader to contact us");
let state = receiver.recv().unwrap();
let dump = match state {
LeaderMsg::JoinFromDump(dump) => dump,
msg => panic!("Received unexpected message {msg:?}"),
};
let (get_batch_sender, get_batch_receiver) = unbounded();
let (must_commit_sender, must_commit_receiver) = unbounded();
let (register_task_sender, register_task_receiver) = unbounded();
std::thread::spawn(move || {
Self::router(receiver, get_batch_sender, must_commit_sender, register_task_sender);
});
(
Follower {
sender,
get_batch: get_batch_receiver,
must_commit: must_commit_receiver,
register_new_task: register_task_receiver,
batch_id: Arc::default(),
},
dump,
)
2023-03-14 17:38:21 +01:00
}
fn router(
receiver: ChannelReceiver<LeaderMsg>,
get_batch: Sender<(u32, Batch)>,
must_commit: Sender<u32>,
register_new_task: Sender<(Task, Option<Vec<u8>>)>,
) {
2023-03-14 17:38:21 +01:00
loop {
match receiver.recv().expect("Lost connection to the leader") {
LeaderMsg::JoinFromDump(_) => {
panic!("Received a join from dump msg but Im already running")
}
LeaderMsg::StartBatch { id, batch } => {
info!("Starting to process a new batch");
get_batch.send((id, batch)).expect("Lost connection to the main thread")
}
LeaderMsg::Commit(id) => {
info!("Must commit");
must_commit.send(id).expect("Lost connection to the main thread")
}
LeaderMsg::RegisterNewTask { task, update_file } => {
info!("Registered a new task");
register_new_task
.send((task, update_file))
.expect("Lost connection to the main thread")
2023-03-14 17:38:21 +01:00
}
}
}
}
pub fn get_new_batch(&self) -> Batch {
2023-03-16 19:02:54 +01:00
info!("Get new batch called");
let (id, batch) = self.get_batch.recv().expect("Lost connection to the leader");
2023-03-16 19:02:54 +01:00
info!("Got a new batch");
*self.batch_id.write().unwrap() = id;
batch
}
pub fn ready_to_commit(&self) {
2023-03-16 19:02:54 +01:00
info!("I'm ready to commit");
let batch_id = self.batch_id.read().unwrap();
self.sender.send(FollowerMsg::ReadyToCommit(*batch_id)).unwrap();
2023-03-14 17:38:21 +01:00
loop {
let id = self.must_commit.recv().expect("Lost connection to the leader");
#[allow(clippy::comparison_chain)]
if id == *batch_id {
break;
} else if id > *batch_id {
panic!("We missed a batch");
2023-03-14 17:38:21 +01:00
}
}
2023-03-16 19:02:54 +01:00
info!("I got the right to commit");
2023-03-14 17:38:21 +01:00
}
pub fn get_new_task(&self) -> (Task, Option<Vec<u8>>) {
self.register_new_task.recv().unwrap()
}
2023-03-14 17:38:21 +01:00
}