Loading include/common/rpc/distribution_config.hpp +3 −3 Changes for include/common/rpc/distribution_config.hpp: 3 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -61,11 +61,11 @@ public: return strategy_ == DistributionStrategy::SimpleHash; } /// Random Slicing does not yet define independent replica placement. /// Random Slicing supports deterministic replica placement. Mutation and /// CutShift still require separate safety checks. bool supports_replication(const int replica_count) const { return replica_count == 0 || strategy_ != DistributionStrategy::RandomSlicing; return replica_count >= 0; } /// Get the default strategy Loading src/client/preload.cpp +1 −2 Changes for src/client/preload.cpp: 1 added line, 2 removed lines. Original line number Diff line number Diff line Loading @@ -371,8 +371,7 @@ init_environment() { } if(!config.supports_replication(CTX->get_replicas())) { exit_error_msg(EXIT_FAILURE, "GKFS_NUM_REPL is not supported with random_slicing " "distribution"); "Invalid replica count for distribution strategy"); } LOG(INFO, "{}() Distribution strategy: '{}'", __func__, config.get_strategy_string()); Loading src/common/rpc/random_slicing_distributor.cpp +28 −12 Changes for src/common/rpc/random_slicing_distributor.cpp: 28 added lines, 12 removed lines. Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <sstream> #include <iomanip> #include <cstdlib> #include <set> #include <string> #include <cstring> Loading Loading @@ -250,21 +251,36 @@ host_t RandomSlicingDistributor::locate_data(const std::string& path, const chunkid_t& chnk_id, const int num_copy) const { // Simplification: return primary host only, same as current // SimpleHashDistributor. The replication is handled at a higher level by // the MDS. uint64_t seed = hash_seed(path, chnk_id); // Use mt19937 for better distribution quality (FISHER-YATES recommendation // #4 from thesis) std::mt19937_64 prng(static_cast<uint64_t>(seed ^ (seed >> 16))); const auto seed = hash_seed(path, chnk_id); std::mt19937_64 prng(seed ^ (seed >> 16)); std::uniform_real_distribution<float> dist(0.0f, 1.0f); float x = dist(prng); // x is in [0, 1) const auto primary = interval_idx_.find_host(x); if(num_copy <= 0 || all_hosts_.size() <= 1) { return primary; } const auto& intervals = interval_idx_.intervals(); const auto start = interval_idx_.find_partition(x); if(start < 0 || intervals.empty()) { return primary; } // Walk the authoritative interval table from the primary position and // select the requested distinct owner. This keeps placement deterministic // and honors the configured interval ownership. std::set<host_t> selected; for(std::size_t step = 0; step < intervals.size(); ++step) { const auto index = (static_cast<std::size_t>(start) + step) % intervals.size(); const auto candidate = intervals[index].host_id; if(selected.insert(candidate).second && selected.size() == static_cast<std::size_t>(num_copy + 1)) { return candidate; } } host_t host = interval_idx_.find_host(x); // Fallback should never happen if intervals cover [0, 1) return host; return primary; } host_t Loading src/daemon/daemon.cpp +0 −4 Changes for src/daemon/daemon.cpp: 0 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -748,10 +748,6 @@ init_environment() { if(!distributor) { throw std::runtime_error("Failed to create distributor"); } if(config.is_random_slicing() && GKFS_DATA->replica_count() > 0) { throw std::runtime_error( "Replication is not supported with random_slicing"); } if(config.is_random_slicing() && !GKFS_DATA->hosts_file().empty()) { auto rs = dynamic_cast<gkfs::rpc::RandomSlicingDistributor*>( distributor.get()); Loading tests/unit/test_random_slicing_distributor.cpp +28 −0 Changes for tests/unit/test_random_slicing_distributor.cpp: 28 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -109,6 +109,34 @@ TEST_CASE("RandomSlicingDistributor determinism", "[common][distributor][random_ REQUIRE(c2a == c2b); } TEST_CASE("RandomSlicingDistributor places replicas on distinct hosts", "[common][distributor][random_slicing][replication]") { auto d = gkfs::rpc::RandomSlicingDistributor(0, 4); const auto primary = d.locate_data("/replicated", 7, 0); const auto replica1 = d.locate_data("/replicated", 7, 1); const auto replica2 = d.locate_data("/replicated", 7, 2); REQUIRE(primary < 4); REQUIRE(replica1 < 4); REQUIRE(replica2 < 4); REQUIRE(primary != replica1); REQUIRE(primary != replica2); REQUIRE(replica1 != replica2); REQUIRE(d.locate_data("/replicated", 7, 1) == replica1); REQUIRE(d.locate_data("/replicated", 7, 2) == replica2); } TEST_CASE("RandomSlicingDistributor keeps primary placement unchanged by replicas", "[common][distributor][random_slicing][replication]") { auto d = gkfs::rpc::RandomSlicingDistributor(0, 4); for(unsigned int chunk = 0; chunk < 100; ++chunk) { const auto primary = d.locate_data("/primary", chunk, 0); REQUIRE(primary == d.locate_data("/primary", chunk, 0)); } } TEST_CASE("RandomSlicingDistributor bounds checking", "[common][distributor][random_slicing]") { auto d = gkfs::rpc::RandomSlicingDistributor(0, 10); Loading Loading
include/common/rpc/distribution_config.hpp +3 −3 Changes for include/common/rpc/distribution_config.hpp: 3 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -61,11 +61,11 @@ public: return strategy_ == DistributionStrategy::SimpleHash; } /// Random Slicing does not yet define independent replica placement. /// Random Slicing supports deterministic replica placement. Mutation and /// CutShift still require separate safety checks. bool supports_replication(const int replica_count) const { return replica_count == 0 || strategy_ != DistributionStrategy::RandomSlicing; return replica_count >= 0; } /// Get the default strategy Loading
src/client/preload.cpp +1 −2 Changes for src/client/preload.cpp: 1 added line, 2 removed lines. Original line number Diff line number Diff line Loading @@ -371,8 +371,7 @@ init_environment() { } if(!config.supports_replication(CTX->get_replicas())) { exit_error_msg(EXIT_FAILURE, "GKFS_NUM_REPL is not supported with random_slicing " "distribution"); "Invalid replica count for distribution strategy"); } LOG(INFO, "{}() Distribution strategy: '{}'", __func__, config.get_strategy_string()); Loading
src/common/rpc/random_slicing_distributor.cpp +28 −12 Changes for src/common/rpc/random_slicing_distributor.cpp: 28 added lines, 12 removed lines. Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <sstream> #include <iomanip> #include <cstdlib> #include <set> #include <string> #include <cstring> Loading Loading @@ -250,21 +251,36 @@ host_t RandomSlicingDistributor::locate_data(const std::string& path, const chunkid_t& chnk_id, const int num_copy) const { // Simplification: return primary host only, same as current // SimpleHashDistributor. The replication is handled at a higher level by // the MDS. uint64_t seed = hash_seed(path, chnk_id); // Use mt19937 for better distribution quality (FISHER-YATES recommendation // #4 from thesis) std::mt19937_64 prng(static_cast<uint64_t>(seed ^ (seed >> 16))); const auto seed = hash_seed(path, chnk_id); std::mt19937_64 prng(seed ^ (seed >> 16)); std::uniform_real_distribution<float> dist(0.0f, 1.0f); float x = dist(prng); // x is in [0, 1) const auto primary = interval_idx_.find_host(x); if(num_copy <= 0 || all_hosts_.size() <= 1) { return primary; } const auto& intervals = interval_idx_.intervals(); const auto start = interval_idx_.find_partition(x); if(start < 0 || intervals.empty()) { return primary; } // Walk the authoritative interval table from the primary position and // select the requested distinct owner. This keeps placement deterministic // and honors the configured interval ownership. std::set<host_t> selected; for(std::size_t step = 0; step < intervals.size(); ++step) { const auto index = (static_cast<std::size_t>(start) + step) % intervals.size(); const auto candidate = intervals[index].host_id; if(selected.insert(candidate).second && selected.size() == static_cast<std::size_t>(num_copy + 1)) { return candidate; } } host_t host = interval_idx_.find_host(x); // Fallback should never happen if intervals cover [0, 1) return host; return primary; } host_t Loading
src/daemon/daemon.cpp +0 −4 Changes for src/daemon/daemon.cpp: 0 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -748,10 +748,6 @@ init_environment() { if(!distributor) { throw std::runtime_error("Failed to create distributor"); } if(config.is_random_slicing() && GKFS_DATA->replica_count() > 0) { throw std::runtime_error( "Replication is not supported with random_slicing"); } if(config.is_random_slicing() && !GKFS_DATA->hosts_file().empty()) { auto rs = dynamic_cast<gkfs::rpc::RandomSlicingDistributor*>( distributor.get()); Loading
tests/unit/test_random_slicing_distributor.cpp +28 −0 Changes for tests/unit/test_random_slicing_distributor.cpp: 28 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -109,6 +109,34 @@ TEST_CASE("RandomSlicingDistributor determinism", "[common][distributor][random_ REQUIRE(c2a == c2b); } TEST_CASE("RandomSlicingDistributor places replicas on distinct hosts", "[common][distributor][random_slicing][replication]") { auto d = gkfs::rpc::RandomSlicingDistributor(0, 4); const auto primary = d.locate_data("/replicated", 7, 0); const auto replica1 = d.locate_data("/replicated", 7, 1); const auto replica2 = d.locate_data("/replicated", 7, 2); REQUIRE(primary < 4); REQUIRE(replica1 < 4); REQUIRE(replica2 < 4); REQUIRE(primary != replica1); REQUIRE(primary != replica2); REQUIRE(replica1 != replica2); REQUIRE(d.locate_data("/replicated", 7, 1) == replica1); REQUIRE(d.locate_data("/replicated", 7, 2) == replica2); } TEST_CASE("RandomSlicingDistributor keeps primary placement unchanged by replicas", "[common][distributor][random_slicing][replication]") { auto d = gkfs::rpc::RandomSlicingDistributor(0, 4); for(unsigned int chunk = 0; chunk < 100; ++chunk) { const auto primary = d.locate_data("/primary", chunk, 0); REQUIRE(primary == d.locate_data("/primary", chunk, 0)); } } TEST_CASE("RandomSlicingDistributor bounds checking", "[common][distributor][random_slicing]") { auto d = gkfs::rpc::RandomSlicingDistributor(0, 10); Loading