diff options
25 files changed, 299 insertions, 467 deletions
diff --git a/storage/src/tests/distributor/distributor_bucket_space_test.cpp b/storage/src/tests/distributor/distributor_bucket_space_test.cpp index 3ea4c1ca3c2..00bc803e81c 100644 --- a/storage/src/tests/distributor/distributor_bucket_space_test.cpp +++ b/storage/src/tests/distributor/distributor_bucket_space_test.cpp @@ -100,10 +100,10 @@ DistributorBucketSpaceTest::CountVector DistributorBucketSpaceTest::count_service_layer_buckets(const std::vector<BucketId>& buckets) { CountVector result(3); - std::vector<uint16_t> ideal_nodes; for (auto& bucket : buckets) { const auto & ideal_nodes_bundle = bucket_space.get_ideal_service_layer_nodes_bundle(bucket); for (uint32_t i = 0; i < 3; ++i) { + IdealServiceLayerNodesBundle::ConstNodesRef ideal_nodes; switch (i) { case 0: ideal_nodes = ideal_nodes_bundle.available_nodes(); diff --git a/storage/src/tests/distributor/operationtargetresolvertest.cpp b/storage/src/tests/distributor/operationtargetresolvertest.cpp index 2d41b0f4d32..19ca81e933f 100644 --- a/storage/src/tests/distributor/operationtargetresolvertest.cpp +++ b/storage/src/tests/distributor/operationtargetresolvertest.cpp @@ -3,7 +3,6 @@ #include <tests/distributor/distributor_stripe_test_util.h> #include <vespa/config/helper/configgetter.h> #include <vespa/config/helper/configgetter.hpp> -#include <vespa/document/config/config-documenttypes.h> #include <vespa/document/repo/documenttyperepo.h> #include <vespa/document/test/make_bucket_space.h> #include <vespa/document/test/make_document_bucket.h> @@ -14,7 +13,6 @@ #include <vespa/storageapi/message/bucket.h> #include <vespa/storageapi/message/persistence.h> #include <vespa/vdslib/distribution/distribution.h> -#include <vespa/vdslib/distribution/idealnodecalculatorimpl.h> #include <vespa/vespalib/gtest/gtest.h> using document::BucketId; @@ -112,14 +110,10 @@ struct TestTargets { } // anonymous BucketInstanceList -OperationTargetResolverTest::getInstances(const BucketId& id, - bool stripToRedundancy) +OperationTargetResolverTest::getInstances(const BucketId& id, bool stripToRedundancy) { - lib::IdealNodeCalculatorImpl idealNodeCalc; auto &bucketSpaceRepo(operation_context().bucket_space_repo()); auto &distributorBucketSpace(bucketSpaceRepo.get(makeBucketSpace())); - idealNodeCalc.setDistribution(distributorBucketSpace.getDistribution()); - idealNodeCalc.setClusterState(distributorBucketSpace.getClusterState()); OperationTargetResolverImpl resolver( distributorBucketSpace, distributorBucketSpace.getBucketDatabase(), 16, distributorBucketSpace.getDistribution().getRedundancy(), @@ -142,24 +136,6 @@ TEST_F(OperationTargetResolverTest, simple) { .sendsTo(BucketId(16, 0), 0); } -TEST_F(OperationTargetResolverTest, multiple_nodes) { - setup_stripe(1, 2, "storage:2 distributor:1"); - - auto &bucketSpaceRepo(operation_context().bucket_space_repo()); - auto &distributorBucketSpace(bucketSpaceRepo.get(makeBucketSpace())); - for (int i = 0; i < 100; ++i) { - addNodesToBucketDB(BucketId(16, i), "0=0,1=0"); - - lib::IdealNodeCalculatorImpl idealNodeCalc; - idealNodeCalc.setDistribution(distributorBucketSpace.getDistribution()); - idealNodeCalc.setClusterState(distributorBucketSpace.getClusterState()); - lib::IdealNodeList idealNodes( - idealNodeCalc.getIdealStorageNodes(BucketId(16, i))); - uint16_t expectedNode = idealNodes[0].getIndex(); - MY_ASSERT_THAT(BucketId(32, i)).sendsTo(BucketId(16, i), expectedNode); - } -} - TEST_F(OperationTargetResolverTest, choose_ideal_state_when_many_copies) { setup_stripe(2, 4, "storage:4 distributor:1"); addNodesToBucketDB(BucketId(16, 0), "0=0,1=0,2=0,3=0"); // ideal nodes: 1, 3 diff --git a/storage/src/tests/distributor/statecheckerstest.cpp b/storage/src/tests/distributor/statecheckerstest.cpp index 4ca4d70a816..87a825fdc9a 100644 --- a/storage/src/tests/distributor/statecheckerstest.cpp +++ b/storage/src/tests/distributor/statecheckerstest.cpp @@ -7,6 +7,7 @@ #include <vespa/document/test/make_document_bucket.h> #include <vespa/storage/distributor/top_level_bucket_db_updater.h> #include <vespa/storage/distributor/top_level_distributor.h> +#include <vespa/storage/distributor/activecopy.h> #include <vespa/storage/distributor/distributor_bucket_space.h> #include <vespa/storage/distributor/distributor_stripe.h> #include <vespa/storage/distributor/operations/idealstate/mergeoperation.h> @@ -1587,7 +1588,8 @@ TEST_F(StateCheckersTest, context_populates_ideal_state_containers) { StateChecker::Context c(node_context(), operation_context(), getDistributorBucketSpace(), statsTracker, makeDocumentBucket({17, 0})); - ASSERT_THAT(c.idealState(), ElementsAre(1, 3)); + ASSERT_EQ(1, c.idealState()[0]); + ASSERT_EQ(3, c.idealState()[1]); for (uint16_t node : c.idealState()) { ASSERT_TRUE(c.idealStateBundle.is_nonretired_or_maintenance(node)); } @@ -1736,4 +1738,9 @@ TEST_F(StateCheckersTest, stats_updates_for_maximum_time_since_gc_run) { EXPECT_EQ(runner.stats().max_observed_time_since_last_gc(), 1900s); } +TEST(ActiveCopyTest, control_size) { + EXPECT_EQ(12, sizeof(ActiveCopy)); + EXPECT_EQ(64, sizeof(IdealServiceLayerNodesBundle)); +} + } diff --git a/storage/src/vespa/storage/common/distributorcomponent.h b/storage/src/vespa/storage/common/distributorcomponent.h index 06bb49a6090..6542bf2ddfe 100644 --- a/storage/src/vespa/storage/common/distributorcomponent.h +++ b/storage/src/vespa/storage/common/distributorcomponent.h @@ -34,13 +34,6 @@ namespace storage { -namespace bucketdb { - class DistrBucketDatabase; -} -namespace lib { - class IdealNodeCalculator; -} - using DistributorConfig = vespa::config::content::core::internal::InternalStorDistributormanagerType; using VisitorConfig = vespa::config::content::core::internal::InternalStorVisitordispatcherType; diff --git a/storage/src/vespa/storage/distributor/activecopy.cpp b/storage/src/vespa/storage/distributor/activecopy.cpp index 5d59d1a838f..54524b5deef 100644 --- a/storage/src/vespa/storage/distributor/activecopy.cpp +++ b/storage/src/vespa/storage/distributor/activecopy.cpp @@ -28,26 +28,9 @@ namespace storage::distributor { using IndexList = lib::Distribution::IndexList; -ActiveCopy::ActiveCopy(uint16_t node, const BucketDatabase::Entry& e, const std::vector<uint16_t>& idealState) - : _nodeIndex(node), - _ideal(0xffff) -{ - const BucketCopy* copy = e->getNode(node); - assert(copy != nullptr); - _doc_count = copy->getDocumentCount(); - _ready = copy->ready(); - _active = copy->active(); - for (uint32_t i=0; i<idealState.size(); ++i) { - if (idealState[i] == node) { - _ideal = i; - break; - } - } -} - vespalib::string ActiveCopy::getReason() const { - if (_ready && (_doc_count > 0) && (_ideal < 0xffff)) { + if (_ready && (_doc_count > 0) && valid_ideal()) { vespalib::asciistream ost; ost << "copy is ready, has " << _doc_count << " docs and ideal state priority " << _ideal; @@ -58,7 +41,7 @@ ActiveCopy::getReason() const { return ost.str(); } else if (_ready) { return "copy is ready"; - } else if ((_doc_count > 0) && (_ideal < 0xffff)) { + } else if ((_doc_count > 0) && valid_ideal()) { vespalib::asciistream ost; ost << "copy has " << _doc_count << " docs and ideal state priority " << _ideal; return ost.str(); @@ -68,7 +51,7 @@ ActiveCopy::getReason() const { return ost.str(); } else if (_active) { return "copy is already active"; - } else if (_ideal < 0xffff) { + } else if (valid_ideal()) { vespalib::asciistream ost; ost << "copy is ideal state priority " << _ideal; return ost.str(); @@ -86,7 +69,7 @@ operator<<(std::ostream& out, const ActiveCopy & e) { if (e._doc_count > 0) { out << ", doc_count " << e._doc_count; } - if (e._ideal < 0xffff) { + if (e.valid_ideal()) { out << ", ideal pri " << e._ideal; } out << ")"; @@ -95,26 +78,8 @@ operator<<(std::ostream& out, const ActiveCopy & e) { namespace { -struct ActiveStateOrder { - bool operator()(const ActiveCopy & e1, const ActiveCopy & e2) noexcept { - if (e1._ready != e2._ready) { - return e1._ready; - } - if (e1._doc_count != e2._doc_count) { - return e1._doc_count > e2._doc_count; - } - if (e1._ideal != e2._ideal) { - return e1._ideal < e2._ideal; - } - if (e1._active != e2._active) { - return e1._active; - } - return e1._nodeIndex < e2._nodeIndex; - } -}; - IndexList -buildValidNodeIndexList(BucketDatabase::Entry& e) { +buildValidNodeIndexList(const BucketDatabase::Entry& e) { IndexList result; result.reserve(e->getNodeCount()); for (uint32_t i=0, n=e->getNodeCount(); i < n; ++i) { @@ -126,22 +91,45 @@ buildValidNodeIndexList(BucketDatabase::Entry& e) { return result; } -std::vector<ActiveCopy> -buildNodeList(BucketDatabase::Entry& e,vespalib::ConstArrayRef<uint16_t> nodeIndexes, const std::vector<uint16_t>& idealState) +using SmallActiveCopyList = vespalib::SmallVector<ActiveCopy, 2>; +static_assert(sizeof(SmallActiveCopyList) == 40); + +SmallActiveCopyList +buildNodeList(const BucketDatabase::Entry& e,vespalib::ConstArrayRef<uint16_t> nodeIndexes, const IdealServiceLayerNodesBundle::Node2Index & idealState) { - std::vector<ActiveCopy> result; + SmallActiveCopyList result; result.reserve(nodeIndexes.size()); for (uint16_t nodeIndex : nodeIndexes) { - result.emplace_back(nodeIndex, e, idealState); + const BucketCopy *copy = e->getNode(nodeIndex); + assert(copy); + result.emplace_back(nodeIndex, *copy, idealState.lookup(nodeIndex)); } return result; } } +struct ActiveStateOrder { + bool operator()(const ActiveCopy & e1, const ActiveCopy & e2) noexcept { + if (e1._ready != e2._ready) { + return e1._ready; + } + if (e1._doc_count != e2._doc_count) { + return e1._doc_count > e2._doc_count; + } + if (e1._ideal != e2._ideal) { + return e1._ideal < e2._ideal; + } + if (e1._active != e2._active) { + return e1._active; + } + return e1.nodeIndex() < e2.nodeIndex(); + } +}; + ActiveList -ActiveCopy::calculate(const std::vector<uint16_t>& idealState, const lib::Distribution& distribution, - BucketDatabase::Entry& e, uint32_t max_activation_inhibited_out_of_sync_groups) +ActiveCopy::calculate(const Node2Index & idealState, const lib::Distribution& distribution, + const BucketDatabase::Entry& e, uint32_t max_activation_inhibited_out_of_sync_groups) { IndexList validNodesWithCopy = buildValidNodeIndexList(e); if (validNodesWithCopy.empty()) { @@ -161,7 +149,7 @@ ActiveCopy::calculate(const std::vector<uint16_t>& idealState, const lib::Distri : api::BucketInfo()); // Invalid by default uint32_t inhibited_groups = 0; for (const auto& group_nodes : groups) { - std::vector<ActiveCopy> entries = buildNodeList(e, group_nodes, idealState); + SmallActiveCopyList entries = buildNodeList(e, group_nodes, idealState); auto best = std::min_element(entries.begin(), entries.end(), ActiveStateOrder()); if ((groups.size() > 1) && (inhibited_groups < max_activation_inhibited_out_of_sync_groups) && @@ -179,24 +167,22 @@ ActiveCopy::calculate(const std::vector<uint16_t>& idealState, const lib::Distri } void -ActiveList::print(std::ostream& out, bool verbose, - const std::string& indent) const +ActiveList::print(std::ostream& out, bool verbose, const std::string& indent) const { out << "["; if (verbose) { for (size_t i=0; i<_v.size(); ++i) { - out << "\n" << indent << " " - << _v[i]._nodeIndex << " " << _v[i].getReason(); + out << "\n" << indent << " " << _v[i].nodeIndex() << " " << _v[i].getReason(); } if (!_v.empty()) { out << "\n" << indent; } } else { if (!_v.empty()) { - out << _v[0]._nodeIndex; + out << _v[0].nodeIndex(); } for (size_t i=1; i<_v.size(); ++i) { - out << " " << _v[i]._nodeIndex; + out << " " << _v[i].nodeIndex(); } } out << "]"; @@ -206,7 +192,7 @@ bool ActiveList::contains(uint16_t node) const noexcept { for (const auto& candidate : _v) { - if (node == candidate._nodeIndex) { + if (node == candidate.nodeIndex()) { return true; } } diff --git a/storage/src/vespa/storage/distributor/activecopy.h b/storage/src/vespa/storage/distributor/activecopy.h index 258fe3cdf16..a2de77306be 100644 --- a/storage/src/vespa/storage/distributor/activecopy.h +++ b/storage/src/vespa/storage/distributor/activecopy.h @@ -2,25 +2,43 @@ #pragma once +#include "ideal_service_layer_nodes_bundle.h" #include <vespa/storage/bucketdb/bucketdatabase.h> namespace storage::lib { class Distribution; } namespace storage::distributor { class ActiveList; +struct ActiveStateOrder; -struct ActiveCopy { - constexpr ActiveCopy() noexcept : _nodeIndex(-1), _ideal(-1), _doc_count(0), _ready(false), _active(false) { } - ActiveCopy(uint16_t node, const BucketDatabase::Entry& e, const std::vector<uint16_t>& idealState); +class ActiveCopy { + using Index = IdealServiceLayerNodesBundle::Index; + using Node2Index = IdealServiceLayerNodesBundle::Node2Index; +public: + constexpr ActiveCopy() noexcept + : _nodeIndex(Index::invalid()), + _ideal(Index::invalid()), + _doc_count(0), + _ready(false), + _active(false) + { } + ActiveCopy(uint16_t node, const BucketCopy & copy, uint16_t ideal) noexcept + : _nodeIndex(node), + _ideal(ideal), + _doc_count(copy.getDocumentCount()), + _ready(copy.ready()), + _active(copy.active()) + { } vespalib::string getReason() const; friend std::ostream& operator<<(std::ostream& out, const ActiveCopy& e); - static ActiveList calculate(const std::vector<uint16_t>& idealState, - const lib::Distribution&, - BucketDatabase::Entry&, - uint32_t max_activation_inhibited_out_of_sync_groups); - + static ActiveList calculate(const Node2Index & idealState, const lib::Distribution&, + const BucketDatabase::Entry&, uint32_t max_activation_inhibited_out_of_sync_groups); + uint16_t nodeIndex() const noexcept { return _nodeIndex; } +private: + friend ActiveStateOrder; + bool valid_ideal() const noexcept { return _ideal < Index::invalid(); } uint16_t _nodeIndex; uint16_t _ideal; uint32_t _doc_count; @@ -29,8 +47,6 @@ struct ActiveCopy { }; class ActiveList : public vespalib::Printable { - std::vector<ActiveCopy> _v; - public: ActiveList() {} ActiveList(std::vector<ActiveCopy>&& v) : _v(std::move(v)) { } @@ -41,6 +57,8 @@ public: [[nodiscard]] bool empty() const noexcept { return _v.empty(); } size_t size() const noexcept { return _v.size(); } void print(std::ostream&, bool verbose, const std::string& indent) const override; +private: + std::vector<ActiveCopy> _v; }; } diff --git a/storage/src/vespa/storage/distributor/distributor_bucket_space.cpp b/storage/src/vespa/storage/distributor/distributor_bucket_space.cpp index 299aaffb569..7ba9c67b156 100644 --- a/storage/src/vespa/storage/distributor/distributor_bucket_space.cpp +++ b/storage/src/vespa/storage/distributor/distributor_bucket_space.cpp @@ -121,9 +121,9 @@ setup_ideal_nodes_bundle(IdealServiceLayerNodesBundle& ideal_nodes_bundle, const lib::ClusterState& cluster_state, document::BucketId bucket) { - ideal_nodes_bundle.set_available_nodes(distribution.getIdealStorageNodes(cluster_state, bucket, up_states)); - ideal_nodes_bundle.set_available_nonretired_nodes(distribution.getIdealStorageNodes(cluster_state, bucket, nonretired_up_states)); - ideal_nodes_bundle.set_available_nonretired_or_maintenance_nodes(distribution.getIdealStorageNodes(cluster_state, bucket, nonretired_or_maintenance_up_states)); + ideal_nodes_bundle.set_nodes(distribution.getIdealStorageNodes(cluster_state, bucket, up_states), + distribution.getIdealStorageNodes(cluster_state, bucket, nonretired_up_states), + distribution.getIdealStorageNodes(cluster_state, bucket, nonretired_or_maintenance_up_states)); } /* diff --git a/storage/src/vespa/storage/distributor/distributor_stripe_component.cpp b/storage/src/vespa/storage/distributor/distributor_stripe_component.cpp index 16cc887096f..9c44ef87cce 100644 --- a/storage/src/vespa/storage/distributor/distributor_stripe_component.cpp +++ b/storage/src/vespa/storage/distributor/distributor_stripe_component.cpp @@ -53,18 +53,19 @@ class UpdateBucketDatabaseProcessor : public BucketDatabase::EntryUpdateProcesso const std::vector<BucketCopy>& _changed_nodes; std::vector<uint16_t> _ideal_nodes; bool _reset_trusted; + using ConstNodesRef = IdealServiceLayerNodesBundle::ConstNodesRef; public: - UpdateBucketDatabaseProcessor(const framework::Clock& clock, const std::vector<BucketCopy>& changed_nodes, std::vector<uint16_t> ideal_nodes, bool reset_trusted); + UpdateBucketDatabaseProcessor(const framework::Clock& clock, const std::vector<BucketCopy>& changed_nodes, ConstNodesRef ideal_nodes, bool reset_trusted); ~UpdateBucketDatabaseProcessor() override; BucketDatabase::Entry create_entry(const document::BucketId& bucket) const override; bool process_entry(BucketDatabase::Entry &entry) const override; }; -UpdateBucketDatabaseProcessor::UpdateBucketDatabaseProcessor(const framework::Clock& clock, const std::vector<BucketCopy>& changed_nodes, std::vector<uint16_t> ideal_nodes, bool reset_trusted) +UpdateBucketDatabaseProcessor::UpdateBucketDatabaseProcessor(const framework::Clock& clock, const std::vector<BucketCopy>& changed_nodes, ConstNodesRef ideal_nodes, bool reset_trusted) : BucketDatabase::EntryUpdateProcessor(), _clock(clock), _changed_nodes(changed_nodes), - _ideal_nodes(std::move(ideal_nodes)), + _ideal_nodes(ideal_nodes.cbegin(), ideal_nodes.cend()), _reset_trusted(reset_trusted) { } diff --git a/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.cpp b/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.cpp index cc4eedd2a35..1ce5e5c589f 100644 --- a/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.cpp +++ b/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.cpp @@ -1,30 +1,60 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include "ideal_service_layer_nodes_bundle.h" -#include <vespa/vdslib/distribution/idealnodecalculator.h> -#include <vespa/vespalib/stllike/hash_set_insert.hpp> - +#include <vespa/vespalib/stllike/hash_map.hpp> namespace storage::distributor { -IdealServiceLayerNodesBundle::IdealServiceLayerNodesBundle() noexcept - : _available_nodes(), - _available_nonretired_nodes(), - _available_nonretired_or_maintenance_nodes(), - _unordered_nonretired_or_maintenance_nodes() -{ +namespace { +constexpr size_t BUILD_HASH_LIMIT = 32; } +struct IdealServiceLayerNodesBundle::LookupMap : public vespalib::hash_map<uint16_t, Index> { + using Parent = vespalib::hash_map<uint16_t, Index>; + using Parent::Parent; +}; + +IdealServiceLayerNodesBundle::IdealServiceLayerNodesBundle() noexcept = default; +IdealServiceLayerNodesBundle::IdealServiceLayerNodesBundle(IdealServiceLayerNodesBundle &&) noexcept = default; +IdealServiceLayerNodesBundle::~IdealServiceLayerNodesBundle() = default; + void -IdealServiceLayerNodesBundle::set_available_nonretired_or_maintenance_nodes(std::vector<uint16_t> available_nonretired_or_maintenance_nodes) { - _available_nonretired_or_maintenance_nodes = std::move(available_nonretired_or_maintenance_nodes); - _unordered_nonretired_or_maintenance_nodes.clear(); - _unordered_nonretired_or_maintenance_nodes.insert(_available_nonretired_or_maintenance_nodes.begin(), - _available_nonretired_or_maintenance_nodes.end()); +IdealServiceLayerNodesBundle::set_nodes(ConstNodesRef nodes, + ConstNodesRef nonretired_nodes, + ConstNodesRef nonretired_or_maintenance_nodes) +{ + _nodes.clear(); + _nodes.reserve(nodes.size() + nonretired_nodes.size() + nonretired_or_maintenance_nodes.size()); + std::for_each(nodes.cbegin(), nodes.cend(), [this](uint16_t n) { _nodes.emplace_back(n); }); + _available_sz = nodes.size(); + std::for_each(nonretired_nodes.cbegin(), nonretired_nodes.cend(), [this](uint16_t n) { _nodes.emplace_back(n); }); + _nonretired_sz = nonretired_nodes.size(); + std::for_each(nonretired_or_maintenance_nodes.cbegin(), nonretired_or_maintenance_nodes.cend(), [this](uint16_t n) { _nodes.emplace_back(n); }); + + if (nonretired_or_maintenance_nodes.size() > BUILD_HASH_LIMIT) { + _nonretired_or_maintenance_node_2_index = std::make_unique<LookupMap>(nonretired_or_maintenance_nodes.size()); + for (uint16_t i(0); i < nonretired_or_maintenance_nodes.size(); i++) { + _nonretired_or_maintenance_node_2_index->insert(std::make_pair(nonretired_or_maintenance_nodes[i], Index(i))); + } + } } -IdealServiceLayerNodesBundle::IdealServiceLayerNodesBundle(IdealServiceLayerNodesBundle &&) noexcept = default; +IdealServiceLayerNodesBundle::Index +IdealServiceLayerNodesBundle::ConstNodesRef2Index::lookup(uint16_t node) const noexcept { + for (uint16_t i(0); i < _idealState.size(); i++) { + if (node == _idealState[i]) return Index(i); + } + return Index::invalid(); +} -IdealServiceLayerNodesBundle::~IdealServiceLayerNodesBundle() = default; +IdealServiceLayerNodesBundle::Index +IdealServiceLayerNodesBundle::nonretired_or_maintenance_index(uint16_t node) const noexcept { + if (_nonretired_or_maintenance_node_2_index) { + const auto found = _nonretired_or_maintenance_node_2_index->find(node); + return (found != _nonretired_or_maintenance_node_2_index->end()) ? found->second : Index::invalid(); + } else { + return ConstNodesRef2Index(available_nonretired_or_maintenance_nodes()).lookup(node); + } +} } diff --git a/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.h b/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.h index 9577ec09208..38ec0e37044 100644 --- a/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.h +++ b/storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.h @@ -1,7 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once -#include <vespa/vespalib/stllike/hash_set.h> +#include <vespa/vespalib/util/small_vector.h> namespace storage::distributor { @@ -9,30 +9,67 @@ namespace storage::distributor { * Bundle of ideal service layer nodes for a bucket. */ class IdealServiceLayerNodesBundle { - std::vector<uint16_t> _available_nodes; - std::vector<uint16_t> _available_nonretired_nodes; - std::vector<uint16_t> _available_nonretired_or_maintenance_nodes; - vespalib::hash_set<uint16_t> _unordered_nonretired_or_maintenance_nodes; public: + using ConstNodesRef = vespalib::ConstArrayRef<uint16_t>; + class Index { + public: + constexpr explicit Index(uint16_t index) noexcept : _index(index) {} + constexpr bool valid() const noexcept { + return _index < MAX_INDEX; + } + constexpr operator uint16_t () const noexcept { return _index; } + static constexpr Index invalid() noexcept { return Index(MAX_INDEX); } + private: + static constexpr uint16_t MAX_INDEX = 0xffff; + uint16_t _index; + }; + struct Node2Index { + virtual ~Node2Index() = default; + virtual Index lookup(uint16_t node) const noexcept = 0; + }; + class NonRetiredOrMaintenance2Index final : public Node2Index { + public: + NonRetiredOrMaintenance2Index(const IdealServiceLayerNodesBundle & idealState) noexcept : _idealState(idealState) {} + Index lookup(uint16_t node) const noexcept override { + return _idealState.nonretired_or_maintenance_index(node); + } + private: + const IdealServiceLayerNodesBundle & _idealState; + }; + class ConstNodesRef2Index final : public Node2Index { + public: + ConstNodesRef2Index(ConstNodesRef idealState) noexcept : _idealState(idealState) {} + Index lookup(uint16_t node) const noexcept override; + private: + ConstNodesRef _idealState; + }; IdealServiceLayerNodesBundle() noexcept; IdealServiceLayerNodesBundle(IdealServiceLayerNodesBundle &&) noexcept; ~IdealServiceLayerNodesBundle(); - void set_available_nodes(std::vector<uint16_t> available_nodes) { - _available_nodes = std::move(available_nodes); + void set_nodes(ConstNodesRef nodes, ConstNodesRef nonretired_nodes, ConstNodesRef nonretired_or_maintenance_nodes); + ConstNodesRef available_nodes() const noexcept { return {_nodes.data(), _available_sz}; } + ConstNodesRef available_nonretired_nodes() const noexcept { return {_nodes.data() + _available_sz, _nonretired_sz}; } + ConstNodesRef available_nonretired_or_maintenance_nodes() const noexcept { + uint16_t offset = _available_sz + _nonretired_sz; + return {_nodes.data() + offset, _nodes.size() - offset}; } - void set_available_nonretired_nodes(std::vector<uint16_t> available_nonretired_nodes) { - _available_nonretired_nodes = std::move(available_nonretired_nodes); + bool is_nonretired_or_maintenance(uint16_t node) const noexcept { + return nonretired_or_maintenance_index(node) != Index::invalid(); } - void set_available_nonretired_or_maintenance_nodes(std::vector<uint16_t> available_nonretired_or_maintenance_nodes); - const std::vector<uint16_t> & available_nodes() const noexcept { return _available_nodes; } - const std::vector<uint16_t> & available_nonretired_nodes() const noexcept { return _available_nonretired_nodes; } - const std::vector<uint16_t> & available_nonretired_or_maintenance_nodes() const noexcept { - return _available_nonretired_or_maintenance_nodes; + NonRetiredOrMaintenance2Index nonRetiredOrMaintenance2Index() const noexcept { + return NonRetiredOrMaintenance2Index(*this); } - bool is_nonretired_or_maintenance(uint16_t node) const noexcept { - return _unordered_nonretired_or_maintenance_nodes.contains(node); + ConstNodesRef2Index available2Index() const noexcept { + return ConstNodesRef2Index(available_nodes()); } +private: + struct LookupMap; + Index nonretired_or_maintenance_index(uint16_t node) const noexcept; + vespalib::SmallVector<uint16_t,16> _nodes; + std::unique_ptr<LookupMap> _nonretired_or_maintenance_node_2_index; + uint16_t _available_sz; + uint16_t _nonretired_sz; }; } diff --git a/storage/src/vespa/storage/distributor/operations/external/putoperation.cpp b/storage/src/vespa/storage/distributor/operations/external/putoperation.cpp index 86ea9a559f5..5414c6221fc 100644 --- a/storage/src/vespa/storage/distributor/operations/external/putoperation.cpp +++ b/storage/src/vespa/storage/distributor/operations/external/putoperation.cpp @@ -10,7 +10,6 @@ #include <vespa/storage/distributor/storage_node_up_states.h> #include <vespa/storageapi/message/persistence.h> #include <vespa/vdslib/distribution/distribution.h> -#include <vespa/vdslib/distribution/idealnodecalculatorimpl.h> #include <vespa/vdslib/state/clusterstate.h> #include <algorithm> @@ -67,13 +66,11 @@ PutOperation::insertDatabaseEntryAndScheduleCreateBucket(const OperationTargetLi assert(!multipleBuckets); (void) multipleBuckets; BucketDatabase::Entry entry(_bucket_space.getBucketDatabase().get(lastBucket)); - const std::vector<uint16_t> & idealState = _bucket_space.get_ideal_service_layer_nodes_bundle( - lastBucket).available_nodes(); - active = ActiveCopy::calculate(idealState, _bucket_space.getDistribution(), entry, + active = ActiveCopy::calculate(_bucket_space.get_ideal_service_layer_nodes_bundle(lastBucket).available2Index(), _bucket_space.getDistribution(), entry, _op_ctx.distributor_config().max_activation_inhibited_out_of_sync_groups()); LOG(debug, "Active copies for bucket %s: %s", entry.getBucketId().toString().c_str(), active.toString().c_str()); for (uint32_t i=0; i<active.size(); ++i) { - BucketCopy copy(*entry->getNode(active[i]._nodeIndex)); + BucketCopy copy(*entry->getNode(active[i].nodeIndex())); copy.setActive(true); entry->updateNode(copy); } diff --git a/storage/src/vespa/storage/distributor/operationtargetresolver.h b/storage/src/vespa/storage/distributor/operationtargetresolver.h index 5e3c4a73f66..2de477d03e5 100644 --- a/storage/src/vespa/storage/distributor/operationtargetresolver.h +++ b/storage/src/vespa/storage/distributor/operationtargetresolver.h @@ -15,23 +15,23 @@ namespace storage::distributor { class OperationTarget : public vespalib::AsciiPrintable { document::Bucket _bucket; - lib::Node _node; - bool _newCopy; + lib::Node _node; + bool _newCopy; public: - OperationTarget() : _newCopy(true) {} - OperationTarget(const document::Bucket& bucket, const lib::Node& node, bool newCopy) + OperationTarget() noexcept : _newCopy(true) {} + OperationTarget(const document::Bucket& bucket, const lib::Node& node, bool newCopy) noexcept : _bucket(bucket), _node(node), _newCopy(newCopy) {} - document::BucketId getBucketId() const { return _bucket.getBucketId(); } - document::Bucket getBucket() const { return _bucket; } - const lib::Node& getNode() const { return _node; } - bool isNewCopy() const { return _newCopy; } + document::BucketId getBucketId() const noexcept { return _bucket.getBucketId(); } + document::Bucket getBucket() const noexcept { return _bucket; } + const lib::Node& getNode() const noexcept { return _node; } + bool isNewCopy() const noexcept { return _newCopy; } - bool operator==(const OperationTarget& o) const { + bool operator==(const OperationTarget& o) const noexcept { return (_bucket == o._bucket && _node == o._node && _newCopy == o._newCopy); } - bool operator!=(const OperationTarget& o) const { + bool operator!=(const OperationTarget& o) const noexcept { return !(operator==(o)); } @@ -40,13 +40,13 @@ public: class OperationTargetList : public std::vector<OperationTarget> { public: - bool hasAnyNewCopies() const { + bool hasAnyNewCopies() const noexcept { for (size_t i=0; i<size(); ++i) { if (operator[](i).isNewCopy()) return true; } return false; } - bool hasAnyExistingCopies() const { + bool hasAnyExistingCopies() const noexcept { for (size_t i=0; i<size(); ++i) { if (!operator[](i).isNewCopy()) return true; } @@ -63,8 +63,7 @@ public: PUT }; - virtual OperationTargetList getTargets(OperationType type, - const document::BucketId& id) = 0; + virtual OperationTargetList getTargets(OperationType type, const document::BucketId& id) = 0; }; } diff --git a/storage/src/vespa/storage/distributor/operationtargetresolverimpl.cpp b/storage/src/vespa/storage/distributor/operationtargetresolverimpl.cpp index 736d8c692e3..17b4dcd4f42 100644 --- a/storage/src/vespa/storage/distributor/operationtargetresolverimpl.cpp +++ b/storage/src/vespa/storage/distributor/operationtargetresolverimpl.cpp @@ -9,22 +9,8 @@ namespace storage::distributor { -namespace { - -lib::IdealNodeList -make_node_list(const std::vector<uint16_t>& nodes) -{ - lib::IdealNodeList list; - for (auto node : nodes) { - list.push_back(lib::Node(lib::NodeType::STORAGE, node)); - } - return list; -} - -} - BucketInstance::BucketInstance(const document::BucketId& id, const api::BucketInfo& info, lib::Node node, - uint16_t idealLocationPriority, bool trusted, bool exist) + uint16_t idealLocationPriority, bool trusted, bool exist) noexcept : _bucket(id), _info(info), _node(node), _idealLocationPriority(idealLocationPriority), _trusted(trusted), _exist(exist) { @@ -44,19 +30,19 @@ BucketInstance::print(vespalib::asciistream& out, const PrintProperties&) const bool BucketInstanceList::contains(lib::Node node) const { - for (uint32_t i=0; i<_instances.size(); ++i) { - if (_instances[i]._node == node) return true; + for (const auto & instance : _instances) { + if (instance._node == node) return true; } return false; } void -BucketInstanceList::add(const BucketDatabase::Entry& e, const lib::IdealNodeList& idealState) +BucketInstanceList::add(const BucketDatabase::Entry& e, const IdealServiceLayerNodesBundle::Node2Index & idealState) { for (uint32_t i = 0; i < e.getBucketInfo().getNodeCount(); ++i) { const BucketCopy& copy(e.getBucketInfo().getNodeRef(i)); lib::Node node(lib::NodeType::STORAGE, copy.getNode()); - _instances.emplace_back(e.getBucketId(), copy.getBucketInfo(), node, idealState.indexOf(node), copy.trusted()); + _instances.emplace_back(e.getBucketId(), copy.getBucketInfo(), node, idealState.lookup(copy.getNode()), copy.trusted(), true); } } @@ -66,8 +52,8 @@ BucketInstanceList::populate(const document::BucketId& specificId, const Distrib std::vector<BucketDatabase::Entry> entries; db.getParents(specificId, entries); for (const auto & entry : entries) { - lib::IdealNodeList idealNodes(make_node_list(distributor_bucket_space.get_ideal_service_layer_nodes_bundle(entry.getBucketId()).available_nonretired_or_maintenance_nodes())); - add(entry, idealNodes); + auto node2Index = distributor_bucket_space.get_ideal_service_layer_nodes_bundle(entry.getBucketId()).nonRetiredOrMaintenance2Index(); + add(entry, node2Index); } } @@ -110,18 +96,17 @@ BucketInstanceList::leastSpecificLeafBucketInSubtree(const document::BucketId& c } void -BucketInstanceList::extendToEnoughCopies(const DistributorBucketSpace& distributor_bucket_space, - const BucketDatabase& db, - const document::BucketId& targetIfNonPreExisting, - const document::BucketId& mostSpecificId) +BucketInstanceList::extendToEnoughCopies(const DistributorBucketSpace& distributor_bucket_space, const BucketDatabase& db, + const document::BucketId& targetIfNonPreExisting, const document::BucketId& mostSpecificId) { document::BucketId newTarget(_instances.empty() ? targetIfNonPreExisting : _instances[0]._bucket); newTarget = leastSpecificLeafBucketInSubtree(newTarget, mostSpecificId, db); - lib::IdealNodeList idealNodes(make_node_list(distributor_bucket_space.get_ideal_service_layer_nodes_bundle(newTarget).available_nonretired_nodes())); + const auto & idealNodes = distributor_bucket_space.get_ideal_service_layer_nodes_bundle(newTarget).available_nonretired_nodes(); for (uint32_t i=0; i<idealNodes.size(); ++i) { - if (!contains(idealNodes[i])) { - _instances.emplace_back(newTarget, api::BucketInfo(), idealNodes[i], i, false, false); + lib::Node node(lib::NodeType::STORAGE, idealNodes[i]); + if (!contains(node)) { + _instances.emplace_back(newTarget, api::BucketInfo(), node, i, false, false); } } } @@ -131,7 +116,7 @@ BucketInstanceList::createTargets(document::BucketSpace bucketSpace) { OperationTargetList result; for (const auto& bi : _instances) { - result.push_back(OperationTarget(document::Bucket(bucketSpace, bi._bucket), bi._node, !bi._exist)); + result.emplace_back(document::Bucket(bucketSpace, bi._bucket), bi._node, !bi._exist); } return result; } diff --git a/storage/src/vespa/storage/distributor/operationtargetresolverimpl.h b/storage/src/vespa/storage/distributor/operationtargetresolverimpl.h index 0caeee466e0..f3d2ce233ef 100644 --- a/storage/src/vespa/storage/distributor/operationtargetresolverimpl.h +++ b/storage/src/vespa/storage/distributor/operationtargetresolverimpl.h @@ -3,8 +3,8 @@ #pragma once #include "operationtargetresolver.h" +#include "ideal_service_layer_nodes_bundle.h" #include <vespa/storage/bucketdb/bucketdatabase.h> -#include <vespa/vdslib/distribution/idealnodecalculator.h> #include <algorithm> namespace storage::distributor { @@ -19,11 +19,11 @@ struct BucketInstance : public vespalib::AsciiPrintable { bool _trusted; bool _exist; - BucketInstance() : _idealLocationPriority(0xffff), - _trusted(false), _exist(false) {} + BucketInstance() noexcept + : _idealLocationPriority(0xffff), _trusted(false), _exist(false) {} BucketInstance(const document::BucketId& id, const api::BucketInfo& info, lib::Node node, uint16_t idealLocationPriority, bool trusted, - bool exist = true); + bool exist) noexcept; void print(vespalib::asciistream& out, const PrintProperties&) const override; }; @@ -42,10 +42,9 @@ class BucketInstanceList : public vespalib::AsciiPrintable { * Postconditions: * <return value>.contains(mostSpecificId) */ - document::BucketId leastSpecificLeafBucketInSubtree( - const document::BucketId& candidateId, - const document::BucketId& mostSpecificId, - const BucketDatabase& db) const; + document::BucketId leastSpecificLeafBucketInSubtree(const document::BucketId& candidateId, + const document::BucketId& mostSpecificId, + const BucketDatabase& db) const; public: void add(const BucketInstance& instance) { _instances.push_back(instance); } @@ -59,13 +58,11 @@ public: * _instances.size() >= configured redundancy level, unless insufficient * number of nodes are available */ - void extendToEnoughCopies(const DistributorBucketSpace& distributor_bucket_space, - const BucketDatabase& db, - const document::BucketId& targetIfNonPreExisting, - const document::BucketId& mostSpecificId); + void extendToEnoughCopies(const DistributorBucketSpace& distributor_bucket_space, const BucketDatabase& db, + const document::BucketId& targetIfNonPreExisting, const document::BucketId& mostSpecificId); void populate(const document::BucketId&, const DistributorBucketSpace&, BucketDatabase&); - void add(const BucketDatabase::Entry& e, const lib::IdealNodeList& idealState); + void add(const BucketDatabase::Entry& e, const IdealServiceLayerNodesBundle::Node2Index & idealState); template <typename Order> void sort(const Order& order) { @@ -79,17 +76,14 @@ public: class OperationTargetResolverImpl : public OperationTargetResolver { const DistributorBucketSpace& _distributor_bucket_space; - BucketDatabase& _bucketDatabase; - uint32_t _minUsedBucketBits; - uint16_t _redundancy; + BucketDatabase& _bucketDatabase; + uint32_t _minUsedBucketBits; + uint16_t _redundancy; document::BucketSpace _bucketSpace; public: - OperationTargetResolverImpl(const DistributorBucketSpace& distributor_bucket_space, - BucketDatabase& bucketDatabase, - uint32_t minUsedBucketBits, - uint16_t redundancy, - document::BucketSpace bucketSpace) + OperationTargetResolverImpl(const DistributorBucketSpace& distributor_bucket_space, BucketDatabase& bucketDatabase, + uint32_t minUsedBucketBits, uint16_t redundancy, document::BucketSpace bucketSpace) : _distributor_bucket_space(distributor_bucket_space), _bucketDatabase(bucketDatabase), _minUsedBucketBits(minUsedBucketBits), @@ -97,8 +91,7 @@ public: _bucketSpace(bucketSpace) {} - BucketInstanceList getAllInstances(OperationType type, - const document::BucketId& id); + BucketInstanceList getAllInstances(OperationType type, const document::BucketId& id); BucketInstanceList getInstances(OperationType type, const document::BucketId& id) { BucketInstanceList result(getAllInstances(type, id)); result.limitToRedundancyCopies(_redundancy); diff --git a/storage/src/vespa/storage/distributor/statechecker.h b/storage/src/vespa/storage/distributor/statechecker.h index 25918e7a047..d120b5e62d7 100644 --- a/storage/src/vespa/storage/distributor/statechecker.h +++ b/storage/src/vespa/storage/distributor/statechecker.h @@ -77,7 +77,9 @@ public: const bool merges_inhibited_in_bucket_space; const BucketDatabase::Entry& getSiblingEntry() const noexcept { return siblingEntry; } - const std::vector<uint16_t> & idealState() const noexcept { return idealStateBundle.available_nonretired_or_maintenance_nodes(); } + IdealServiceLayerNodesBundle::ConstNodesRef idealState() const noexcept { + return idealStateBundle.available_nonretired_or_maintenance_nodes(); + } document::Bucket getBucket() const noexcept { return bucket; } document::BucketId getBucketId() const noexcept { return bucket.getBucketId(); } diff --git a/storage/src/vespa/storage/distributor/statecheckers.cpp b/storage/src/vespa/storage/distributor/statecheckers.cpp index 43766225155..6f453ca417b 100644 --- a/storage/src/vespa/storage/distributor/statecheckers.cpp +++ b/storage/src/vespa/storage/distributor/statecheckers.cpp @@ -145,8 +145,10 @@ JoinBucketsStateChecker::isFirstSibling(const document::BucketId& bucketId) namespace { +using ConstNodesRef = IdealServiceLayerNodesBundle::ConstNodesRef; + bool -equalNodeSet(const std::vector<uint16_t>& idealState, const BucketDatabase::Entry& dbEntry) +equalNodeSet(ConstNodesRef idealState, const BucketDatabase::Entry& dbEntry) { if (idealState.size() != dbEntry->getNodeCount()) { return false; @@ -187,6 +189,12 @@ inconsistentJoinIsAllowed(const StateChecker::Context& context) && bucketAndSiblingReplicaLocationsEqualIdealState(context)); } +bool +isInconsistentlySplit(const StateChecker::Context& c) +{ + return (c.entries.size() > 1); +} + } // anon ns bool @@ -484,11 +492,7 @@ SplitInconsistentStateChecker::getReason(const document::BucketId& bucketId, con namespace { -bool -isInconsistentlySplit(const StateChecker::Context& c) -{ - return (c.entries.size() > 1); -} + } @@ -513,7 +517,7 @@ SplitInconsistentStateChecker::check(Context& c) const namespace { -bool containsMaintenanceNode(const std::vector<uint16_t>& ideal, const StateChecker::Context& c) +bool containsMaintenanceNode(ConstNodesRef ideal, const StateChecker::Context& c) { for (uint16_t n : ideal) { if (c.systemState.getNodeState(lib::Node(lib::NodeType::STORAGE, n)).getState() == lib::State::MAINTENANCE) { @@ -536,7 +540,7 @@ bool ideal_node_is_unavailable_in_pending_state(const StateChecker::Context& c) } bool -consistentApartFromEmptyBucketsInNonIdealLocationAndInvalidEntries(const std::vector<uint16_t>& idealNodes, const BucketInfo& entry) +consistentApartFromEmptyBucketsInNonIdealLocationAndInvalidEntries(ConstNodesRef idealNodes, const BucketInfo& entry) { api::BucketInfo info; for (uint32_t i=0, n=entry.getNodeCount(); i<n; ++i) { @@ -940,7 +944,7 @@ bool BucketStateStateChecker::shouldSkipActivationDueToMaintenance(const ActiveList& activeNodes, const Context& c) { for (uint32_t i = 0; i < activeNodes.size(); ++i) { - const auto node_index = activeNodes[i]._nodeIndex; + const auto node_index = activeNodes[i].nodeIndex(); const BucketCopy* cp(c.entry->getNode(node_index)); if (!cp || cp->active()) { continue; @@ -978,7 +982,7 @@ BucketStateStateChecker::check(Context& c) const return Result::noMaintenanceNeeded(); } - ActiveList activeNodes = ActiveCopy::calculate(c.idealState(), c.distribution, c.entry, + ActiveList activeNodes = ActiveCopy::calculate(c.idealStateBundle.nonRetiredOrMaintenance2Index(), c.distribution, c.entry, c.distributorConfig.max_activation_inhibited_out_of_sync_groups()); if (activeNodes.empty()) { return Result::noMaintenanceNeeded(); @@ -990,12 +994,12 @@ BucketStateStateChecker::check(Context& c) const vespalib::asciistream reason; std::vector<uint16_t> operationNodes; for (uint32_t i=0; i<activeNodes.size(); ++i) { - const BucketCopy* cp = c.entry->getNode(activeNodes[i]._nodeIndex); + const BucketCopy* cp = c.entry->getNode(activeNodes[i].nodeIndex()); if (cp == nullptr || cp->active()) { continue; } - operationNodes.push_back(activeNodes[i]._nodeIndex); - reason << "[Setting node " << activeNodes[i]._nodeIndex << " as active: " << activeNodes[i].getReason() << "]"; + operationNodes.push_back(activeNodes[i].nodeIndex()); + reason << "[Setting node " << activeNodes[i].nodeIndex() << " as active: " << activeNodes[i].getReason() << "]"; } // Deactivate all copies that are currently marked as active. @@ -1006,7 +1010,7 @@ BucketStateStateChecker::check(Context& c) const } bool shouldBeActive = false; for (uint32_t j=0; j<activeNodes.size(); ++j) { - if (activeNodes[j]._nodeIndex == cp.getNode()) { + if (activeNodes[j].nodeIndex() == cp.getNode()) { shouldBeActive = true; } } @@ -1022,7 +1026,7 @@ BucketStateStateChecker::check(Context& c) const std::vector<uint16_t> activeNodeIndexes; for (uint32_t i=0; i<activeNodes.size(); ++i) { - activeNodeIndexes.push_back(activeNodes[i]._nodeIndex); + activeNodeIndexes.push_back(activeNodes[i].nodeIndex()); } auto op = std::make_unique<SetBucketStateOperation>(c.node_ctx, BucketAndNodes(c.getBucket(), operationNodes), activeNodeIndexes); diff --git a/storage/src/vespa/storage/storageutil/utils.h b/storage/src/vespa/storage/storageutil/utils.h index debb7e71ace..d6d82abb56a 100644 --- a/storage/src/vespa/storage/storageutil/utils.h +++ b/storage/src/vespa/storage/storageutil/utils.h @@ -1,7 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once -#include <vector> +#include <vespa/vespalib/util/arrayref.h> #include <sstream> namespace storage { @@ -66,7 +66,7 @@ std::string dumpVector(const std::vector<A>& vec) { } template<class A> -bool hasItem(const std::vector<A>& vec, A entry) { +bool hasItem(vespalib::ConstArrayRef<A> vec, A entry) { for (uint32_t i = 0; i < vec.size(); ++i) { if (vec[i] == entry) { return true; @@ -76,16 +76,5 @@ bool hasItem(const std::vector<A>& vec, A entry) { return false; } -template<typename T> -struct ConfigReader : public T::Subscriber, public T -{ - T& config; // Alter to inherit T to simplify but kept this for compatability - - ConfigReader(const std::string& configId) : config(*this) { - T::subscribe(configId, *this); - } - void configure(const T& c) { config = c; } -}; - } diff --git a/vdslib/src/tests/distribution/CMakeLists.txt b/vdslib/src/tests/distribution/CMakeLists.txt index c4ae8b0291c..3f3be1e1cad 100644 --- a/vdslib/src/tests/distribution/CMakeLists.txt +++ b/vdslib/src/tests/distribution/CMakeLists.txt @@ -3,7 +3,6 @@ vespa_add_library(vdslib_testdistribution SOURCES distributiontest.cpp grouptest.cpp - idealnodecalculatorimpltest.cpp DEPENDS vdslib GTest::GTest diff --git a/vdslib/src/tests/distribution/distributiontest.cpp b/vdslib/src/tests/distribution/distributiontest.cpp index ec7c05fa7a2..ce07711a069 100644 --- a/vdslib/src/tests/distribution/distributiontest.cpp +++ b/vdslib/src/tests/distribution/distributiontest.cpp @@ -5,7 +5,6 @@ #include <vespa/config/subscription/configuri.h> #include <vespa/fastos/file.h> #include <vespa/vdslib/distribution/distribution.h> -#include <vespa/vdslib/distribution/idealnodecalculator.h> #include <vespa/vdslib/state/clusterstate.h> #include <vespa/vdslib/state/random.h> #include <vespa/vespalib/data/slime/slime.h> @@ -84,6 +83,51 @@ TEST(DistributionTest, test_verify_java_distributions) namespace { +/** +* A list of ideal nodes, sorted in preferred order. Wraps a vector to hide +* unneeded details, and make it easily printable. +*/ +class IdealNodeList : public document::Printable { +public: + IdealNodeList() noexcept; + ~IdealNodeList(); + + void push_back(const Node& node) { + _idealNodes.push_back(node); + } + + const Node& operator[](uint32_t i) const noexcept { return _idealNodes[i]; } + uint32_t size() const noexcept { return _idealNodes.size(); } + bool contains(const Node& n) const noexcept { + return indexOf(n) != 0xffff; + } + uint16_t indexOf(const Node& n) const noexcept { + for (uint16_t i=0; i<_idealNodes.size(); ++i) { + if (n == _idealNodes[i]) return i; + } + return 0xffff; + } + + void print(std::ostream& out, bool, const std::string &) const override; +private: + std::vector<Node> _idealNodes; +}; + +IdealNodeList::IdealNodeList() noexcept = default; +IdealNodeList::~IdealNodeList() = default; + +void +IdealNodeList::print(std::ostream& out, bool , const std::string &) const +{ + out << "["; + for (uint32_t i=0; i<_idealNodes.size(); ++i) { + if (i != 0) out << ", "; + out << _idealNodes[i]; + } + out << "]"; +} + + struct ExpectedResult { ExpectedResult() { } ExpectedResult(const ExpectedResult &) = default; diff --git a/vdslib/src/tests/distribution/idealnodecalculatorimpltest.cpp b/vdslib/src/tests/distribution/idealnodecalculatorimpltest.cpp deleted file mode 100644 index 4159491097c..00000000000 --- a/vdslib/src/tests/distribution/idealnodecalculatorimpltest.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include <vespa/config-stor-distribution.h> -#include <vespa/vdslib/distribution/idealnodecalculatorimpl.h> -#include <vespa/vdslib/distribution/distribution.h> -#include <vespa/vdslib/state/clusterstate.h> -#include <vespa/vespalib/gtest/gtest.h> - -namespace storage::lib { - -/** - * Class is just a wrapper for distribution, so little needs to be tested. Just - * that: - * - * - get ideal nodes calls gets propagated correctly. - * - Changes in distribution/cluster state is picked up. - */ - -TEST(IdealNodeCalculatorImplTest, test_normal_usage) -{ - ClusterState state("storage:10"); - Distribution distr(Distribution::getDefaultDistributionConfig(3, 10)); - IdealNodeCalculatorImpl impl; - IdealNodeCalculatorConfigurable& configurable(impl); - IdealNodeCalculator& calc(impl); - configurable.setDistribution(distr); - configurable.setClusterState(state); - - std::string expected("[storage.8, storage.9, storage.6]"); - EXPECT_EQ( - expected, - calc.getIdealStorageNodes(document::BucketId(16, 5)).toString()); -} - -} diff --git a/vdslib/src/vespa/vdslib/distribution/CMakeLists.txt b/vdslib/src/vespa/vdslib/distribution/CMakeLists.txt index 0d9342291e8..58ec94eec9c 100644 --- a/vdslib/src/vespa/vdslib/distribution/CMakeLists.txt +++ b/vdslib/src/vespa/vdslib/distribution/CMakeLists.txt @@ -4,7 +4,6 @@ vespa_add_library(vdslib_distribution OBJECT distribution.cpp distribution_config_util.cpp group.cpp - idealnodecalculatorimpl.cpp redundancygroupdistribution.cpp DEPENDS ) diff --git a/vdslib/src/vespa/vdslib/distribution/idealnodecalculator.h b/vdslib/src/vespa/vdslib/distribution/idealnodecalculator.h deleted file mode 100644 index 4eb8f7e04ae..00000000000 --- a/vdslib/src/vespa/vdslib/distribution/idealnodecalculator.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * An interface to implement for a calculator calcuting ideal state. It should - * be easy to wrap this calculator in a cache. Thus options that seldom change, - * are taken in as set parameters, such that existing cache can be invalidated. - */ -#pragma once - -#include <vespa/document/bucket/bucketid.h> -#include <vespa/document/util/printable.h> -#include <vespa/vdslib/state/node.h> -#include <vector> -#include <memory> - -namespace storage::lib { - -class Distribution; -class ClusterState; - -/** - * A list of ideal nodes, sorted in preferred order. Wraps a vector to hide - * unneeded details, and make it easily printable. - */ -class IdealNodeList : public document::Printable { -public: - IdealNodeList() noexcept; - ~IdealNodeList(); - - void push_back(const Node& node) { - _idealNodes.push_back(node); - } - - const Node& operator[](uint32_t i) const noexcept { return _idealNodes[i]; } - uint32_t size() const noexcept { return _idealNodes.size(); } - bool contains(const Node& n) const noexcept { - return indexOf(n) != 0xffff; - } - uint16_t indexOf(const Node& n) const noexcept { - for (uint16_t i=0; i<_idealNodes.size(); ++i) { - if (n == _idealNodes[i]) return i; - } - return 0xffff; - } - - void print(std::ostream& out, bool, const std::string &) const override; -private: - std::vector<Node> _idealNodes; -}; - -/** - * Simple interface to use for those who needs to calculate ideal nodes. - */ -class IdealNodeCalculator { -public: - using SP = std::shared_ptr<IdealNodeCalculator>; - enum UpStates { - UpInit, - UpInitMaintenance, - UP_STATE_COUNT - }; - - virtual ~IdealNodeCalculator() = default; - - virtual IdealNodeList getIdealNodes(const NodeType&, const document::BucketId&, UpStates upStates = UpInit) const = 0; - - // Wrapper functions to make prettier call if nodetype is given. - IdealNodeList getIdealDistributorNodes(const document::BucketId& bucket, UpStates upStates = UpInit) const { - return getIdealNodes(NodeType::DISTRIBUTOR, bucket, upStates); - } - IdealNodeList getIdealStorageNodes(const document::BucketId& bucket, UpStates upStates = UpInit) const { - return getIdealNodes(NodeType::STORAGE, bucket, upStates); - } -}; - - -/** - * More complex interface that provides a way to alter needed settings not - * provided in the function call itself. - */ -class IdealNodeCalculatorConfigurable : public IdealNodeCalculator -{ -public: - using SP = std::shared_ptr<IdealNodeCalculatorConfigurable>; - - virtual void setDistribution(const Distribution&) = 0; - virtual void setClusterState(const ClusterState&) = 0; -}; - -} diff --git a/vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.cpp b/vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.cpp deleted file mode 100644 index 86123f47d6f..00000000000 --- a/vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "idealnodecalculatorimpl.h" -#include "distribution.h" -#include <vespa/vespalib/util/exceptions.h> -#include <ostream> -#include <cassert> - -namespace storage::lib { - -IdealNodeList::IdealNodeList() noexcept = default; -IdealNodeList::~IdealNodeList() = default; - -void -IdealNodeList::print(std::ostream& out, bool , const std::string &) const -{ - out << "["; - for (uint32_t i=0; i<_idealNodes.size(); ++i) { - if (i != 0) out << ", "; - out << _idealNodes[i]; - } - out << "]"; -} - -IdealNodeCalculatorImpl::IdealNodeCalculatorImpl() - : _distribution(0), - _clusterState(0) -{ - initUpStateMapping(); -} - -IdealNodeCalculatorImpl::~IdealNodeCalculatorImpl() = default; - -void -IdealNodeCalculatorImpl::setDistribution(const Distribution& d) { - _distribution = &d; -} -void -IdealNodeCalculatorImpl::setClusterState(const ClusterState& cs) { - _clusterState = &cs; -} - -IdealNodeList -IdealNodeCalculatorImpl::getIdealNodes(const NodeType& nodeType, - const document::BucketId& bucket, - UpStates upStates) const -{ - assert(_clusterState != 0); - assert(_distribution != 0); - std::vector<uint16_t> nodes; - _distribution->getIdealNodes(nodeType, *_clusterState, bucket, nodes, _upStates[upStates]); - IdealNodeList list; - for (uint32_t i=0; i<nodes.size(); ++i) { - list.push_back(Node(nodeType, nodes[i])); - } - return list; -} - -void -IdealNodeCalculatorImpl::initUpStateMapping() { - _upStates.clear(); - _upStates.resize(UP_STATE_COUNT); - _upStates[UpInit] = "ui"; - _upStates[UpInitMaintenance] = "uim"; - for (uint32_t i=0; i<_upStates.size(); ++i) { - if (_upStates[i] == 0) { - throw vespalib::IllegalStateException("Failed to initialize up state. Code likely not updated " - "after another upstate was added.", VESPA_STRLOC); - } - } -} - -} diff --git a/vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.h b/vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.h deleted file mode 100644 index 9b36f1094fd..00000000000 --- a/vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * A cache for an ideal nodes implementation. Making it cheap for localized - * access, regardless of real implementation. - */ -#pragma once - -#include "idealnodecalculator.h" - -namespace storage::lib { - -class IdealNodeCalculatorImpl : public IdealNodeCalculatorConfigurable { - std::vector<const char*> _upStates; - const Distribution* _distribution; - const ClusterState* _clusterState; - -public: - IdealNodeCalculatorImpl(); - ~IdealNodeCalculatorImpl(); - - void setDistribution(const Distribution& d) override; - void setClusterState(const ClusterState& cs) override; - - IdealNodeList getIdealNodes(const NodeType& nodeType, - const document::BucketId& bucket, - UpStates upStates) const override; -private: - void initUpStateMapping(); -}; - -} diff --git a/vespalib/src/vespa/vespalib/stllike/hash_map.cpp b/vespalib/src/vespa/vespalib/stllike/hash_map.cpp index abb88fe674f..50a3d73fe12 100644 --- a/vespalib/src/vespa/vespalib/stllike/hash_map.cpp +++ b/vespalib/src/vespa/vespalib/stllike/hash_map.cpp @@ -16,6 +16,7 @@ VESPALIB_HASH_MAP_INSTANTIATE(vespalib::string, double); VESPALIB_HASH_MAP_INSTANTIATE(int64_t, int32_t); VESPALIB_HASH_MAP_INSTANTIATE(int64_t, uint32_t); VESPALIB_HASH_MAP_INSTANTIATE(int32_t, uint32_t); +VESPALIB_HASH_MAP_INSTANTIATE(uint16_t, uint16_t); VESPALIB_HASH_MAP_INSTANTIATE(uint16_t, uint32_t); VESPALIB_HASH_MAP_INSTANTIATE(uint32_t, int32_t); VESPALIB_HASH_MAP_INSTANTIATE(uint32_t, uint32_t); |