|
| 1 | +use std::collections::BTreeSet; |
| 2 | +use std::sync::Arc; |
| 3 | +use std::time::Duration; |
| 4 | + |
| 5 | +use maplit::btreeset; |
| 6 | +use openraft::Config; |
| 7 | +use openraft::NodeId; |
| 8 | +use openraft::State; |
| 9 | +use tracing_futures::Instrument; |
| 10 | + |
| 11 | +use crate::fixtures::RaftRouter; |
| 12 | + |
| 13 | +#[tokio::test(flavor = "multi_thread", worker_threads = 6)] |
| 14 | +async fn change_membership_cases() -> anyhow::Result<()> { |
| 15 | + let (_log_guard, ut_span) = init_ut!(); |
| 16 | + |
| 17 | + async { |
| 18 | + change_from_to(btreeset! {0}, btreeset! {1}).await?; |
| 19 | + change_from_to(btreeset! {0}, btreeset! {1,2}).await?; |
| 20 | + change_from_to(btreeset! {0}, btreeset! {1,2, 3}).await?; |
| 21 | + change_from_to(btreeset! {0, 1}, btreeset! {1, 2}).await?; |
| 22 | + change_from_to(btreeset! {0, 1}, btreeset! {1}).await?; |
| 23 | + change_from_to(btreeset! {0, 1}, btreeset! {2}).await?; |
| 24 | + change_from_to(btreeset! {0, 1}, btreeset! {3}).await?; |
| 25 | + change_from_to(btreeset! {0, 1, 2}, btreeset! {4}).await?; |
| 26 | + change_from_to(btreeset! {0, 1, 2}, btreeset! {4,5,6}).await?; |
| 27 | + change_from_to(btreeset! {0, 1, 2, 3, 4}, btreeset! {0, 1, 2, 3}).await?; |
| 28 | + |
| 29 | + Ok::<(), anyhow::Error>(()) |
| 30 | + } |
| 31 | + .instrument(ut_span) |
| 32 | + .await?; |
| 33 | + |
| 34 | + Ok(()) |
| 35 | +} |
| 36 | + |
| 37 | +#[tracing::instrument(level = "debug")] |
| 38 | +async fn change_from_to(old: BTreeSet<NodeId>, new: BTreeSet<NodeId>) -> anyhow::Result<()> { |
| 39 | + let mes = format!("from {:?} to {:?}", old, new); |
| 40 | + |
| 41 | + let only_in_old = old.difference(&new); |
| 42 | + let only_in_new = new.difference(&old); |
| 43 | + |
| 44 | + let config = Arc::new(Config::default().validate()?); |
| 45 | + let router = Arc::new(RaftRouter::new(config.clone())); |
| 46 | + |
| 47 | + let mut log_index = router.new_nodes_from_single(old.clone(), btreeset! {}).await?; |
| 48 | + |
| 49 | + tracing::info!("--- write 100 logs"); |
| 50 | + { |
| 51 | + router.client_request_many(0, "client", 100).await; |
| 52 | + log_index += 100; |
| 53 | + |
| 54 | + router.wait_for_log(&old, Some(log_index), timeout(), &format!("write 100 logs, {}", mes)).await?; |
| 55 | + } |
| 56 | + |
| 57 | + // let mtx = router.wait(&0, timeout()).await?.log(Some(0), "get metrics").await?; |
| 58 | + // let term_0 = mtx.current_term; |
| 59 | + |
| 60 | + tracing::info!("--- change to {:?}", new); |
| 61 | + { |
| 62 | + for id in only_in_new { |
| 63 | + router.new_raft_node(*id).await; |
| 64 | + } |
| 65 | + |
| 66 | + router.change_membership(0, new.clone()).await?; |
| 67 | + log_index += 2; // two member-change logs |
| 68 | + |
| 69 | + tracing::info!("--- wait for old leader or new leader"); |
| 70 | + { |
| 71 | + for id in new.iter() { |
| 72 | + router |
| 73 | + .wait(id, Some(Duration::from_millis(5_000))) |
| 74 | + .await? |
| 75 | + .metrics( |
| 76 | + |x| x.current_leader.is_some() && new.contains(&x.current_leader.unwrap()), |
| 77 | + format!("node {} in new cluster has leader in new cluster, {}", id, mes), |
| 78 | + ) |
| 79 | + .await?; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + for id in new.iter() { |
| 84 | + // new leader may already elected and committed a blank log. |
| 85 | + router |
| 86 | + .wait(id, timeout()) |
| 87 | + .await? |
| 88 | + .log_at_least(Some(log_index), format!("new cluster, {}", mes)) |
| 89 | + .await?; |
| 90 | + } |
| 91 | + |
| 92 | + for id in only_in_old.clone() { |
| 93 | + router |
| 94 | + .wait(id, timeout()) |
| 95 | + .await? |
| 96 | + .state(State::Learner, format!("node {} only in old, {}", id, mes)) |
| 97 | + .await?; |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + tracing::info!("--- write another 100 logs"); |
| 102 | + { |
| 103 | + // get new leader |
| 104 | + |
| 105 | + let m = router |
| 106 | + .wait(new.iter().next().unwrap(), timeout()) |
| 107 | + .await? |
| 108 | + .metrics(|x| x.current_leader.is_some(), format!("wait for new leader, {}", mes)) |
| 109 | + .await?; |
| 110 | + |
| 111 | + let leader = m.current_leader.unwrap(); |
| 112 | + |
| 113 | + router.client_request_many(leader, "client", 100).await; |
| 114 | + log_index += 100; |
| 115 | + } |
| 116 | + |
| 117 | + for id in new.iter() { |
| 118 | + router |
| 119 | + .wait(id, timeout()) |
| 120 | + .await? |
| 121 | + // new leader may commit a blonk log |
| 122 | + .log_at_least(Some(log_index), format!("new cluster recv logs 100~200, {}", mes)) |
| 123 | + .await?; |
| 124 | + } |
| 125 | + |
| 126 | + tracing::info!("--- log will not be sync to removed node"); |
| 127 | + { |
| 128 | + for id in only_in_old { |
| 129 | + let res = router |
| 130 | + .wait(id, timeout()) |
| 131 | + .await? |
| 132 | + .log( |
| 133 | + Some(log_index), |
| 134 | + format!("node {} in old cluster wont recv new logs, {}", id, mes), |
| 135 | + ) |
| 136 | + .await; |
| 137 | + assert!(res.is_err()); |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + Ok(()) |
| 142 | +} |
| 143 | + |
| 144 | +fn timeout() -> Option<Duration> { |
| 145 | + Some(Duration::from_millis(500)) |
| 146 | +} |
0 commit comments