aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--storage/src/tests/distributor/distributor_bucket_space_test.cpp2
-rw-r--r--storage/src/tests/distributor/operationtargetresolvertest.cpp26
-rw-r--r--storage/src/tests/distributor/statecheckerstest.cpp9
-rw-r--r--storage/src/vespa/storage/common/distributorcomponent.h7
-rw-r--r--storage/src/vespa/storage/distributor/activecopy.cpp94
-rw-r--r--storage/src/vespa/storage/distributor/activecopy.h38
-rw-r--r--storage/src/vespa/storage/distributor/distributor_bucket_space.cpp6
-rw-r--r--storage/src/vespa/storage/distributor/distributor_stripe_component.cpp7
-rw-r--r--storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.cpp62
-rw-r--r--storage/src/vespa/storage/distributor/ideal_service_layer_nodes_bundle.h69
-rw-r--r--storage/src/vespa/storage/distributor/operations/external/putoperation.cpp7
-rw-r--r--storage/src/vespa/storage/distributor/operationtargetresolver.h27
-rw-r--r--storage/src/vespa/storage/distributor/operationtargetresolverimpl.cpp43
-rw-r--r--storage/src/vespa/storage/distributor/operationtargetresolverimpl.h39
-rw-r--r--storage/src/vespa/storage/distributor/statechecker.h4
-rw-r--r--storage/src/vespa/storage/distributor/statecheckers.cpp34
-rw-r--r--storage/src/vespa/storage/storageutil/utils.h15
-rw-r--r--vdslib/src/tests/distribution/CMakeLists.txt1
-rw-r--r--vdslib/src/tests/distribution/distributiontest.cpp46
-rw-r--r--vdslib/src/tests/distribution/idealnodecalculatorimpltest.cpp35
-rw-r--r--vdslib/src/vespa/vdslib/distribution/CMakeLists.txt1
-rw-r--r--vdslib/src/vespa/vdslib/distribution/idealnodecalculator.h89
-rw-r--r--vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.cpp73
-rw-r--r--vdslib/src/vespa/vdslib/distribution/idealnodecalculatorimpl.h31
-rw-r--r--vespalib/src/vespa/vespalib/stllike/hash_map.cpp1
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);