1
0
Fork 0
arangodb/arangod/Cluster/ClusterRepairOperations.h

388 lines
15 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 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 Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_CLUSTER_CLUSTER_REPAIR_OPERATIONS_H
#define ARANGOD_CLUSTER_CLUSTER_REPAIR_OPERATIONS_H
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <velocypack/velocypack-common.h>
#include <map>
#include "ClusterInfo.h"
namespace arangodb {
namespace velocypack {
template <typename T>
class Buffer;
}
namespace cluster_repairs {
using DBServers = std::vector<ServerID>;
using VPackBufferPtr = std::shared_ptr<velocypack::Buffer<uint8_t>>;
// Elements are (shardId, protoShardId, dbServers). The dbServers are
// the same for both shard and protoShard at this point.
using ShardWithProtoAndDbServers = std::tuple<ShardID, ShardID, DBServers>;
class VersionSort {
public:
bool operator()(std::string const& a, std::string const& b) const;
private:
// boost::variant cannot discern between ambiguously convertible types
// (unlike std::variant from C++17). So, for compilers where char is unsigned,
// using uint64_t results in a compile error (vice versa for signed chars and
// int64_t) and therefore this distinct type is needed.
class WrappedUInt64 {
public:
uint64_t value;
explicit inline WrappedUInt64(uint64_t value_) : value(value_) {}
bool inline operator<(WrappedUInt64 const& other) const {
return this->value < other.value;
}
};
using CharOrInt = boost::variant<char, WrappedUInt64>;
std::vector<CharOrInt> static splitVersion(std::string const& str);
};
// "proto collection" always means the collection referred to in the
// "distributeShardsLike" attribute of "collection"
// All RepairOperations use a constructor with named parameters. This is done to
// forbid braced initializer lists and assure that all constructions initialize
// every member (defaults really don't make sense here), make constructions more
// readable and avoid mixing up arguments of the same type (most are std::string
// or typedefs thereof).
// The following are used for the named parameters mentioned above.
template <typename Tag, typename Type>
struct tagged_argument {
Type const& value;
};
template <typename Tag, typename Type>
struct keyword {
// NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator)
struct tagged_argument<Tag, Type> const operator=(Type const& arg) const {
return tagged_argument<Tag, Type>{arg};
}
static keyword<Tag, Type> const instance;
};
template <typename Tag, typename Type>
struct keyword<Tag, Type> const keyword<Tag, Type>::instance = {};
// Parameters used in Operation-constructors
namespace tag {
struct database;
struct collectionId;
struct collectionName;
struct protoCollectionId;
struct protoCollectionName;
struct shard;
struct protoShard;
struct from;
struct to;
struct isLeader;
struct protoReplicationFactor;
struct collectionReplicationFactor;
struct replicationFactor;
struct renameDistributeShardsLike;
struct shards;
struct leader;
struct followers;
struct protoFollowers;
struct distributeShardsLike;
struct repairingDistributeShardsLike;
struct shardsById;
struct deleted;
struct isSmart;
} // namespace tag
namespace {
keyword<tag::database, std::string> _database = decltype(_database)::instance;
keyword<tag::collectionId, std::string> _collectionId = decltype(_collectionId)::instance;
keyword<tag::collectionName, std::string> _collectionName =
decltype(_collectionName)::instance;
keyword<tag::protoCollectionId, std::string> _protoCollectionId =
decltype(_protoCollectionId)::instance;
keyword<tag::protoCollectionName, std::string> _protoCollectionName =
decltype(_protoCollectionName)::instance;
keyword<tag::shard, std::string> _shard = decltype(_shard)::instance;
keyword<tag::protoShard, std::string> _protoShard = decltype(_protoShard)::instance;
keyword<tag::from, std::string> _from = decltype(_from)::instance;
keyword<tag::to, std::string> _to = decltype(_to)::instance;
keyword<tag::isLeader, bool> _isLeader = decltype(_isLeader)::instance;
keyword<tag::protoReplicationFactor, uint64_t> _protoReplicationFactor =
decltype(_protoReplicationFactor)::instance;
keyword<tag::collectionReplicationFactor, uint64_t> _collectionReplicationFactor =
decltype(_collectionReplicationFactor)::instance;
keyword<tag::replicationFactor, uint64_t> _replicationFactor =
decltype(_replicationFactor)::instance;
keyword<tag::renameDistributeShardsLike, bool> _renameDistributeShardsLike =
decltype(_renameDistributeShardsLike)::instance;
keyword<tag::shards, std::vector<ShardWithProtoAndDbServers>> _shards =
decltype(_shards)::instance;
keyword<tag::leader, std::string> _leader = decltype(_leader)::instance;
keyword<tag::followers, std::vector<std::string>> _followers = decltype(_followers)::instance;
keyword<tag::protoFollowers, std::vector<std::string>> _protoFollowers =
decltype(_protoFollowers)::instance;
keyword<tag::distributeShardsLike, boost::optional<CollectionID>> _distributeShardsLike =
decltype(_distributeShardsLike)::instance;
keyword<tag::repairingDistributeShardsLike, boost::optional<CollectionID>> _repairingDistributeShardsLike =
decltype(_repairingDistributeShardsLike)::instance;
keyword<tag::shardsById, std::map<ShardID, DBServers, VersionSort>> _shardsById =
decltype(_shardsById)::instance;
keyword<tag::deleted, bool> _deleted = decltype(_deleted)::instance;
keyword<tag::isSmart, bool> _isSmart = decltype(_isSmart)::instance;
} // namespace
// Applies the following changes iff renameDistributeShardsLike is true:
// * Renames "distributeShardsLike" to "repairingDistributeShardsLike"
// * Sets collection.replicationFactor = `protoReplicationFactor`
// Asserts the following preconditions:
// if renameDistributeShardsLike:
// * collection.distributeShardsLike == `protoCollectionId`
// * collection.repairingDistributeShardsLike == undefined
// * collection.replicationFactor == `collectionReplicationFactor`
// * protoCollection.replicationFactor == `protoReplicationFactor`
// else:
// * collection.repairingDistributeShardsLike == `protoCollectionId`
// * collection.distributeShardsLike == undefined
// * collection.replicationFactor == `protoReplicationFactor` (sic!)
// * protoCollection.replicationFactor == `protoReplicationFactor`
//
// See RepairOperationToTransactionVisitor for the implementation.
struct BeginRepairsOperation {
DatabaseID database;
CollectionID collectionId;
std::string collectionName;
CollectionID protoCollectionId;
std::string protoCollectionName;
uint64_t collectionReplicationFactor;
uint64_t protoReplicationFactor;
bool renameDistributeShardsLike;
BeginRepairsOperation() = delete;
// constructor with named parameters
BeginRepairsOperation(
tagged_argument<tag::database, DatabaseID> database_,
tagged_argument<tag::collectionId, CollectionID> collectionId_,
tagged_argument<tag::collectionName, std::string> collectionName_,
tagged_argument<tag::protoCollectionId, CollectionID> protoCollectionId_,
tagged_argument<tag::protoCollectionName, std::string> protoCollectionName_,
tagged_argument<tag::collectionReplicationFactor, uint64_t> collectionReplicationFactor_,
tagged_argument<tag::protoReplicationFactor, uint64_t> protoReplicationFactor_,
tagged_argument<tag::renameDistributeShardsLike, bool> renameDistributeShardsLike_);
};
// Applies the following changes:
// * Renames "repairingDistributeShardsLike" to "distributeShardsLike"
// Asserts the following preconditions:
// * collection.repairingDistributeShardsLike == `protoCollectionId`
// * collection.distributeShardsLike == undefined
// * collection.replicationFactor == `replicationFactor`
// * protoCollection.replicationFactor == `replicationFactor`
// * shards of both collection and protoCollection match `shards`
//
// `shards` should contain *all* shards or collection and protoCollection,
// so if this transaction succeeds, the collection is guaranteed to be
// completely fixed.
//
// See RepairOperationToTransactionVisitor for the implementation.
struct FinishRepairsOperation {
DatabaseID database;
CollectionID collectionId;
std::string collectionName;
CollectionID protoCollectionId;
std::string protoCollectionName;
std::vector<ShardWithProtoAndDbServers> shards;
uint64_t replicationFactor;
FinishRepairsOperation() = delete;
// constructor with named parameters
FinishRepairsOperation(
tagged_argument<tag::database, DatabaseID> database_,
tagged_argument<tag::collectionId, CollectionID> collectionId_,
tagged_argument<tag::collectionName, std::string> collectionName_,
tagged_argument<tag::protoCollectionId, CollectionID> protoCollectionId_,
tagged_argument<tag::protoCollectionName, std::string> protoCollectionName_,
tagged_argument<tag::shards, std::vector<ShardWithProtoAndDbServers>> shards_,
tagged_argument<tag::replicationFactor, uint64_t> replicationFactor_);
};
// Writes a moveShard job in Target/ToDo/ to move
// the `shard` from the server `from` to server `to`.
//
// See RepairOperationToTransactionVisitor for the implementation.
struct MoveShardOperation {
DatabaseID database;
CollectionID collectionId;
std::string collectionName;
ShardID shard;
ServerID from;
ServerID to;
bool isLeader;
MoveShardOperation() = delete;
// constructor with named parameters
MoveShardOperation(tagged_argument<tag::database, DatabaseID> database_,
tagged_argument<tag::collectionId, CollectionID> collectionId_,
tagged_argument<tag::collectionName, std::string> collectionName_,
tagged_argument<tag::shard, ShardID> shard_,
tagged_argument<tag::from, ServerID> from_,
tagged_argument<tag::to, ServerID> to_,
tagged_argument<tag::isLeader, bool> isLeader_);
VPackBufferPtr toVPackTodo(uint64_t jobId,
std::chrono::system_clock::time_point jobCreationTimestamp) const;
};
// Applies the following changes:
// * Sets collection/shards/`shard` to leader :: protoFollowers
// Asserts the following preconditions:
// * collection/shards/`shard` == leader :: followers
// * collection/shards/`shard` == leader :: protoFollowers
//
// See RepairOperationToTransactionVisitor for the implementation.
struct FixServerOrderOperation {
DatabaseID database;
CollectionID collectionId;
std::string collectionName;
CollectionID protoCollectionId;
std::string protoCollectionName;
ShardID shard;
ShardID protoShard;
ServerID leader;
std::vector<ServerID> followers;
std::vector<ServerID> protoFollowers;
FixServerOrderOperation() = delete;
// constructor with named parameters
FixServerOrderOperation(
tagged_argument<tag::database, DatabaseID> database_,
tagged_argument<tag::collectionId, CollectionID> collectionId_,
tagged_argument<tag::collectionName, std::string> collectionName_,
tagged_argument<tag::protoCollectionId, CollectionID> protoCollectionId_,
tagged_argument<tag::protoCollectionName, std::string> protoCollectionName_,
tagged_argument<tag::shard, ShardID> shard_,
tagged_argument<tag::protoShard, ShardID> protoShard_,
tagged_argument<tag::leader, ServerID> leader_,
tagged_argument<tag::followers, std::vector<ServerID>> followers_,
tagged_argument<tag::protoFollowers, std::vector<ServerID>> protoFollowers_);
};
bool operator==(BeginRepairsOperation const& left, BeginRepairsOperation const& right);
bool operator==(FinishRepairsOperation const& left, FinishRepairsOperation const& right);
bool operator==(MoveShardOperation const& left, MoveShardOperation const& right);
bool operator==(FixServerOrderOperation const& left, FixServerOrderOperation const& right);
std::ostream& operator<<(std::ostream& ostream, BeginRepairsOperation const& operation);
std::ostream& operator<<(std::ostream& ostream, FinishRepairsOperation const& operation);
std::ostream& operator<<(std::ostream& ostream, MoveShardOperation const& operation);
std::ostream& operator<<(std::ostream& ostream, FixServerOrderOperation const& operation);
using RepairOperation =
boost::variant<BeginRepairsOperation const, FinishRepairsOperation const, MoveShardOperation const, FixServerOrderOperation const>;
std::string getTypeAsString(RepairOperation const& op);
std::ostream& operator<<(std::ostream& ostream, RepairOperation const& operation);
// Converts any RepairOperation to a Transaction. If its a job (i.e. put in
// Target/ToDo/), it returns the corresponding job id as well.
class RepairOperationToTransactionVisitor
: public boost::static_visitor<std::pair<AgencyWriteTransaction, boost::optional<uint64_t>>> {
using ReturnValueT = std::pair<AgencyWriteTransaction, boost::optional<uint64_t>>;
public:
RepairOperationToTransactionVisitor();
RepairOperationToTransactionVisitor(std::function<uint64_t()> getJobId,
std::function<std::chrono::system_clock::time_point()> getJobCreationTimestamp);
ReturnValueT operator()(BeginRepairsOperation const& op);
ReturnValueT operator()(FinishRepairsOperation const& op);
ReturnValueT operator()(MoveShardOperation const& op);
ReturnValueT operator()(FixServerOrderOperation const& op);
private:
std::vector<VPackBufferPtr> _vpackBufferArray;
std::function<uint64_t()> _getJobId;
std::function<std::chrono::system_clock::time_point()> _getJobCreationTimestamp;
std::vector<VPackBufferPtr>&& steal();
std::string agencyCollectionId(DatabaseID database, CollectionID collection) const;
VPackBufferPtr createShardDbServerArray(ServerID const& leader,
DBServers const& followers) const;
template <typename T>
VPackSlice createSingleValueVPack(T val);
};
// Adds any RepairOperation to a VPack as an object, suitable for users to see.
// Doesn't contain all data, some members are named differently.
// TODO Maybe it would still be good to add all members, at least for the
// functional tests?
class RepairOperationToVPackVisitor : public boost::static_visitor<void> {
public:
RepairOperationToVPackVisitor() = delete;
explicit RepairOperationToVPackVisitor(VPackBuilder& builder);
void operator()(BeginRepairsOperation const& op);
void operator()(FinishRepairsOperation const& op);
void operator()(MoveShardOperation const& op);
void operator()(FixServerOrderOperation const& op);
private:
VPackBuilder& _builder;
VPackBuilder& builder();
};
} // namespace cluster_repairs
} // namespace arangodb
#endif // ARANGOD_CLUSTER_CLUSTER_REPAIR_OPERATIONS_H