From 94ab5e364eac2701f2672ae732f4fc8749b3bbf8 Mon Sep 17 00:00:00 2001 From: Georgy Moshkin <gmoshkin@picodata.io> Date: Mon, 27 Nov 2023 13:00:26 +0300 Subject: [PATCH] refactor: make it explicit that Clusterwide storage is only initialized once --- src/cas.rs | 4 ++-- src/instance.rs | 16 ++++++++-------- src/storage.rs | 30 ++++++++++++++++++++++++------ src/sync.rs | 2 +- src/traft/network.rs | 8 ++++---- 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/cas.rs b/src/cas.rs index 997db280bc..0ae3c21bb8 100644 --- a/src/cas.rs +++ b/src/cas.rs @@ -757,7 +757,7 @@ mod tests { #[::tarantool::test] fn ddl() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); let t = |op: &Op, range: Range| -> std::result::Result<(), Error> { let predicate = Predicate { @@ -879,7 +879,7 @@ mod tests { fn dml() { let key = (12,).to_tuple_buffer().unwrap(); let tuple = (12, "twelve").to_tuple_buffer().unwrap(); - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); let test = |op: &Dml, range: Range| { let predicate = Predicate { diff --git a/src/instance.rs b/src/instance.rs index 4ddda4437b..0c855e3d46 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -205,7 +205,7 @@ mod tests { #[::tarantool::test] fn test_simple() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![], 1); let instance = build_instance(None, None, &FailureDomain::default(), &storage, DEFAULT_TIER).unwrap(); @@ -239,7 +239,7 @@ mod tests { #[::tarantool::test] fn test_override() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![ Instance::new(Some(1), Some("i1"), Some("r1"), Grade::new(Online, 1), Grade::new(Online, 1), FailureDomain::default(), DEFAULT_TIER), Instance::new(Some(2), Some("i2"), Some("r2-original"), Grade::new(Offline, 0), Grade::new(Offline, 0), FailureDomain::default(), DEFAULT_TIER), @@ -291,7 +291,7 @@ mod tests { #[::tarantool::test] fn test_instance_id_collision() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![ Instance::new(Some(1), Some("i1"), Some("r1"), Grade::new(Online, 1), Grade::new(Online, 1), FailureDomain::default(), DEFAULT_TIER), Instance::new(Some(2), Some("i3"), Some("r3"), Grade::new(Online, 1), Grade::new(Online, 1), FailureDomain::default(), DEFAULT_TIER), @@ -306,7 +306,7 @@ mod tests { #[::tarantool::test] fn test_replication_factor() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![ Instance::new(Some(9), Some("i9"), Some("r9"), Grade::new(Online, 1), Grade::new(Online, 1), FailureDomain::default(), DEFAULT_TIER), Instance::new(Some(10), Some("i10"), Some("r9"), Grade::new(Online, 1), Grade::new(Online, 1), FailureDomain::default(), DEFAULT_TIER), @@ -348,7 +348,7 @@ mod tests { #[::tarantool::test] fn test_update_grade() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); let mut instance = Instance::new(Some(1), Some("i1"), Some("r1"), Grade::new(Online, 1), Grade::new(Online, 1), FailureDomain::default(), DEFAULT_TIER); setup_storage(&storage, vec![instance.clone()], 1); @@ -441,7 +441,7 @@ mod tests { #[::tarantool::test] fn failure_domain() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![], 3); let instance = @@ -507,7 +507,7 @@ mod tests { #[::tarantool::test] fn reconfigure_failure_domain() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![], 3); // first instance @@ -586,7 +586,7 @@ mod tests { let second_tier = "compute"; let third_tier = "trash"; - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); setup_storage(&storage, vec![], 1); add_tier(&storage, first_tier, 3).unwrap(); diff --git a/src/storage.rs b/src/storage.rs index ef26f7c4b7..f05459bbd2 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -123,8 +123,19 @@ macro_rules! define_clusterwide_tables { } impl $Clusterwide { + /// Initialize the clusterwide storage. + /// + /// This function is private because it should only be called once + /// per picodata instance on boot. #[inline(always)] - pub fn new() -> tarantool::Result<Self> { + fn initialize() -> tarantool::Result<Self> { + // SAFETY: safe as long as only called from tx thread. + static mut WAS_CALLED: bool = false; + unsafe { + assert!(!WAS_CALLED, "Clusterwide storage must only be initialized once"); + WAS_CALLED = true; + } + Ok(Self { $( $Clusterwide_field: $space_struct::new()?, )+ $( $Clusterwide_extra_field: Default::default(), )* @@ -360,7 +371,7 @@ impl Clusterwide { if !init { return Err(Error::Uninitialized); } - STORAGE = Some(Self::new()?); + STORAGE = Some(Self::initialize()?); } Ok(STORAGE.as_ref().unwrap()) } @@ -376,6 +387,13 @@ impl Clusterwide { Self::try_get(false).expect("shouldn't be calling this until it's initialized") } + /// Get an instance of clusterwide storage for use in unit tests. + /// + /// Should only be used in tests. + pub(crate) fn for_tests() -> Self { + Self::initialize().unwrap() + } + fn open_read_view(&self, entry_id: RaftEntryId) -> Result<SnapshotReadView> { let mut space_indexes = Vec::with_capacity(32); // ClusterwideTable::all_tables is guaranteed to iterate in order @@ -3152,7 +3170,7 @@ mod tests { use crate::instance::InstanceId; use crate::failure_domain::FailureDomain; - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); let storage_peer_addresses = PeerAddresses::new().unwrap(); let space_peer_addresses = storage_peer_addresses.space.clone(); @@ -3261,7 +3279,7 @@ mod tests { #[::tarantool::test] fn clusterwide_space_index() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); storage .space_by_name(ClusterwideTable::Address) @@ -3292,7 +3310,7 @@ mod tests { #[::tarantool::test] fn snapshot_data() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); storage.for_each_space(|s| s.truncate()).unwrap(); let i = Instance { @@ -3364,7 +3382,7 @@ mod tests { #[::tarantool::test] fn apply_snapshot_data() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); let mut data = SnapshotData { schema_version: 0, diff --git a/src/sync.rs b/src/sync.rs index 9c2cf7753e..5e19fdd4c5 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -186,7 +186,7 @@ mod tests { #[::tarantool::test] async fn vclock_proc() { - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); // Connect to the current Tarantool instance let pool = ConnectionPool::new(storage.clone(), Default::default()); let l = ::tarantool::lua_state(); diff --git a/src/traft/network.rs b/src/traft/network.rs index c69fb0d108..c8cb14687e 100644 --- a/src/traft/network.rs +++ b/src/traft/network.rs @@ -628,7 +628,7 @@ mod tests { ) .unwrap(); - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); // Connect to the current Tarantool instance let pool = ConnectionPool::new(storage.clone(), Default::default()); let listen: String = l.eval("return box.info.listen").unwrap(); @@ -673,7 +673,7 @@ mod tests { }), ); - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); // Connect to the current Tarantool instance let opts = WorkerOptions { raft_msg_handler: "test_interact", @@ -759,7 +759,7 @@ mod tests { }), ); - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); // Connect to the current Tarantool instance let opts = WorkerOptions { raft_msg_handler: "test_interact", @@ -837,7 +837,7 @@ mod tests { }), ); - let storage = Clusterwide::new().unwrap(); + let storage = Clusterwide::for_tests(); // Connect to the current Tarantool instance let opts = WorkerOptions { raft_msg_handler: "test_interact", -- GitLab