From a6215e86fde413db093a1d00ea8c6fcd34373c05 Mon Sep 17 00:00:00 2001 From: Valentin Syrovatskiy <v.syrovatskiy@picodata.io> Date: Tue, 30 Aug 2022 17:59:14 +0300 Subject: [PATCH] refactor: add Peer.target_grade --- src/main.rs | 3 +- src/traft/mod.rs | 39 +++++++++++++++++++++- src/traft/storage.rs | 33 ++++++++----------- src/traft/topology.rs | 71 ++++++++++++++++++++++------------------ test/int/test_couple.py | 2 +- test/int/test_joining.py | 1 + 6 files changed, 95 insertions(+), 54 deletions(-) diff --git a/src/main.rs b/src/main.rs index ef345b8bef..05c8198580 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use traft::ExpelRequest; use clap::StructOpt as _; use protobuf::Message as _; -use crate::traft::{Grade, LogicalClock, RaftIndex, UpdatePeerRequest}; +use crate::traft::{Grade, LogicalClock, RaftIndex, TargetGrade, UpdatePeerRequest}; use traft::error::Error; mod app; @@ -812,6 +812,7 @@ fn postjoin(args: &args::Run) { tlog!(Info, "initiating self-activation of {}", peer.instance_id); let req = UpdatePeerRequest::new(peer.instance_id, cluster_id) .with_grade(Grade::Online) + .with_target_grade(TargetGrade::Online) .with_failure_domain(args.failure_domain()); let leader_id = node.status().leader_id.expect("leader_id deinitialized"); diff --git a/src/traft/mod.rs b/src/traft/mod.rs index 462198308b..52b9d22eb6 100644 --- a/src/traft/mod.rs +++ b/src/traft/mod.rs @@ -198,8 +198,10 @@ pub struct Peer { /// `0` means it's not committed yet. pub commit_index: RaftIndex, - /// The state of this instance's activity. + /// The cluster's mind about actual state of this instance's activity. pub grade: Grade, + /// The desired state of this instance + pub target_grade: TargetGrade, /// Instance failure domains. Instances with overlapping failure domains /// must not be in the same replicaset. @@ -606,6 +608,35 @@ impl Default for Grade { } } +#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)] +pub enum TargetGrade { + // Instance should be configured up + Online, + // Instance should be gracefully shut down + Offline, + // Instance should be removed from cluster + Expelled, +} +impl TargetGrade { + const fn to_str(&self) -> &str { + match self { + Self::Online => "Online", + Self::Offline => "Offline", + Self::Expelled => "Expelled", + } + } +} +impl Display for TargetGrade { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(self.to_str()) + } +} +impl Default for TargetGrade { + fn default() -> Self { + Self::Online + } +} + /////////////////////////////////////////////////////////////////////////////// /// Request to update the instance in the storage. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -618,6 +649,7 @@ pub struct UpdatePeerRequest { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum PeerChange { Grade(Grade), + TargetGrade(TargetGrade), FailureDomain(FailureDomain), } @@ -637,6 +669,11 @@ impl UpdatePeerRequest { self } #[inline] + pub fn with_target_grade(mut self, target_grade: TargetGrade) -> Self { + self.changes.push(PeerChange::TargetGrade(target_grade)); + self + } + #[inline] pub fn with_failure_domain(mut self, failure_domain: FailureDomain) -> Self { self.changes.push(PeerChange::FailureDomain(failure_domain)); self diff --git a/src/traft/storage.rs b/src/traft/storage.rs index 48d9f20b90..d8f7eb0691 100644 --- a/src/traft/storage.rs +++ b/src/traft/storage.rs @@ -86,6 +86,7 @@ impl Storage { {name = 'replicaset_uuid', type = 'string', is_nullable = false}, {name = 'commit_index', type = 'unsigned', is_nullable = false}, {name = 'grade', type = 'string', is_nullable = false}, + {name = 'target_grade', type = 'string', is_nullable = false}, {name = 'failure_domain', type = 'map', is_nullable = false}, } }) @@ -605,10 +606,11 @@ inventory::submit!(crate::InnerTest { } }); +#[rustfmt::skip] inventory::submit!(crate::InnerTest { name: "test_storage_peers", body: || { - use traft::Grade::{Offline, Online}; + use traft::{Grade, TargetGrade}; let mut raft_group = Storage::space(RAFT_GROUP).unwrap(); @@ -616,23 +618,13 @@ inventory::submit!(crate::InnerTest { for peer in vec![ // r1 - ( - "i1", "i1-uuid", 1u64, "addr:1", "r1", "r1-uuid", 1u64, Online, &faildom, - ), - ( - "i2", "i2-uuid", 2u64, "addr:2", "r1", "r1-uuid", 2, Online, &faildom, - ), + ("i1", "i1-uuid", 1u64, "addr:1", "r1", "r1-uuid", 1u64, Grade::Online, TargetGrade::Online, &faildom,), + ("i2", "i2-uuid", 2u64, "addr:2", "r1", "r1-uuid", 2, Grade::Online, TargetGrade::Online, &faildom,), // r2 - ( - "i3", "i3-uuid", 3u64, "addr:3", "r2", "r2-uuid", 10, Online, &faildom, - ), - ( - "i4", "i4-uuid", 4u64, "addr:4", "r2", "r2-uuid", 10, Online, &faildom, - ), + ("i3", "i3-uuid", 3u64, "addr:3", "r2", "r2-uuid", 10, Grade::Online, TargetGrade::Online, &faildom,), + ("i4", "i4-uuid", 4u64, "addr:4", "r2", "r2-uuid", 10, Grade::Online, TargetGrade::Online, &faildom,), // r3 - ( - "i5", "i5-uuid", 5u64, "addr:5", "r3", "r3-uuid", 10, Online, &faildom, - ), + ("i5", "i5-uuid", 5u64, "addr:5", "r3", "r3-uuid", 10, Grade::Online, TargetGrade::Online, &faildom,), ] { raft_group.put(&peer).unwrap(); } @@ -657,12 +649,13 @@ inventory::submit!(crate::InnerTest { " in unique index \"raft_id\"", " in space \"raft_group\"", " with old tuple", - r#" - ["i1", "i1-uuid", 1, "addr:1", "r1", "r1-uuid", 1, "{on}", {{"A": "B"}}]"#, + r#" - ["i1", "i1-uuid", 1, "addr:1", "r1", "r1-uuid", 1, "{gon}", "{tgon}", {{"A": "B"}}]"#, " and new tuple", - r#" - ["i99", "", 1, "", "", "", 0, "{off}", {{}}]"#, + r#" - ["i99", "", 1, "", "", "", 0, "{goff}", "{tgon}", {{}}]"#, ), - on = Online, - off = Offline, + gon = Grade::Online, + goff = Grade::Offline, + tgon = TargetGrade::Online, ) ); diff --git a/src/traft/topology.rs b/src/traft/topology.rs index d288492852..ecf3c54713 100644 --- a/src/traft/topology.rs +++ b/src/traft/topology.rs @@ -11,6 +11,8 @@ use crate::util::Uppercase; use raft::INVALID_INDEX; +use super::TargetGrade; + pub struct Topology { replication_factor: u8, max_raft_id: RaftId, @@ -166,6 +168,7 @@ impl Topology { // It prevents a disruption in case of the // instance_id collision. grade: Grade::Online, + target_grade: super::TargetGrade::Online, failure_domain, }; @@ -203,6 +206,11 @@ impl Topology { peer.grade = grade; } } + PeerChange::TargetGrade(target_grade) => { + if peer.target_grade != TargetGrade::Expelled { + peer.target_grade = target_grade; + } + } PeerChange::FailureDomain(fd) => { // SAFETY: this is safe, because rust doesn't complain if you inline // the function @@ -239,9 +247,9 @@ mod tests { use crate::traft::instance_uuid; use crate::traft::replicaset_uuid; use crate::traft::FailureDomain; - use crate::traft::Grade::{Offline, Online}; use crate::traft::Peer; use crate::traft::UpdatePeerRequest; + use crate::traft::{Grade, TargetGrade}; use pretty_assertions::assert_eq; macro_rules! peers { @@ -279,6 +287,7 @@ mod tests { replicaset_uuid: replicaset_uuid($replicaset_id), commit_index: raft::INVALID_INDEX, grade: $grade, + target_grade: TargetGrade::Online, failure_domain: { let _f = FailureDomain::default(); $( let _f = $failure_domain; )? @@ -330,7 +339,7 @@ mod tests { ) => { $topology.update_peer( UpdatePeerRequest::new($instance_id.into(), "".into()) - .with_grade(Online) + .with_grade(Grade::Online) .with_failure_domain($failure_domain), ) }; @@ -349,38 +358,38 @@ mod tests { assert_eq!( join!(topology, None, None, "addr:1").unwrap(), - peer!(1, "i1", "r1", "addr:1", Online) + peer!(1, "i1", "r1", "addr:1", Grade::Online) ); assert_eq!( join!(topology, None, None, "addr:1").unwrap(), - peer!(2, "i2", "r2", "addr:1", Online) + peer!(2, "i2", "r2", "addr:1", Grade::Online) ); assert_eq!( join!(topology, None, Some("R3"), "addr:1").unwrap(), - peer!(3, "i3", "R3", "addr:1", Online) + peer!(3, "i3", "R3", "addr:1", Grade::Online) ); assert_eq!( join!(topology, Some("I4"), None, "addr:1").unwrap(), - peer!(4, "I4", "r3", "addr:1", Online) + peer!(4, "I4", "r3", "addr:1", Grade::Online) ); - let mut topology = Topology::from_peers(peers![(1, "i1", "r1", "addr:1", Online)]) + let mut topology = Topology::from_peers(peers![(1, "i1", "r1", "addr:1", Grade::Online)]) .with_replication_factor(1); assert_eq!( join!(topology, None, None, "addr:1").unwrap(), - peer!(2, "i2", "r2", "addr:1", Online) + peer!(2, "i2", "r2", "addr:1", Grade::Online) ); } #[test] fn test_override() { let mut topology = Topology::from_peers(peers![ - (1, "i1", "r1", "active:1", Online), - (2, "i2", "r2-original", "inactive:1", Offline), + (1, "i1", "r1", "active:1", Grade::Online), + (2, "i2", "r2-original", "inactive:1", Grade::Offline), ]) .with_replication_factor(2); @@ -408,7 +417,7 @@ mod tests { // Disruption isn't destructive if auto-expel allows (TODO). assert_eq!( join!(topology, Some("i2"), None, "inactive:2").unwrap(), - peer!(3, "i2", "r1", "inactive:2", Online), + peer!(3, "i2", "r1", "inactive:2", Grade::Online), // Attention: generated replicaset_id differs from the // original one, as well as raft_id. // That's a desired behavior. @@ -430,71 +439,71 @@ mod tests { #[test] fn test_instance_id_collision() { let mut topology = Topology::from_peers(peers![ - (1, "i1", "r1", "addr:1", Online), - (2, "i3", "r3", "addr:3", Online), + (1, "i1", "r1", "addr:1", Grade::Online), + (2, "i3", "r3", "addr:3", Grade::Online), // Attention: i3 has raft_id=2 ]); assert_eq!( join!(topology, None, Some("r2"), "addr:2").unwrap(), - peer!(3, "i3-2", "r2", "addr:2", Online), + peer!(3, "i3-2", "r2", "addr:2", Grade::Online), ); } #[test] fn test_replication_factor() { let mut topology = Topology::from_peers(peers![ - (9, "i9", "r9", "nowhere", Online), - (10, "i10", "r9", "nowhere", Online), + (9, "i9", "r9", "nowhere", Grade::Online), + (10, "i10", "r9", "nowhere", Grade::Online), ]) .with_replication_factor(2); assert_eq!( join!(topology, Some("i1"), None, "addr:1").unwrap(), - peer!(11, "i1", "r1", "addr:1", Online), + peer!(11, "i1", "r1", "addr:1", Grade::Online), ); assert_eq!( join!(topology, Some("i2"), None, "addr:2").unwrap(), - peer!(12, "i2", "r1", "addr:2", Online), + peer!(12, "i2", "r1", "addr:2", Grade::Online), ); assert_eq!( join!(topology, Some("i3"), None, "addr:3").unwrap(), - peer!(13, "i3", "r2", "addr:3", Online), + peer!(13, "i3", "r2", "addr:3", Grade::Online), ); assert_eq!( join!(topology, Some("i4"), None, "addr:4").unwrap(), - peer!(14, "i4", "r2", "addr:4", Online), + peer!(14, "i4", "r2", "addr:4", Grade::Online), ); } #[test] fn test_set_active() { let mut topology = Topology::from_peers(peers![ - (1, "i1", "r1", "nowhere", Online), - (2, "i2", "r2", "nowhere", Offline), + (1, "i1", "r1", "nowhere", Grade::Online), + (2, "i2", "r2", "nowhere", Grade::Offline), ]) .with_replication_factor(1); assert_eq!( - set_grade!(topology, "i1", Offline).unwrap(), - peer!(1, "i1", "r1", "nowhere", Offline), + set_grade!(topology, "i1", Grade::Offline).unwrap(), + peer!(1, "i1", "r1", "nowhere", Grade::Offline), ); // idempotency assert_eq!( - set_grade!(topology, "i1", Offline).unwrap(), - peer!(1, "i1", "r1", "nowhere", Offline), + set_grade!(topology, "i1", Grade::Offline).unwrap(), + peer!(1, "i1", "r1", "nowhere", Grade::Offline), ); assert_eq!( - set_grade!(topology, "i2", Online).unwrap(), - peer!(2, "i2", "r2", "nowhere", Online), + set_grade!(topology, "i2", Grade::Online).unwrap(), + peer!(2, "i2", "r2", "nowhere", Grade::Online), ); // idempotency assert_eq!( - set_grade!(topology, "i2", Online).unwrap(), - peer!(2, "i2", "r2", "nowhere", Online), + set_grade!(topology, "i2", Grade::Online).unwrap(), + peer!(2, "i2", "r2", "nowhere", Grade::Online), ); } @@ -629,7 +638,7 @@ mod tests { assert_eq!(peer.replicaset_id, "r1"); assert_eq!( - set_grade!(t, "i3", Offline).unwrap().failure_domain, + set_grade!(t, "i3", Grade::Offline).unwrap().failure_domain, faildoms! {planet: B, owner: V, dimension: C137}, ); diff --git a/test/int/test_couple.py b/test/int/test_couple.py index c6bbabfa3f..d540b4b01c 100644 --- a/test/int/test_couple.py +++ b/test/int/test_couple.py @@ -174,7 +174,7 @@ def test_deactivation(cluster2: Cluster): ".raft_update_peer", target.instance_id, target.cluster_id, - [{"Grade": grade}], + [{"Grade": grade}, {"TargetGrade": "Online"}], ) # check idempotency diff --git a/test/int/test_joining.py b/test/int/test_joining.py index 59833f7b6b..475c0265be 100644 --- a/test/int/test_joining.py +++ b/test/int/test_joining.py @@ -152,6 +152,7 @@ def test_replication(cluster: Cluster): instance.eval("return box.info.cluster.uuid"), None, "Online", + "Online", dict(), ] -- GitLab