diff --git a/crates/lox-distributor/src/lox_context.rs b/crates/lox-distributor/src/lox_context.rs index a9f8dc1..7bd92cd 100644 --- a/crates/lox-distributor/src/lox_context.rs +++ b/crates/lox-distributor/src/lox_context.rs @@ -13,7 +13,6 @@ use serde::{Deserialize, Serialize}; use lox_zkp::ProofError; use serde_json::json; use std::{ - cmp::Ordering, collections::HashMap, sync::{Arc, Mutex}, }; diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index 61a82be..e79d3cd 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -73,6 +73,7 @@ pub enum ReplaceSuccess { NotFound = 0, NotReplaced = 1, Replaced = 2, + Removed = 3, } /// This error is thrown if the number of buckets/keys in the bridge table @@ -546,6 +547,25 @@ impl BridgeAuth { Ok(()) } + // Remove an unallocated resource + pub fn remove_unallocated(&mut self, bridge: &BridgeLine) -> ReplaceSuccess { + // Check if the bridge is in the unallocated bridges and remove the bridge if so + // Bridges in spare buckets should have been moved to the unallocated bridges + if self.bridge_table.unallocated_bridges.contains(bridge) { + let index = self + .bridge_table + .unallocated_bridges + .iter() + .position(|x| x == bridge) + .unwrap(); + self.bridge_table.unallocated_bridges.swap_remove(index); + // A bridge that is in the unallocated_bridges is not exactly replaced + // but is successfully handled and no further action is needed + return ReplaceSuccess::Removed; + } + ReplaceSuccess::NotFound + } + /// Attempt to remove a bridge that is failing tests and replace it with a bridge from /// available_bridge or from a spare bucket pub fn bridge_replace( @@ -555,8 +575,23 @@ impl BridgeAuth { ) -> ReplaceSuccess { let reachable_bridges = &self.bridge_table.reachable.clone(); let Some(positions) = reachable_bridges.get(bridge) else { - return ReplaceSuccess::NotFound; + return self.remove_unallocated(bridge); }; + // Check if the bridge is in a spare bucket first, if it is, dissolve the bucket + if let Some(spare) = self + .bridge_table + .spares + .iter() + .find(|x| positions.iter().any(|(bucketnum, _)| &bucketnum == x)) + .cloned() + { + let Ok(_) = self.dissolve_spare_bucket(spare) else { + return ReplaceSuccess::NotReplaced; + }; + // Next Check if the bridge is in the unallocated bridges and remove the bridge if so + // Bridges in spare buckets should have been moved to the unallocated bridges + return self.remove_unallocated(bridge); + } // select replacement: // - first try the given bridge // - second try to pick one from the set of available bridges diff --git a/crates/lox-library/src/tests.rs b/crates/lox-library/src/tests.rs index e7bee96..e4c683c 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/src/tests.rs @@ -1032,22 +1032,39 @@ fn test_update_bridge() { #[test] fn test_bridge_replace() { // Create 3 open invitation buckets and 3 spare buckets - let cases = vec!["not found", "available", "unallocated", "spare", "failed"]; + let cases = vec![ + "not found", + "available", + "unallocated", + "use_spare", + "remove_spare", + "failed", + ]; let num_buckets = 5; let hot_spare = 0; for case in cases { + let table_size: usize; let mut th: TestHarness; - if String::from(case) != "failed" { - th = TestHarness::new(); - } else { - th = TestHarness::new_buckets(num_buckets, hot_spare); + match case { + "failed" => { + th = TestHarness::new_buckets(num_buckets, hot_spare); + table_size = th.ba.bridge_table.buckets.len(); + } + "remove_spare" => { + th = TestHarness::new_buckets(0, 5); + table_size = th.ba.bridge_table.buckets.len(); + } + _ => { + th = TestHarness::new(); + // Ensure that a randomly selected bucket isn't taken from the set of spare bridges + table_size = th.ba.bridge_table.buckets.len() - 5; + } } // Randomly select a bridge to replace - let table_size = th.ba.bridge_table.buckets.len(); let mut num = 100000; while !th.ba.bridge_table.buckets.contains_key(&num) { - num = rand::thread_rng().gen_range(0..th.ba.bridge_table.counter); + num = rand::thread_rng().gen_range(0..table_size as u32); } println!("chosen num is: {:?}", num); let replaceable_bucket = *th.ba.bridge_table.buckets.get(&num).unwrap(); @@ -1108,8 +1125,10 @@ fn test_bridge_replace() { .is_some(), "Replacement bridge not added to reachable bridges" ); + println!("Table Size {:?}", table_size); + println!("Bucket length {:?}", th.ba.bridge_table.buckets.len() - 5); assert!( - table_size == th.ba.bridge_table.buckets.len(), + table_size == th.ba.bridge_table.buckets.len() - 5, "Number of buckets changed size" ); assert!( @@ -1150,7 +1169,7 @@ fn test_bridge_replace() { "Replacement bridge not added to reachable bridges" ); assert!( - table_size == th.ba.bridge_table.buckets.len(), + table_size == th.ba.bridge_table.buckets.len() - 5, "Number of buckets changed size" ); assert!( @@ -1160,7 +1179,7 @@ fn test_bridge_replace() { println!("Successfully added unallocated bridgeline"); } - "spare" => { + "use_spare" => { // Case three: available_bridge == null and unallocated_bridges == null assert!( th.ba.bridge_table.unallocated_bridges.is_empty(), @@ -1180,7 +1199,7 @@ fn test_bridge_replace() { ); // Remove a spare bucket to replace bridge, buckets decrease by 1 assert!( - (table_size - 1) == th.ba.bridge_table.buckets.len(), + (table_size - 1) == th.ba.bridge_table.buckets.len() - 5, "Number of buckets changed size" ); assert!( @@ -1190,6 +1209,40 @@ fn test_bridge_replace() { println!("Successfully added bridgeline from spare"); } + "remove_spare" => { + // Case three: available_bridge == null and unallocated_bridges == null + assert!( + th.ba.bridge_table.unallocated_bridges.is_empty(), + "Unallocated bridges should have a length of 0" + ); + assert!( + th.ba.bridge_replace(replacement_bridge, None) == ReplaceSuccess::Removed, + "Bridge was replaced with available spare, instead of being removed" + ); + assert!( + th.ba.bridge_table.unallocated_bridges.len() == 2, + "Unallocated bridges should have a length of 2" + ); + assert!( + th.ba + .bridge_table + .reachable + .get(replacement_bridge) + .is_none(), + "Replacement bridge still marked as reachable" + ); + // Remove a spare bucket to replace bridge, buckets decrease by 1 + assert!( + (table_size - 1) == th.ba.bridge_table.buckets.len(), + "Number of buckets changed size" + ); + assert!( + th.ba.bridge_table.unallocated_bridges.len() == 2, + "Extra spare bridges not added to unallocated bridges" + ); + + println!("Successfully removed a spare bridgeline marked to be replaced"); + } "failed" => { // Case four: available_bridge == None and unallocated_bridges == None and spare buckets == None assert!(