mirror of https://gitee.com/bigwinds/arangodb
841 lines
28 KiB
C++
841 lines
28 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief test suite for Cluster maintenance
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2017 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Kaveh Vahedipour
|
|
/// @author Matthew Von-Maszewski
|
|
/// @author Copyright 2017-2018, ArangoDB GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "Cluster/Maintenance.h"
|
|
#include "MMFiles/MMFilesEngine.h"
|
|
#include "StorageEngine/EngineSelectorFeature.h"
|
|
|
|
#include <velocypack/Iterator.h>
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <random>
|
|
#include <typeinfo>
|
|
|
|
#include "MaintenanceFeatureMock.h"
|
|
|
|
using namespace arangodb;
|
|
using namespace arangodb::consensus;
|
|
using namespace arangodb::maintenance;
|
|
|
|
#ifndef _WIN32
|
|
|
|
char const* planStr =
|
|
#include "Plan.json"
|
|
;
|
|
char const* currentStr =
|
|
#include "Current.json"
|
|
;
|
|
char const* supervisionStr =
|
|
#include "Supervision.json"
|
|
;
|
|
char const* dbs0Str =
|
|
#include "DBServer0001.json"
|
|
;
|
|
char const* dbs1Str =
|
|
#include "DBServer0002.json"
|
|
;
|
|
char const* dbs2Str =
|
|
#include "DBServer0003.json"
|
|
;
|
|
|
|
std::map<std::string, std::string> matchShortLongIds(Node const& supervision) {
|
|
std::map<std::string, std::string> ret;
|
|
for (auto const& dbs : supervision("Health").children()) {
|
|
if (dbs.first.front() == 'P') {
|
|
ret.emplace((*dbs.second)("ShortName").getString(), dbs.first);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Node createNodeFromBuilder(Builder const& builder) {
|
|
Builder opBuilder;
|
|
{
|
|
VPackObjectBuilder a(&opBuilder);
|
|
opBuilder.add("new", builder.slice());
|
|
}
|
|
|
|
Node node("");
|
|
node.handle<SET>(opBuilder.slice());
|
|
return node;
|
|
}
|
|
|
|
Builder createBuilder(char const* c) {
|
|
VPackOptions options;
|
|
options.checkAttributeUniqueness = true;
|
|
VPackParser parser(&options);
|
|
parser.parse(c);
|
|
|
|
Builder builder;
|
|
builder.add(parser.steal()->slice());
|
|
return builder;
|
|
}
|
|
|
|
Node createNode(char const* c) {
|
|
return createNodeFromBuilder(createBuilder(c));
|
|
}
|
|
|
|
// Random stuff
|
|
std::random_device rd;
|
|
std::mt19937 g(rd());
|
|
|
|
// Relevant agency
|
|
Node plan("");
|
|
Node originalPlan("");
|
|
Node supervision("");
|
|
Node current("");
|
|
|
|
std::vector<std::string> const shortNames{"DBServer0001", "DBServer0002",
|
|
"DBServer0003"};
|
|
|
|
// map <shortId, UUID>
|
|
std::map<std::string, std::string> dbsIds;
|
|
|
|
std::string const PLAN_COL_PATH = "/Collections/";
|
|
std::string const PLAN_DB_PATH = "/Databases/";
|
|
|
|
size_t localId = 1016002;
|
|
|
|
VPackBuilder createDatabase(std::string const& dbname) {
|
|
Builder builder;
|
|
{
|
|
VPackObjectBuilder o(&builder);
|
|
builder.add("id", VPackValue(std::to_string(localId++)));
|
|
builder.add("coordinator",
|
|
VPackValue("CRDN-42df19c3-73d5-48f4-b02e-09b29008eff8"));
|
|
builder.add(VPackValue("options"));
|
|
{ VPackObjectBuilder oo(&builder); }
|
|
builder.add("name", VPackValue(dbname));
|
|
}
|
|
return builder;
|
|
}
|
|
|
|
void createPlanDatabase(std::string const& dbname, Node& plan) {
|
|
plan(PLAN_DB_PATH + dbname) = createDatabase(dbname).slice();
|
|
}
|
|
|
|
VPackBuilder createIndex(std::string const& type, std::vector<std::string> const& fields,
|
|
bool unique, bool sparse, bool deduplicate) {
|
|
VPackBuilder index;
|
|
{
|
|
VPackObjectBuilder o(&index);
|
|
{
|
|
index.add("deduplicate", VPackValue(deduplicate));
|
|
index.add(VPackValue("fields"));
|
|
{
|
|
VPackArrayBuilder a(&index);
|
|
for (auto const& field : fields) {
|
|
index.add(VPackValue(field));
|
|
}
|
|
}
|
|
index.add("id", VPackValue(std::to_string(localId++)));
|
|
index.add("sparse", VPackValue(sparse));
|
|
index.add("type", VPackValue(type));
|
|
index.add("unique", VPackValue(unique));
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void createPlanIndex(std::string const& dbname, std::string const& colname,
|
|
std::string const& type, std::vector<std::string> const& fields,
|
|
bool unique, bool sparse, bool deduplicate, Node& plan) {
|
|
VPackBuilder val;
|
|
{
|
|
VPackObjectBuilder o(&val);
|
|
val.add("new", createIndex(type, fields, unique, sparse, deduplicate).slice());
|
|
}
|
|
plan(PLAN_COL_PATH + dbname + "/" + colname + "/indexes").handle<PUSH>(val.slice());
|
|
}
|
|
|
|
void createCollection(std::string const& colname, VPackBuilder& col) {
|
|
VPackBuilder keyOptions;
|
|
{
|
|
VPackObjectBuilder o(&keyOptions);
|
|
keyOptions.add("lastValue", VPackValue(0));
|
|
keyOptions.add("type", VPackValue("traditional"));
|
|
keyOptions.add("allowUserKeys", VPackValue(true));
|
|
}
|
|
|
|
VPackBuilder shardKeys;
|
|
{
|
|
VPackArrayBuilder a(&shardKeys);
|
|
shardKeys.add(VPackValue("_key"));
|
|
}
|
|
|
|
VPackBuilder indexes;
|
|
{
|
|
VPackArrayBuilder a(&indexes);
|
|
indexes.add(createIndex("primary", {"_key"}, true, false, false).slice());
|
|
}
|
|
|
|
col.add("id", VPackValue(std::to_string(localId++)));
|
|
col.add("status", VPackValue(3));
|
|
col.add("keyOptions", keyOptions.slice());
|
|
col.add("cacheEnabled", VPackValue(false));
|
|
col.add("waitForSync", VPackValue(false));
|
|
col.add("type", VPackValue(2));
|
|
col.add("isSystem", VPackValue(true));
|
|
col.add("name", VPackValue(colname));
|
|
col.add("shardingStrategy", VPackValue("hash"));
|
|
col.add("statusString", VPackValue("loaded"));
|
|
col.add("shardKeys", shardKeys.slice());
|
|
}
|
|
|
|
std::string S("s");
|
|
std::string C("c");
|
|
|
|
void createPlanShards(size_t numberOfShards, size_t replicationFactor, VPackBuilder& col) {
|
|
auto servers = shortNames;
|
|
std::shuffle(servers.begin(), servers.end(), g);
|
|
|
|
col.add("numberOfShards", VPackValue(1));
|
|
col.add("replicationFactor", VPackValue(2));
|
|
col.add(VPackValue("shards"));
|
|
{
|
|
VPackObjectBuilder s(&col);
|
|
for (size_t i = 0; i < numberOfShards; ++i) {
|
|
col.add(VPackValue(S + std::to_string(localId++)));
|
|
{
|
|
VPackArrayBuilder a(&col);
|
|
size_t j = 0;
|
|
for (auto const& server : servers) {
|
|
if (j++ < replicationFactor) {
|
|
col.add(VPackValue(dbsIds[server]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void createPlanCollection(std::string const& dbname, std::string const& colname,
|
|
size_t numberOfShards, size_t replicationFactor, Node& plan) {
|
|
VPackBuilder tmp;
|
|
{
|
|
VPackObjectBuilder o(&tmp);
|
|
createCollection(colname, tmp);
|
|
tmp.add("isSmart", VPackValue(false));
|
|
tmp.add("deleted", VPackValue(false));
|
|
createPlanShards(numberOfShards, replicationFactor, tmp);
|
|
}
|
|
|
|
Slice col = tmp.slice();
|
|
auto id = col.get("id").copyString();
|
|
plan(PLAN_COL_PATH + dbname + "/" + col.get("id").copyString()) = col;
|
|
}
|
|
|
|
void createLocalCollection(std::string const& dbname, std::string const& colname, Node& node) {
|
|
size_t planId = std::stoull(colname);
|
|
VPackBuilder tmp;
|
|
{
|
|
VPackObjectBuilder o(&tmp);
|
|
createCollection(colname, tmp);
|
|
tmp.add("planId", VPackValue(colname));
|
|
tmp.add("theLeader", VPackValue(""));
|
|
tmp.add("globallyUniqueId",
|
|
VPackValue(C + colname + "/" + S + std::to_string(planId + 1)));
|
|
tmp.add("objectId", VPackValue("9031415"));
|
|
}
|
|
node(dbname + "/" + S + std::to_string(planId + 1)) = tmp.slice();
|
|
}
|
|
|
|
std::map<std::string, std::string> collectionMap(Node const& plan) {
|
|
std::map<std::string, std::string> ret;
|
|
auto const pb = plan("Collections").toBuilder();
|
|
auto const ps = pb.slice();
|
|
for (auto const& db : VPackObjectIterator(ps)) {
|
|
for (auto const& col : VPackObjectIterator(db.value)) {
|
|
ret.emplace(db.key.copyString() + "/" + col.value.get("name").copyString(),
|
|
col.key.copyString());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
namespace arangodb {
|
|
class LogicalCollection;
|
|
}
|
|
|
|
class MaintenanceTestActionDescription : public ::testing::Test {
|
|
protected:
|
|
MaintenanceTestActionDescription() {
|
|
plan = createNode(planStr);
|
|
originalPlan = plan;
|
|
supervision = createNode(supervisionStr);
|
|
current = createNode(currentStr);
|
|
dbsIds = matchShortLongIds(supervision);
|
|
}
|
|
};
|
|
|
|
TEST_F(MaintenanceTestActionDescription, construct_minimal_actiondescription) {
|
|
ActionDescription desc(std::map<std::string, std::string>{{"name",
|
|
"SomeAction"}},
|
|
NORMAL_PRIORITY);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, construct_minimal_actiondescription_with_nullptr_props) {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, construct_minimal_actiondescription_with_empty_props) {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_nonassigned_key_from_actiondescription) {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
try {
|
|
auto bogus = desc.get("bogus");
|
|
ASSERT_TRUE(bogus == "bogus");
|
|
} catch (std::out_of_range const& e) {
|
|
}
|
|
std::string value;
|
|
auto res = desc.get("bogus", value);
|
|
ASSERT_TRUE(value.empty());
|
|
ASSERT_TRUE(!res.ok());
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_nonassigned_key_from_actiondescription_2) {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}, {"bogus", "bogus"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
try {
|
|
auto bogus = desc.get("bogus");
|
|
ASSERT_TRUE(bogus == "bogus");
|
|
} catch (std::out_of_range const& e) {
|
|
}
|
|
std::string value;
|
|
auto res = desc.get("bogus", value);
|
|
ASSERT_TRUE(value == "bogus");
|
|
ASSERT_TRUE(res.ok());
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_nonassigned_properties_from_actiondescription) {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties() == nullptr);
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_empty_properties_from_actiondescription) {
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties()->isEmpty());
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_empty_object_properties_from_actiondescription) {
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{ VPackObjectBuilder empty(props.get()); }
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties()->slice().isEmptyObject());
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_string_value_from_actiondescriptions_properties) {
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{
|
|
VPackObjectBuilder obj(props.get());
|
|
props->add("hello", VPackValue("world"));
|
|
}
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties()->slice().hasKey("hello"));
|
|
ASSERT_TRUE(desc.properties()->slice().get("hello").copyString() == "world");
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_double_value_from_actiondescriptions_properties) {
|
|
double pi = 3.14159265359;
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{
|
|
VPackObjectBuilder obj(props.get());
|
|
props->add("pi", VPackValue(3.14159265359));
|
|
}
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties()->slice().hasKey("pi"));
|
|
ASSERT_TRUE(desc.properties()->slice().get("pi").getNumber<double>() == pi);
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_integer_value_from_actiondescriptions_property) {
|
|
size_t one = 1;
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{
|
|
VPackObjectBuilder obj(props.get());
|
|
props->add("one", VPackValue(one));
|
|
}
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties()->slice().hasKey("one"));
|
|
ASSERT_TRUE(desc.properties()->slice().get("one").getNumber<size_t>() == one);
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionDescription, retrieve_array_value_from_actiondescriptions_properties) {
|
|
double pi = 3.14159265359;
|
|
size_t one = 1;
|
|
std::string hello("hello world!");
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{
|
|
VPackObjectBuilder obj(props.get());
|
|
props->add(VPackValue("array"));
|
|
{
|
|
VPackArrayBuilder arr(props.get());
|
|
props->add(VPackValue(pi));
|
|
props->add(VPackValue(one));
|
|
props->add(VPackValue(hello));
|
|
}
|
|
}
|
|
ActionDescription desc({{"name", "SomeAction"}}, NORMAL_PRIORITY, props);
|
|
ASSERT_TRUE(desc.get("name") == "SomeAction");
|
|
ASSERT_TRUE(desc.properties()->slice().hasKey("array"));
|
|
ASSERT_TRUE(desc.properties()->slice().get("array").isArray());
|
|
ASSERT_TRUE(desc.properties()->slice().get("array").length() == 3);
|
|
ASSERT_TRUE(desc.properties()->slice().get("array")[0].getNumber<double>() == pi);
|
|
ASSERT_TRUE(desc.properties()->slice().get("array")[1].getNumber<size_t>() == one);
|
|
ASSERT_TRUE(desc.properties()->slice().get("array")[2].copyString() == hello);
|
|
}
|
|
|
|
class MaintenanceTestActionPhaseOne : public ::testing::Test {
|
|
protected:
|
|
std::shared_ptr<arangodb::options::ProgramOptions> po;
|
|
arangodb::application_features::ApplicationServer as;
|
|
TestMaintenanceFeature feature;
|
|
MaintenanceFeature::errors_t errors;
|
|
|
|
std::map<std::string, Node> localNodes;
|
|
|
|
arangodb::MMFilesEngine engine; // arbitrary implementation that has index types registered
|
|
arangodb::StorageEngine* origStorageEngine;
|
|
|
|
MaintenanceTestActionPhaseOne()
|
|
: po(std::make_shared<arangodb::options::ProgramOptions>("test", std::string(),
|
|
std::string(),
|
|
"path")),
|
|
as(po, nullptr),
|
|
feature(as),
|
|
localNodes{{dbsIds[shortNames[0]], createNode(dbs0Str)},
|
|
{dbsIds[shortNames[1]], createNode(dbs1Str)},
|
|
{dbsIds[shortNames[2]], createNode(dbs2Str)}},
|
|
engine(as),
|
|
origStorageEngine(arangodb::EngineSelectorFeature::ENGINE) {
|
|
arangodb::EngineSelectorFeature::ENGINE = &engine;
|
|
}
|
|
|
|
~MaintenanceTestActionPhaseOne() {
|
|
arangodb::EngineSelectorFeature::ENGINE = origStorageEngine;
|
|
}
|
|
};
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, in_sync_should_have_0_effects) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
for (auto const& node : localNodes) {
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (actions.size() != 0) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 0);
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, local_databases_one_more_empty_database_should_be_dropped) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
localNodes.begin()->second("db3") = arangodb::velocypack::Slice::emptyObjectSlice();
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
localNodes.begin()->second.toBuilder().slice(),
|
|
localNodes.begin()->first, errors, feature, actions);
|
|
|
|
if (actions.size() != 1) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
ASSERT_TRUE(actions.front().name() == "DropDatabase");
|
|
ASSERT_TRUE(actions.front().get("database") == "db3");
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne,
|
|
local_databases_one_more_non_empty_database_should_be_dropped) {
|
|
std::vector<ActionDescription> actions;
|
|
localNodes.begin()->second("db3/col") = arangodb::velocypack::Slice::emptyObjectSlice();
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
localNodes.begin()->second.toBuilder().slice(),
|
|
localNodes.begin()->first, errors, feature, actions);
|
|
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
ASSERT_TRUE(actions.front().name() == "DropDatabase");
|
|
ASSERT_TRUE(actions.front().get("database") == "db3");
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne,
|
|
add_one_collection_to_db3_in_plan_with_shards_for_all_db_servers) {
|
|
std::string dbname("db3"), colname("x");
|
|
|
|
plan = originalPlan;
|
|
createPlanDatabase(dbname, plan);
|
|
createPlanCollection(dbname, colname, 1, 3, plan);
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
node.second("db3") = arangodb::velocypack::Slice::emptyObjectSlice();
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (actions.size() != 1) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "CreateCollection");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne,
|
|
add_two_more_collections_to_db3_in_plan_with_shards_for_all_db_servers) {
|
|
std::string dbname("db3"), colname1("x"), colname2("y");
|
|
|
|
plan = originalPlan;
|
|
createPlanDatabase(dbname, plan);
|
|
createPlanCollection(dbname, colname1, 1, 3, plan);
|
|
createPlanCollection(dbname, colname2, 1, 3, plan);
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
node.second("db3") = arangodb::velocypack::Slice::emptyObjectSlice();
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (actions.size() != 2) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 2);
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "CreateCollection");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, add_an_index_to_queues) {
|
|
plan = originalPlan;
|
|
auto cid = collectionMap(plan).at("_system/_queues");
|
|
auto shards = plan({"Collections", "_system", cid, "shards"}).children();
|
|
|
|
createPlanIndex("_system", cid, "hash", {"someField"}, false, false, false, plan);
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
auto local = node.second;
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
local.toBuilder().slice(), node.first,
|
|
errors, feature, actions);
|
|
|
|
size_t n = 0;
|
|
for (auto const& shard : shards) {
|
|
if (local.has({"_system", shard.first})) {
|
|
++n;
|
|
}
|
|
}
|
|
|
|
if (actions.size() != n) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == n);
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "EnsureIndex");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, remove_an_index_from_plan) {
|
|
std::string dbname("_system");
|
|
std::string indexes("indexes");
|
|
|
|
plan = originalPlan;
|
|
auto cid = collectionMap(plan).at("_system/bar");
|
|
auto shards = plan({"Collections", dbname, cid, "shards"}).children();
|
|
|
|
plan({"Collections", dbname, cid, indexes}).handle<POP>(arangodb::velocypack::Slice::emptyObjectSlice());
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
auto local = node.second;
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
local.toBuilder().slice(), node.first,
|
|
errors, feature, actions);
|
|
|
|
size_t n = 0;
|
|
for (auto const& shard : shards) {
|
|
if (local.has({"_system", shard.first})) {
|
|
++n;
|
|
}
|
|
}
|
|
|
|
if (actions.size() != n) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == n);
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "DropIndex");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, add_one_collection_to_local) {
|
|
plan = originalPlan;
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
createLocalCollection("_system", "1111111", node.second);
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (actions.size() != 1) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "DropCollection");
|
|
ASSERT_TRUE(action.get("database") == "_system");
|
|
ASSERT_TRUE(action.get("collection") == "s1111112");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne,
|
|
modify_journalsize_in_plan_should_update_the_according_collection) {
|
|
VPackBuilder v;
|
|
v.add(VPackValue(0));
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
std::string dbname = "_system";
|
|
std::string prop = arangodb::maintenance::JOURNAL_SIZE;
|
|
|
|
auto cb = node.second(dbname).children().begin()->second->toBuilder();
|
|
auto collection = cb.slice();
|
|
auto shname = collection.get(NAME).copyString();
|
|
|
|
(*node.second(dbname).children().begin()->second)(prop) = v.slice();
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
/*
|
|
if (actions.size() != 1) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
|
|
ASSERT_TRUE(action.name() == "UpdateCollection");
|
|
ASSERT_TRUE(action.get("shard") == shname);
|
|
ASSERT_TRUE(action.get("database") == dbname);
|
|
auto const props = action.properties();
|
|
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, have_theleader_set_to_empty) {
|
|
VPackBuilder v;
|
|
v.add(VPackValue(std::string()));
|
|
|
|
for (auto node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
auto& collection = *node.second("foo").children().begin()->second;
|
|
auto& leader = collection("theLeader");
|
|
|
|
bool check = false;
|
|
if (!leader.getString().empty()) {
|
|
check = true;
|
|
leader = v.slice();
|
|
}
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (check) {
|
|
if (actions.size() != 1) {
|
|
std::cout << __FILE__ << ":" << __LINE__ << " " << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "TakeoverShardLeadership");
|
|
ASSERT_TRUE(action.has("shard"));
|
|
ASSERT_TRUE(action.get("shard") == collection("name").getString());
|
|
ASSERT_TRUE(action.get("localLeader").empty());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne,
|
|
empty_db3_in_plan_should_drop_all_local_db3_collections_on_all_servers) {
|
|
plan(PLAN_COL_PATH + "db3") = arangodb::velocypack::Slice::emptyObjectSlice();
|
|
|
|
createPlanDatabase("db3", plan);
|
|
|
|
for (auto& node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
node.second("db3") = node.second("_system");
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
ASSERT_TRUE(actions.size() == node.second("db3").children().size());
|
|
for (auto const& action : actions) {
|
|
ASSERT_TRUE(action.name() == "DropCollection");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, resign_leadership) {
|
|
plan = originalPlan;
|
|
std::string const dbname("_system");
|
|
std::string const colname("bar");
|
|
auto cid = collectionMap(plan).at(dbname + "/" + colname);
|
|
auto& shards = plan({"Collections", dbname, cid, "shards"}).children();
|
|
|
|
for (auto const& node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
std::string shname;
|
|
|
|
for (auto const& shard : shards) {
|
|
shname = shard.first;
|
|
auto shardBuilder = shard.second->toBuilder();
|
|
Slice servers = shardBuilder.slice();
|
|
|
|
ASSERT_TRUE(servers.isArray());
|
|
ASSERT_TRUE(servers.length() == 2);
|
|
auto const leader = servers[0].copyString();
|
|
auto const follower = servers[1].copyString();
|
|
|
|
if (leader == node.first) {
|
|
VPackBuilder newServers;
|
|
{
|
|
VPackArrayBuilder a(&newServers);
|
|
newServers.add(VPackValue(std::string("_") + leader));
|
|
newServers.add(VPackValue(follower));
|
|
}
|
|
plan({"Collections", dbname, cid, "shards", shname}) = newServers.slice();
|
|
break;
|
|
}
|
|
}
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (actions.size() != 2) {
|
|
std::cout << actions << std::endl;
|
|
}
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
ASSERT_TRUE(actions[0].name() == "ResignShardLeadership");
|
|
ASSERT_TRUE(actions[0].get(DATABASE) == dbname);
|
|
ASSERT_TRUE(actions[0].get(SHARD) == shname);
|
|
}
|
|
}
|
|
|
|
TEST_F(MaintenanceTestActionPhaseOne, removed_follower_in_plan_must_be_dropped) {
|
|
plan = originalPlan;
|
|
std::string const dbname("_system");
|
|
std::string const colname("bar");
|
|
auto cid = collectionMap(plan).at(dbname + "/" + colname);
|
|
Node::Children& shards = plan({"Collections", dbname, cid, "shards"}).children();
|
|
auto firstShard = shards.begin();
|
|
VPackBuilder b = firstShard->second->toBuilder();
|
|
std::string const shname = firstShard->first;
|
|
std::string const leaderName = b.slice()[0].copyString();
|
|
std::string const followerName = b.slice()[1].copyString();
|
|
firstShard->second->handle<POP>(arangodb::velocypack::Slice::emptyObjectSlice());
|
|
|
|
for (auto const& node : localNodes) {
|
|
std::vector<ActionDescription> actions;
|
|
|
|
arangodb::maintenance::diffPlanLocal(plan.toBuilder().slice(),
|
|
node.second.toBuilder().slice(),
|
|
node.first, errors, feature, actions);
|
|
|
|
if (node.first == followerName) {
|
|
// Must see an action dropping the shard
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
ASSERT_TRUE(actions.front().name() == "DropCollection");
|
|
ASSERT_TRUE(actions.front().get(DATABASE) == dbname);
|
|
ASSERT_TRUE(actions.front().get(COLLECTION) == shname);
|
|
} else if (node.first == leaderName) {
|
|
// Must see an UpdateCollection action to drop the follower
|
|
ASSERT_TRUE(actions.size() == 1);
|
|
ASSERT_TRUE(actions.front().name() == "UpdateCollection");
|
|
ASSERT_TRUE(actions.front().get(DATABASE) == dbname);
|
|
ASSERT_TRUE(actions.front().get(SHARD) == shname);
|
|
ASSERT_TRUE(actions.front().get(FOLLOWERS_TO_DROP) == followerName);
|
|
} else {
|
|
// No actions required
|
|
ASSERT_TRUE(actions.size() == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|