Commit c11b6ed4 authored by Ramon Nou's avatar Ramon Nou
Browse files

Enable deterministic Random Slicing replicas

parent 3e2a785b
Loading
Loading
Loading
Loading
Loading
+3 −3
Changes for include/common/rpc/distribution_config.hpp: 3 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -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
+1 −2
Changes for src/client/preload.cpp: 1 added line, 2 removed lines.
Original line number Diff line number Diff line
@@ -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());
+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
@@ -28,6 +28,7 @@
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <set>
#include <string>
#include <cstring>

@@ -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
+0 −4
Changes for src/daemon/daemon.cpp: 0 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -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());
+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
@@ -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