mirror of https://gitee.com/bigwinds/arangodb
856 lines
25 KiB
C++
856 lines
25 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 "catch.hpp"
|
|
|
|
#include "Cluster/Maintenance.h"
|
|
|
|
#include <velocypack/Iterator.h>
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <iostream>
|
|
#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;
|
|
}
|
|
|
|
TEST_CASE("ActionDescription", "[cluster][maintenance]") {
|
|
plan = createNode(planStr);
|
|
originalPlan = plan;
|
|
supervision = createNode(supervisionStr);
|
|
current = createNode(currentStr);
|
|
|
|
dbsIds = matchShortLongIds(supervision);
|
|
|
|
SECTION("Construct minimal ActionDescription") {
|
|
ActionDescription desc(std::map<std::string,std::string>{{"name", "SomeAction"}});
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
}
|
|
|
|
SECTION("Construct minimal ActionDescription with nullptr props") {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
}
|
|
|
|
SECTION("Construct minimal ActionDescription with empty props") {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
}
|
|
|
|
SECTION("Retrieve non-assigned key from ActionDescription") {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
try {
|
|
auto bogus = desc.get("bogus");
|
|
REQUIRE(bogus == "bogus");
|
|
} catch (std::out_of_range const& e) {}
|
|
std::string value;
|
|
auto res = desc.get("bogus", value);
|
|
REQUIRE(value.empty());
|
|
REQUIRE(!res.ok());
|
|
}
|
|
|
|
SECTION("Retrieve non-assigned key from ActionDescription") {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}, {"bogus", "bogus"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
try {
|
|
auto bogus = desc.get("bogus");
|
|
REQUIRE(bogus == "bogus");
|
|
} catch (std::out_of_range const& e) {}
|
|
std::string value;
|
|
auto res = desc.get("bogus", value);
|
|
REQUIRE(value == "bogus");
|
|
REQUIRE(res.ok());
|
|
}
|
|
|
|
SECTION("Retrieve non-assigned properties from ActionDescription") {
|
|
std::shared_ptr<VPackBuilder> props;
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties() == nullptr);
|
|
}
|
|
|
|
SECTION("Retrieve empty properties from ActionDescription") {
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties()->isEmpty());
|
|
}
|
|
|
|
SECTION("Retrieve empty object properties from ActionDescription") {
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{ VPackObjectBuilder empty(props.get()); }
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties()->slice().isEmptyObject());
|
|
}
|
|
|
|
SECTION("Retrieve string value from ActionDescription's properties") {
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{ VPackObjectBuilder obj(props.get());
|
|
props->add("hello", VPackValue("world")); }
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties()->slice().hasKey("hello"));
|
|
REQUIRE(desc.properties()->slice().get("hello").copyString() == "world");
|
|
}
|
|
|
|
SECTION("Retrieve double value from ActionDescription's 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"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties()->slice().hasKey("pi"));
|
|
REQUIRE(
|
|
desc.properties()->slice().get("pi").getNumber<double>() == pi);
|
|
}
|
|
|
|
SECTION("Retrieve integer value from ActionDescription's properties") {
|
|
size_t one = 1;
|
|
auto props = std::make_shared<VPackBuilder>();
|
|
{ VPackObjectBuilder obj(props.get());
|
|
props->add("one", VPackValue(one)); }
|
|
ActionDescription desc({{"name", "SomeAction"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties()->slice().hasKey("one"));
|
|
REQUIRE(
|
|
desc.properties()->slice().get("one").getNumber<size_t>() == one);
|
|
}
|
|
|
|
SECTION("Retrieve array value from ActionDescription's 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"}}, props);
|
|
REQUIRE(desc.get("name") == "SomeAction");
|
|
REQUIRE(desc.properties()->slice().hasKey("array"));
|
|
REQUIRE(desc.properties()->slice().get("array").isArray());
|
|
REQUIRE(desc.properties()->slice().get("array").length() == 3);
|
|
REQUIRE(desc.properties()->slice().get("array")[0].getNumber<double>()==pi);
|
|
REQUIRE(desc.properties()->slice().get("array")[1].getNumber<size_t>()==one);
|
|
REQUIRE(desc.properties()->slice().get("array")[2].copyString() == hello );
|
|
}
|
|
|
|
}
|
|
|
|
TEST_CASE("ActionPhaseOne", "[cluster][maintenance]") {
|
|
|
|
std::shared_ptr<arangodb::options::ProgramOptions> po =
|
|
std::make_shared<arangodb::options::ProgramOptions>(
|
|
"test", std::string(), std::string(), "path");
|
|
arangodb::application_features::ApplicationServer as(po, nullptr);
|
|
TestMaintenanceFeature feature(as);
|
|
MaintenanceFeature::errors_t errors;
|
|
|
|
std::map<std::string, Node> localNodes {
|
|
{dbsIds[shortNames[0]], createNode(dbs0Str)},
|
|
{dbsIds[shortNames[1]], createNode(dbs1Str)},
|
|
{dbsIds[shortNames[2]], createNode(dbs2Str)}};
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == 0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == 1);
|
|
REQUIRE(actions.front().name() == "DropDatabase");
|
|
REQUIRE(actions.front().get("database") == "db3");
|
|
|
|
}
|
|
|
|
|
|
SECTION("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);
|
|
|
|
REQUIRE(actions.size() == 1);
|
|
REQUIRE(actions.front().name() == "DropDatabase");
|
|
REQUIRE(actions.front().get("database") == "db3");
|
|
|
|
}
|
|
|
|
|
|
SECTION(
|
|
"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;
|
|
}
|
|
REQUIRE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "CreateCollection");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("Add two more collection 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;
|
|
}
|
|
REQUIRE(actions.size() == 2);
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "CreateCollection");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == n);
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "EnsureIndex");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == n);
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "DropIndex");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "DropCollection");
|
|
REQUIRE(action.get("database") == "_system");
|
|
REQUIRE(action.get("collection") == "s1111112");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
|
|
REQUIRE(action.name() == "UpdateCollection");
|
|
REQUIRE(action.get("shard") == shname);
|
|
REQUIRE(action.get("database") == dbname);
|
|
auto const props = action.properties();
|
|
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
SECTION("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;
|
|
}
|
|
REQUIRE(actions.size() == 1);
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "UpdateCollection");
|
|
REQUIRE(action.has("shard"));
|
|
REQUIRE(action.get("shard") == collection("name").getString());
|
|
REQUIRE(action.get("localLeader").empty());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SECTION(
|
|
"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);
|
|
|
|
REQUIRE(actions.size() == node.second("db3").children().size());
|
|
for (auto const& action : actions) {
|
|
REQUIRE(action.name() == "DropCollection");
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SECTION("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();
|
|
|
|
REQUIRE(servers.isArray());
|
|
REQUIRE(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;
|
|
}
|
|
REQUIRE(actions.size() == 2);
|
|
REQUIRE(actions.front().name() == "UpdateCollection");
|
|
REQUIRE(actions.front().get(DATABASE) == dbname);
|
|
REQUIRE(actions.front().get(SHARD) == shname);
|
|
REQUIRE(actions.front().get("localLeader") == std::string(""));
|
|
REQUIRE(actions[1].name() == "ResignShardLeadership");
|
|
REQUIRE(actions[1].get(DATABASE) == dbname);
|
|
REQUIRE(actions[1].get(SHARD) == shname);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TEST_CASE("ActionPhaseTwo", "[cluster][maintenance]") {
|
|
|
|
plan = originalPlan;
|
|
|
|
std::map<std::string, Node> localNodes {
|
|
{dbsIds[shortNames[0]], createNode(dbs0Str)},
|
|
{dbsIds[shortNames[1]], createNode(dbs1Str)},
|
|
{dbsIds[shortNames[2]], createNode(dbs2Str)}};
|
|
|
|
|
|
/* SECTION("Diffing local and current in equilibrium") {
|
|
|
|
VPackSlice p = plan.toBuilder().slice();
|
|
REQUIRE(p.hasKey("Collections"));
|
|
REQUIRE(p.get("Collections").isObject());
|
|
VPackSlice c = current.toBuilder().slice();
|
|
|
|
for (auto const& node : localNodes) {
|
|
|
|
VPackSlice l = node.second.toBuilder().slice();
|
|
REQUIRE(c.isObject());
|
|
REQUIRE(p.isObject());
|
|
REQUIRE(l.isObject());
|
|
|
|
VPackBuilder rb;
|
|
{ VPackObjectBuilder o(&rb);
|
|
rb.add(VPackValue("phaseTwo"));
|
|
{ VPackObjectBuilder oo(&rb);
|
|
arangodb::maintenance::reportInCurrent(p, c, l, node.first, rb); }}
|
|
|
|
VPackSlice report = rb.slice();
|
|
|
|
VPackSlice pt = report.get("PhaseTwo");
|
|
REQUIRE(pt.isObject());
|
|
|
|
if (!pt.isEmptyObject()) {
|
|
std::cout << pt.toJson() << std::endl;
|
|
}
|
|
REQUIRE(pt.isEmptyObject());
|
|
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
#endif
|