diff --git a/docs/clustering.md b/docs/clustering.md index 84e45cca6442628f1a32c7b32d29ef7ac61daab6..519bf5dd9215ecabdc5eac0217639d0faa35cda5 100644 --- a/docs/clustering.md +++ b/docs/clustering.md @@ -56,7 +56,7 @@ picodata run --instance-id iN --listen iN --peer i1 Логика функции `postjoin()` одинакова Ð´Ð»Ñ Ð²Ñех инÑтанÑов. К Ñтому моменту Ð´Ð»Ñ Ð¸Ð½ÑтанÑа уже инициализированы корректные проÑтранÑтва Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð² БД и могут быть накоплены запиÑи в журнале Raft. ИнÑÑ‚Ð°Ð½Ñ Ð¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð¸Ñ€ÑƒÐµÑ‚ узел Raft и проверÑет, что данные Ñинхронизированы (`read barrier` получен) и журнал Raft актуален. Из журнала ÑтановÑÑ‚ÑÑ Ð¸Ð·Ð²ÐµÑтны параметры репликации, и инÑÑ‚Ð°Ð½Ñ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÐµÑ‚ Ñинхронизацию данных уровне репликационных групп Tarantool. -Следующим шагом инÑтанÑу необходимо актуализировать Ñвой ÑÑ‚Ð°Ñ‚ÑƒÑ (`UpdatePeerRequest{ health: Online, failure_domain }`), и дождатьÑÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð° Ñтой запиÑи в Raft. +Следующим шагом инÑтанÑу необходимо актуализировать Ñвой ÑÑ‚Ð°Ñ‚ÑƒÑ (`UpdatePeerRequest{ grade: Online, failure_domain }`), и дождатьÑÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð° Ñтой запиÑи в Raft. Теперь узел Raft готов к иÑпользованию. @@ -101,7 +101,7 @@ struct Peer { failure_domain: FailureDomain, /// Loading / Online / Offline - health: Health, + grade: Grade, /// Ð˜Ð½Ð´ÐµÐºÑ Ð·Ð°Ð¿Ð¸Ñи в Raft-журнале. ПрепÑÑ‚Ñтвует затиранию /// более Ñтарыми запиÑÑми, по мере Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Raft-журнала. @@ -113,7 +113,7 @@ struct Peer { - `JoinRequest` отправлÑет вÑегда неинициализированный инÑтанÑ. - Ð’ завиÑимоÑти от того, ÑодержитÑÑ Ð»Ð¸ в запроÑе `instance_id`, проводитÑÑ Ð°Ð½Ð°Ð»Ð¸Ð· его корректноÑти (уникальноÑти). -- Ð’ процеÑÑе обработки запроÑа в Raft-журнал добавлÑетÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ `op::PersistPeer{ peer }`, который помимо вÑевозможных айдишников Ñодержит поле `health: Loading`, которое играет важную роль в обеÑпечении надежноÑти клаÑтера. [TODO](## "Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð² коде Online вмеÑто Loading, но Ñто надо иÑправить.") +- Ð’ процеÑÑе обработки запроÑа в Raft-журнал добавлÑетÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ `op::PersistPeer{ peer }`, который помимо вÑевозможных айдишников Ñодержит поле `grade: Loading`, которое играет важную роль в обеÑпечении надежноÑти клаÑтера. [TODO](## "Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð² коде Online вмеÑто Loading, но Ñто надо иÑправить.") - Обработка запроÑа также включает в ÑÐµÐ±Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ðµ инÑтанÑа в Raft-группу в роли `Learner` (процедура также извеÑÑ‚Ð½Ð°Ñ ÐºÐ°Ðº `raft::ConfChangeV2`). Raft не позволÑет изменÑÑ‚ÑŒ топологию, пока предыдущее изменение не было применено. ПоÑтому в целÑÑ… оптимизации обработка идет в отдельном потоке (Ñ‚.н. `raft_conf_change_loop`) и выполнÑетÑÑ Ð³Ñ€ÑƒÐ¿Ð¿Ð°Ð¼Ð¸. - Прежде чем отвечать на запроÑ, инÑÑ‚Ð°Ð½Ñ Ð´Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ‚ÑÑ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ конфигурации. Ðто произойдет поÑле того как он выйдет из ÑоÑтоÑÐ½Ð¸Ñ `joint state` ([подробнее](https://web.stanford.edu/~ouster/cgi-bin/papers/OngaroPhD.pdf), §4.3). [TODO](## "Ñтот Ñ‚ÐµÐ·Ð¸Ñ Ð² коде пока не реализован") - Ð’ ответ выдаётÑÑ Ð²Ñегда новый `raft_id`, никому другому ранее не принадлежавший. @@ -127,7 +127,7 @@ struct Peer { Поток предÑтавлÑет Ñобой беÑконечный цикл. Ðа каждой итерации выполнÑетÑÑ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ°, что ÑоÑтав `voters` / `learners` ÑоответÑтвует ÑоÑтоÑнию инÑтанÑов, и при необходимоÑти Ñти Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð°Ñ‡ÐºÐ¾Ð¹ запиÑываютÑÑ Ð² Raft-журнал: -- ИнÑтанÑÑ‹ в ÑтатуÑе `health: Loading` довавлÑÑŽÑ‚ÑÑ ÐºÐ°Ðº неголоÑующие. +- ИнÑтанÑÑ‹ в ÑтатуÑе `grade: Loading` довавлÑÑŽÑ‚ÑÑ ÐºÐ°Ðº неголоÑующие. - ЕÑли еÑÑ‚ÑŒ возможноÑÑ‚ÑŒ, `Offline` инÑтанÑÑ‹ передают право голоÑа другим `Online`. [TODO](## "Ð¡ÐµÐ¹Ñ‡Ð°Ñ offline инÑÑ‚Ð°Ð½Ñ Ð´ÐµÐ¼Ð¾ÑƒÑ‚Ð¸Ñ‚ÑÑ Ð±ÐµÐ·ÑƒÑловно, даже еÑли других онлайн кандидатов нет. Ðе надо так делать.") - ЕÑли общее количеÑтво голоÑующих инÑтанÑов оказываетÑÑ Ð¼ÐµÐ½ÑŒÑˆÐµ целевого, `Online` инÑтанÑÑ‹ получают право голоÑа. @@ -163,4 +163,4 @@ struct Peer { - ИнÑÑ‚Ð°Ð½Ñ Ð½Ðµ должен оÑтаватьÑÑ Ð²Ð¾ÑƒÑ‚ÐµÑ€Ð¾Ð¼, пока еÑÑ‚ÑŒ другие онлайн кандидаты. - ИнÑÑ‚Ð°Ð½Ñ Ð½Ðµ должен оÑтаватьÑÑ Ð»Ð¸Ð´ÐµÑ€Ð¾Ð¼. -Чтобы Ñтого добитьÑÑ, каждый инÑÑ‚Ð°Ð½Ñ Ð½Ð° `on_shutdown` триггер отправлÑет лидеру Ð·Ð°Ð¿Ñ€Ð¾Ñ `UpdatePeerRequest{ health: Offline }`. ÐепоÑредÑтвенно изменением роли `voter` -> `learner` занимаетÑÑ Ð¾Ñ‚Ð´ÐµÑŒÐ½Ñ‹Ð¹ поток на лидере (тот Ñамый `raft_conf_change_loop`), инÑÑ‚Ð°Ð½Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ дожидаетÑÑ ÐµÐ³Ð¾ применениÑ. +Чтобы Ñтого добитьÑÑ, каждый инÑÑ‚Ð°Ð½Ñ Ð½Ð° `on_shutdown` триггер отправлÑет лидеру Ð·Ð°Ð¿Ñ€Ð¾Ñ `UpdatePeerRequest{ grade: Offline }`. ÐепоÑредÑтвенно изменением роли `voter` -> `learner` занимаетÑÑ Ð¾Ñ‚Ð´ÐµÑŒÐ½Ñ‹Ð¹ поток на лидере (тот Ñамый `raft_conf_change_loop`), инÑÑ‚Ð°Ð½Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ дожидаетÑÑ ÐµÐ³Ð¾ применениÑ. diff --git a/src/traft/mod.rs b/src/traft/mod.rs index 83b12c013b1ef0a5062be25db8b1c341b197a6cf..6c3d53933ee29117f792502d68ffc7e29c50442c 100644 --- a/src/traft/mod.rs +++ b/src/traft/mod.rs @@ -199,7 +199,7 @@ pub struct Peer { pub commit_index: RaftIndex, /// The state of this instance's activity. - pub health: Health, + pub grade: Grade, /// Instance failure domains. Instances with overlapping failure domains /// must not be in the same replicaset. @@ -211,7 +211,7 @@ impl Encode for Peer {} impl Peer { pub fn is_active(&self) -> bool { - matches!(self.health, Health::Online) + matches!(self.grade, Grade::Online) } } @@ -224,7 +224,7 @@ impl std::fmt::Display for Peer { self.raft_id, self.replicaset_id, self.peer_address, - self.health, + self.grade, self.commit_index, &self.failure_domain, ) @@ -575,32 +575,32 @@ impl Encode for ExpelResponse {} /////////////////////////////////////////////////////////////////////////////// /// Activity state of an instance. #[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)] -pub enum Health { - // Instance is active and is handling requests. - Online, +pub enum Grade { // Instance has gracefully shut down. Offline, + // Instance is active and is handling requests. + Online, // Instance has permanently removed from cluster. Expelled, } -impl Health { +impl Grade { const fn to_str(&self) -> &str { match self { - Self::Online => "Online", Self::Offline => "Offline", + Self::Online => "Online", Self::Expelled => "Expelled", } } } -impl Display for Health { +impl Display for Grade { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str(self.to_str()) } } -impl Default for Health { +impl Default for Grade { fn default() -> Self { Self::Offline } @@ -610,7 +610,7 @@ impl Default for Health { /// Request to deactivate the instance. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct UpdatePeerRequest { - pub health: Health, + pub grade: Grade, pub instance_id: String, pub cluster_id: String, pub failure_domain: Option<FailureDomain>, @@ -621,7 +621,7 @@ impl UpdatePeerRequest { #[inline] pub fn set_online(instance_id: impl Into<String>, cluster_id: impl Into<String>) -> Self { Self { - health: Health::Online, + grade: Grade::Online, instance_id: instance_id.into(), cluster_id: cluster_id.into(), failure_domain: None, @@ -631,7 +631,7 @@ impl UpdatePeerRequest { #[inline] pub fn set_offline(instance_id: impl Into<String>, cluster_id: impl Into<String>) -> Self { Self { - health: Health::Offline, + grade: Grade::Offline, instance_id: instance_id.into(), cluster_id: cluster_id.into(), failure_domain: None, @@ -641,7 +641,7 @@ impl UpdatePeerRequest { #[inline] pub fn set_expelled(instance_id: impl Into<String>, cluster_id: impl Into<String>) -> Self { Self { - health: Health::Expelled, + grade: Grade::Expelled, instance_id: instance_id.into(), cluster_id: cluster_id.into(), failure_domain: None, diff --git a/src/traft/node.rs b/src/traft/node.rs index 1851c1e5ed985f75ecc4681715afb630e8503fbf..83bbe7f80c0010a655e323ba8618648997826459 100644 --- a/src/traft/node.rs +++ b/src/traft/node.rs @@ -49,7 +49,7 @@ use crate::traft::Topology; use crate::traft::TopologyRequest; use crate::traft::{ExpelRequest, ExpelResponse, JoinRequest, JoinResponse, UpdatePeerRequest}; -use super::Health; +use super::Grade; use super::OpResult; type RawNode = raft::RawNode<Storage>; @@ -301,10 +301,10 @@ impl Node { ), TopologyRequest::UpdatePeer(UpdatePeerRequest { instance_id, - health, + grade, failure_domain, .. - }) => topology.update_peer(&instance_id, health, failure_domain), + }) => topology.update_peer(&instance_id, grade, failure_domain), }; let mut peer = crate::unwrap_ok_or!(peer_result, Err(e) => { @@ -505,7 +505,7 @@ fn handle_committed_normal_entry( if let Some(traft::Op::PersistPeer { peer }) = entry.op() { pool.connect(peer.raft_id, peer.peer_address.clone()); *topology_changed = true; - if peer.health == Health::Expelled && peer.raft_id == raw_node.raft.id { + if peer.grade == Grade::Expelled && peer.raft_id == raw_node.raft.id { crate::tarantool::exit(0); } } diff --git a/src/traft/storage.rs b/src/traft/storage.rs index c05ebade3452c6c9730f873747cddf67bef33713..48d9f20b906080c5dbcdc7bedb57fd45030d35a1 100644 --- a/src/traft/storage.rs +++ b/src/traft/storage.rs @@ -85,7 +85,7 @@ impl Storage { {name = 'replicaset_id', type = 'string', is_nullable = false}, {name = 'replicaset_uuid', type = 'string', is_nullable = false}, {name = 'commit_index', type = 'unsigned', is_nullable = false}, - {name = 'health', type = 'string', is_nullable = false}, + {name = 'grade', type = 'string', is_nullable = false}, {name = 'failure_domain', type = 'map', is_nullable = false}, } }) @@ -608,7 +608,7 @@ inventory::submit!(crate::InnerTest { inventory::submit!(crate::InnerTest { name: "test_storage_peers", body: || { - use traft::Health::{Offline, Online}; + use traft::Grade::{Offline, Online}; let mut raft_group = Storage::space(RAFT_GROUP).unwrap(); diff --git a/src/traft/topology.rs b/src/traft/topology.rs index 1a587d4dd3f816c4daf34322d76c6563a80a28ec..fb00c7bad5424a2e2eca0372bd37ee608ddda3e0 100644 --- a/src/traft/topology.rs +++ b/src/traft/topology.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use crate::traft::instance_uuid; use crate::traft::replicaset_uuid; use crate::traft::FailureDomain; -use crate::traft::Health; +use crate::traft::Grade; use crate::traft::Peer; use crate::traft::{InstanceId, RaftId, ReplicasetId}; use crate::util::Uppercase; @@ -163,7 +163,7 @@ impl Topology { // Mark instance already active when it joins. // It prevents a disruption in case of the // instance_id collision. - health: Health::Online, + grade: Grade::Online, failure_domain, }; @@ -189,13 +189,13 @@ impl Topology { pub fn update_peer( &mut self, instance_id: &str, - health: Health, + grade: Grade, failure_domain: Option<FailureDomain>, ) -> Result<Peer, String> { let current_peer = self.get_peer(instance_id).unwrap(); - let health = match current_peer.health { - Health::Expelled => Health::Expelled, - _ => health, + let grade = match current_peer.grade { + Grade::Expelled => Grade::Expelled, + _ => grade, }; let this = self as *const Self; @@ -213,7 +213,7 @@ impl Topology { peer.failure_domain = fd; } - peer.health = health; + peer.grade = grade; Ok(peer.clone()) } } @@ -240,7 +240,7 @@ mod tests { use crate::traft::instance_uuid; use crate::traft::replicaset_uuid; use crate::traft::FailureDomain; - use crate::traft::Health::{Offline, Online}; + use crate::traft::Grade::{Offline, Online}; use crate::traft::Peer; use pretty_assertions::assert_eq; @@ -250,12 +250,12 @@ mod tests { $instance_id:literal, $replicaset_id:literal, $peer_address:literal, - $health:expr + $grade:expr $(, $failure_domain:expr)? $(,)? ) ),* $(,)? ] => { vec![$( - peer!($raft_id, $instance_id, $replicaset_id, $peer_address, $health $(,$failure_domain)?) + peer!($raft_id, $instance_id, $replicaset_id, $peer_address, $grade $(,$failure_domain)?) ),*] }; } @@ -266,7 +266,7 @@ mod tests { $instance_id:literal, $replicaset_id:literal, $peer_address:literal, - $health:expr + $grade:expr $(, $failure_domain:expr)? $(,)? ) => { @@ -278,7 +278,7 @@ mod tests { instance_uuid: instance_uuid($instance_id), replicaset_uuid: replicaset_uuid($replicaset_id), commit_index: raft::INVALID_INDEX, - health: $health, + grade: $grade, failure_domain: { let _f = FailureDomain::default(); $( let _f = $failure_domain; )? @@ -314,9 +314,9 @@ mod tests { ( $topology:expr, $instance_id:expr, - $health:expr $(,)? + $grade:expr $(,)? ) => { - $topology.update_peer($instance_id, $health, None) + $topology.update_peer($instance_id, $grade, None) }; } diff --git a/test/int/test_couple.py b/test/int/test_couple.py index 077163501391e998b4261b9b2e7432672da11912..c509d7e98751ea97c494fca10a0182582317f03f 100644 --- a/test/int/test_couple.py +++ b/test/int/test_couple.py @@ -113,8 +113,8 @@ def test_deactivation(cluster2: Cluster): instance.eval( """ raft_id = ... - health = box.space.raft_group.index.raft_id:get(raft_id).health - is_active = health == 'Online' + grade = box.space.raft_group.index.raft_id:get(raft_id).grade + is_active = grade == 'Online' voters = box.space.raft_state:get('voters').value for _, voter in pairs(voters) do if voter == raft_id then diff --git a/test/int/test_expelling.py b/test/int/test_expelling.py index df4dd8d14e1964bd194bdc04c71cfaa668197f95..c554d125329175986dc4429b2b89a0e053832f27 100644 --- a/test/int/test_expelling.py +++ b/test/int/test_expelling.py @@ -9,12 +9,11 @@ def cluster3(cluster: Cluster): def assert_peer_expelled(expelled_peer: Instance, instance: Instance): - health = instance.eval( - "return box.space.raft_group.index.instance_id:get(...).health", + grade = instance.eval( + "return box.space.raft_group.index.instance_id:get(...).grade", expelled_peer.instance_id, ) - # print(health) - assert health == "Expelled" + assert grade == "Expelled" def assert_voters(voters: list[Instance], instance: Instance):