From abe5523418a7549dc9868acfcfb4c0fcee161178 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Tue, 18 Jul 2023 20:10:52 +0300
Subject: [PATCH] refactor: move schema related property keys handling into a
 common part

---
 src/cas.rs    | 65 +++++++++++++++++++++++++++++++++++++--------------
 src/schema.rs | 11 +--------
 2 files changed, 49 insertions(+), 27 deletions(-)

diff --git a/src/cas.rs b/src/cas.rs
index 2ee1417979..c0dd0eba99 100644
--- a/src/cas.rs
+++ b/src/cas.rs
@@ -25,8 +25,6 @@ use tarantool::space::{Space, SpaceId};
 use tarantool::tlua;
 use tarantool::tuple::{KeyDef, ToTupleBuffer, Tuple, TupleBuffer};
 
-use once_cell::sync::Lazy;
-
 /// This spaces cannot be changed directly dy a [`Dml`] operation. They have
 /// dedicated operation types (e.g. Ddl, Acl) because updating these spaces
 /// requires automatically updating corresponding local spaces.
@@ -395,20 +393,6 @@ impl Predicate {
             requested: self.index,
             conflict_index: entry_index,
         };
-        let ddl_keys: Lazy<Vec<Tuple>> = Lazy::new(|| {
-            use crate::storage::PropertyName::*;
-
-            [
-                PendingSchemaChange.into(),
-                PendingSchemaVersion.into(),
-                GlobalSchemaVersion.into(),
-                NextSchemaVersion.into(),
-            ]
-            .into_iter()
-            .map(|key: &str| Tuple::new(&(key,)))
-            .collect::<tarantool::Result<_>>()
-            .expect("keys should convert to tuple")
-        });
         for range in &self.ranges {
             if modifies_operable(entry_op, range.space, storage) {
                 return Err(error());
@@ -438,7 +422,7 @@ impl Predicate {
                 }
                 Op::DdlPrepare { .. } | Op::DdlCommit | Op::DdlAbort | Op::Acl { .. } => {
                     let key_def = storage.key_def_for_key(space, 0)?;
-                    for key in ddl_keys.iter() {
+                    for key in schema_related_property_keys() {
                         if range.contains(&key_def, key) {
                             return Err(error());
                         }
@@ -451,6 +435,53 @@ impl Predicate {
     }
 }
 
+const SCHEMA_RELATED_PROPERTIES: [&str; 4] = [
+    crate::storage::PropertyName::PendingSchemaChange.as_str(),
+    crate::storage::PropertyName::PendingSchemaVersion.as_str(),
+    crate::storage::PropertyName::GlobalSchemaVersion.as_str(),
+    crate::storage::PropertyName::NextSchemaVersion.as_str(),
+];
+
+/// Returns a slice of tuples representing keys of space _pico_property which
+/// should be used to check predicates of schema changing CaS operations.
+fn schema_related_property_keys() -> &'static [Tuple] {
+    static mut DATA: Option<Vec<Tuple>> = None;
+
+    // Safety: we only call this from tx thread, so it's ok, trust me
+    unsafe {
+        if DATA.is_none() {
+            let mut data = Vec::with_capacity(SCHEMA_RELATED_PROPERTIES.len());
+            for key in SCHEMA_RELATED_PROPERTIES {
+                let t = Tuple::new(&(key,)).expect("keys should convert to tuple");
+                data.push(t);
+            }
+            DATA = Some(data);
+        }
+
+        DATA.as_ref().unwrap()
+    }
+}
+
+/// Returns a slice of [`Range`] structs which are needed for the CaS
+/// request which performs a schema change operation.
+pub fn schema_change_ranges() -> &'static [Range] {
+    static mut DATA: Option<Vec<Range>> = None;
+
+    // Safety: we only call this from tx thread, so it's ok, trust me
+    unsafe {
+        if DATA.is_none() {
+            let mut data = Vec::with_capacity(SCHEMA_RELATED_PROPERTIES.len());
+            for key in SCHEMA_RELATED_PROPERTIES {
+                let r = Range::new(ClusterwideSpaceId::Property).eq((key,));
+                data.push(r);
+            }
+            DATA = Some(data);
+        }
+
+        DATA.as_ref().unwrap()
+    }
+}
+
 /// Represents a lua table describing a [`Range`].
 ///
 /// This is only used to parse lua arguments from lua api functions such as
diff --git a/src/schema.rs b/src/schema.rs
index f588320717..8ed1cb6ea0 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -631,16 +631,7 @@ pub fn prepare_schema_change(op: Op, timeout: Duration) -> traft::Result<RaftInd
         let predicate = cas::Predicate {
             index,
             term,
-            ranges: vec![
-                cas::Range::new(ClusterwideSpaceId::Property)
-                    .eq((PropertyName::PendingSchemaChange,)),
-                cas::Range::new(ClusterwideSpaceId::Property)
-                    .eq((PropertyName::PendingSchemaVersion,)),
-                cas::Range::new(ClusterwideSpaceId::Property)
-                    .eq((PropertyName::GlobalSchemaVersion,)),
-                cas::Range::new(ClusterwideSpaceId::Property)
-                    .eq((PropertyName::NextSchemaVersion,)),
-            ],
+            ranges: cas::schema_change_ranges().into(),
         };
         let (index, term) = compare_and_swap(op, predicate, timeout)?;
         node.wait_index(index, timeout)?;
-- 
GitLab