From 259ff4ac0d8154a67764b099f941ea7b408b5641 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Thu, 26 Sep 2024 15:57:02 +0300
Subject: [PATCH] refactor: don't use `storage` in `update_instance`

---
 src/instance.rs            | 25 ++++++++++++++-----------
 src/rpc/update_instance.rs | 18 ++++++++----------
 src/util.rs                |  4 ++++
 3 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/src/instance.rs b/src/instance.rs
index ceff216335..4c867175db 100644
--- a/src/instance.rs
+++ b/src/instance.rs
@@ -337,6 +337,7 @@ mod tests {
         let storage = Clusterwide::for_tests();
         add_tier(&storage, DEFAULT_TIER, 1, true).unwrap();
         let instance = add_instance(&storage, 1, "i1", "r1", &State::new(Online, 1)).unwrap();
+        let existing_fds = HashSet::new();
 
         //
         // Current state incarnation is allowed to go down,
@@ -344,7 +345,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_current_state(State::new(Offline, 0));
-        let (dml, do_bump) = update_instance(&instance, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("current_state", State::new(Offline, 0)).unwrap();
@@ -359,7 +360,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_current_state(State::new(Offline, 0));
-        let dml = update_instance(&instance, &req, &storage).unwrap();
+        let dml = update_instance(&instance, &req, &existing_fds).unwrap();
         assert_eq!(dml, None);
 
         //
@@ -367,7 +368,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_target_state(Offline);
-        let (dml, do_bump) = update_instance(&instance, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("target_state", State::new(Offline, 0)).unwrap();
@@ -382,7 +383,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_target_state(Online);
-        let (dml, do_bump) = update_instance(&instance, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("target_state", State::new(Online, 1)).unwrap();
@@ -397,7 +398,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_target_state(Online);
-        let (dml, do_bump) = update_instance(&instance, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("target_state", State::new(Online, 2)).unwrap();
@@ -412,7 +413,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_target_state(Expelled);
-        let (dml, do_bump) = update_instance(&instance, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("target_state", State::new(Expelled, 0)).unwrap();
@@ -427,7 +428,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_current_state(State::new(Expelled, 69));
-        let (dml, do_bump) = update_instance(&instance, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("current_state", State::new(Expelled, 69)).unwrap();
@@ -442,7 +443,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance.instance_id.clone(), "".into())
             .with_target_state(Online);
-        let e = update_instance(&instance, &req, &storage).unwrap_err();
+        let e = update_instance(&instance, &req, &existing_fds).unwrap_err();
         assert_eq!(e.to_string(), "cannot update expelled instance \"i1\"");
     }
 
@@ -516,6 +517,7 @@ mod tests {
         //
         let instance1 = build_instance(Some(&InstanceId::from("i1")), None, &faildoms! {planet: Earth}, &storage, DEFAULT_TIER).unwrap();
         storage.instances.put(&instance1).unwrap();
+        let existing_fds = storage.instances.failure_domain_names().unwrap();
         assert_eq!(instance1.failure_domain, faildoms! {planet: Earth});
         assert_eq!(instance1.replicaset_id, "r1");
 
@@ -524,7 +526,7 @@ mod tests {
         //
         let req = rpc::update_instance::Request::new(instance1.instance_id.clone(), "".into())
             .with_failure_domain(faildoms! {owner: Ivan});
-        let e = update_instance(&instance1, &req, &storage).unwrap_err();
+        let e = update_instance(&instance1, &req, &existing_fds).unwrap_err();
         assert_eq!(e.to_string(), "missing failure domain names: PLANET");
 
         //
@@ -533,7 +535,7 @@ mod tests {
         let fd = faildoms! {planet: Mars, owner: Ivan};
         let req = rpc::update_instance::Request::new(instance1.instance_id.clone(), "".into())
             .with_failure_domain(fd.clone());
-        let (dml, do_bump) = update_instance(&instance1, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance1, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("failure_domain", fd).unwrap();
@@ -556,6 +558,7 @@ mod tests {
         #[rustfmt::skip]
         let instance2 = build_instance(Some(&InstanceId::from("i2")), None, &fd, &storage, DEFAULT_TIER).unwrap();
         storage.instances.put(&instance2).unwrap();
+        let existing_fds = storage.instances.failure_domain_names().unwrap();
         assert_eq!(instance2.failure_domain, fd);
         // doesn't fit into r1
         assert_eq!(instance2.replicaset_id, "r2");
@@ -566,7 +569,7 @@ mod tests {
         let fd = faildoms! {planet: Earth, owner: Mike};
         let req = rpc::update_instance::Request::new(instance2.instance_id.clone(), "".into())
             .with_failure_domain(fd.clone());
-        let (dml, do_bump) = update_instance(&instance2, &req, &storage).unwrap().unwrap();
+        let (dml, do_bump) = update_instance(&instance2, &req, &existing_fds).unwrap().unwrap();
 
         let mut ops = UpdateOps::new();
         ops.assign("failure_domain", fd).unwrap();
diff --git a/src/rpc/update_instance.rs b/src/rpc/update_instance.rs
index 708a2ff7fc..a9704f0a81 100644
--- a/src/rpc/update_instance.rs
+++ b/src/rpc/update_instance.rs
@@ -1,5 +1,3 @@
-use std::time::Duration;
-
 use crate::cas;
 use crate::column_name;
 use crate::failure_domain::FailureDomain;
@@ -9,12 +7,14 @@ use crate::instance::StateVariant::*;
 use crate::instance::{Instance, InstanceId};
 use crate::replicaset::Replicaset;
 use crate::schema::ADMIN_ID;
-use crate::storage::Clusterwide;
 use crate::storage::ClusterwideTable;
 use crate::tier::Tier;
 use crate::traft::op::{Dml, Op};
 use crate::traft::Result;
 use crate::traft::{error::Error, node};
+use crate::util::Uppercase;
+use std::collections::HashSet;
+use std::time::Duration;
 use tarantool::fiber;
 use tarantool::space::UpdateOps;
 
@@ -125,7 +125,9 @@ pub fn handle_update_instance_request_and_wait(req: Request, timeout: Duration)
             return Err(Error::NoSuchReplicaset { id: replicaset_id.to_string(), id_is_uuid: false });
         };
 
-        let dml = update_instance(&instance, &req, storage)?;
+        let existing_fds = storage.instances.failure_domain_names()?;
+
+        let dml = update_instance(&instance, &req, &existing_fds)?;
         let Some((dml, version_bump_needed)) = dml else {
             // No point in proposing an operation which doesn't change anything.
             // Note: if the request tried setting target state Online while it
@@ -196,7 +198,7 @@ pub fn handle_update_instance_request_and_wait(req: Request, timeout: Duration)
 pub fn update_instance(
     instance: &Instance,
     req: &Request,
-    storage: &Clusterwide,
+    existing_fds: &HashSet<Uppercase>,
 ) -> Result<Option<(Dml, bool)>> {
     if instance.current_state.variant == Expelled
         && !matches!(
@@ -215,11 +217,7 @@ pub fn update_instance(
 
     let mut ops = UpdateOps::new();
     if let Some(fd) = req.failure_domain.as_ref() {
-        let existing_fds = storage
-            .instances
-            .failure_domain_names()
-            .expect("storage should not fail");
-        fd.check(&existing_fds)?;
+        fd.check(existing_fds)?;
         if fd != &instance.failure_domain {
             ops.assign(column_name!(Instance, failure_domain), fd)?;
         }
diff --git a/src/util.rs b/src/util.rs
index a12c556e68..a46be3600a 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -171,6 +171,10 @@ macro_rules! define_string_newtype {
 ////////////////////////////////////////////////////////////////////////////////
 /// A wrapper around `String` that garantees the string is uppercase by
 /// converting it to uppercase (if needed) on construction.
+// TODO: this is too much boilerplate for such a simple feature. We should
+// simplify and get rid of this struct, just use `String`, make sure it's
+// uppercase after we get it from the user (arguments, config, env, etc.)
+// and sprinkle `debug_assert` in places where it matters (when comparing values)
 #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, serde::Serialize)]
 pub struct Uppercase(String);
 
-- 
GitLab