1
0
Fork 0

Feature/cluster inventory version (#3152)

* Get rid of a compiler warning in community edition.

* Teach /_api/replication/clusterInventory to report Plan/Version and readiness.

This is first implemented in the ClusterInfo library.
Then the clusterInventory code uses it and checks readiness.
Readiness of a collection means that it is created and all shards
and all replications have been created and are in sync.
This commit is contained in:
Max Neunhöffer 2017-08-30 13:34:23 +02:00 committed by Jan
parent 5677baf561
commit f3acea797b
7 changed files with 103 additions and 84 deletions

View File

@ -903,7 +903,9 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
Serv2ColMap mappingServerToCollections;
size_t length = edges.size();
#ifdef USE_ENTERPRISE
transaction::Methods* trx = query->trx();
#endif
auto findServerLists = [&] (ShardID const& shard) -> Serv2ColMap::iterator {
auto serverList = clusterInfo->getResponsibleServer(shard);

View File

@ -108,61 +108,8 @@ static std::string extractErrorMessage(std::string const& shardId,
/// @brief creates an empty collection info object
////////////////////////////////////////////////////////////////////////////////
CollectionInfoCurrent::CollectionInfoCurrent() {}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a collection info object from json
////////////////////////////////////////////////////////////////////////////////
CollectionInfoCurrent::CollectionInfoCurrent(ShardID const& shardID,
VPackSlice slice) {
add(shardID, slice);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a collection info object from another
////////////////////////////////////////////////////////////////////////////////
CollectionInfoCurrent::CollectionInfoCurrent(CollectionInfoCurrent const& other)
: _vpacks(other._vpacks) {
copyAllVPacks();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief moves a collection info current object from another
////////////////////////////////////////////////////////////////////////////////
CollectionInfoCurrent::CollectionInfoCurrent(CollectionInfoCurrent&& other) {
_vpacks.swap(other._vpacks);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copy assigns a collection info current object from another one
////////////////////////////////////////////////////////////////////////////////
CollectionInfoCurrent& CollectionInfoCurrent::operator=(
CollectionInfoCurrent const& other) {
if (this == &other) {
return *this;
}
_vpacks = other._vpacks;
copyAllVPacks();
return *this;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a collection info object from json
////////////////////////////////////////////////////////////////////////////////
CollectionInfoCurrent& CollectionInfoCurrent::operator=(
CollectionInfoCurrent&& other) {
if (this == &other) {
return *this;
}
_vpacks.clear();
_vpacks.swap(other._vpacks);
return *this;
}
CollectionInfoCurrent::CollectionInfoCurrent(uint64_t currentVersion)
: _currentVersion(currentVersion) {}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys a collection info object
@ -170,17 +117,6 @@ CollectionInfoCurrent& CollectionInfoCurrent::operator=(
CollectionInfoCurrent::~CollectionInfoCurrent() {}
////////////////////////////////////////////////////////////////////////////////
/// @brief copy slices behind the pointers in the map _vpacks
////////////////////////////////////////////////////////////////////////////////
void CollectionInfoCurrent::copyAllVPacks() {
for (auto it : _vpacks) {
auto builder = std::make_shared<VPackBuilder>();
builder->add(it.second->slice());
it.second = builder;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create the clusterinfo instance
////////////////////////////////////////////////////////////////////////////////
@ -201,7 +137,8 @@ ClusterInfo* ClusterInfo::instance() { return _instance.get(); }
////////////////////////////////////////////////////////////////////////////////
ClusterInfo::ClusterInfo(AgencyCallbackRegistry* agencyCallbackRegistry)
: _agency(), _agencyCallbackRegistry(agencyCallbackRegistry), _uniqid() {
: _agency(), _agencyCallbackRegistry(agencyCallbackRegistry),
_planVersion(0), _currentVersion(0), _uniqid() {
_uniqid._currentValue = 1ULL;
_uniqid._upperValue = 0ULL;
@ -450,6 +387,19 @@ void ClusterInfo::loadPlan() {
VPackSlice planSlice = planBuilder->slice();
if (planSlice.isObject()) {
uint64_t newPlanVersion = 0;
VPackSlice planVersionSlice = planSlice.get("Version");
if (planVersionSlice.isNumber()) {
try {
newPlanVersion = planVersionSlice.getNumber<uint64_t>();
} catch (...) {
}
}
if (newPlanVersion == 0) {
LOG_TOPIC(WARN, Logger::CLUSTER)
<< "Attention: /arango/Plan/Version in the agency is not set or not "
"a positive number.";
}
decltype(_plannedDatabases) newDatabases;
decltype(_plannedCollections) newCollections; // map<string /*database id*/
// ,map<string /*collection id*/
@ -590,6 +540,7 @@ void ClusterInfo::loadPlan() {
vocbase, collectionSlice);
}
#endif
newCollection->setPlanVersion(newPlanVersion);
std::string const collectionName = newCollection->name();
if (isCoordinator && !selectivityEstimates.empty()){
LOG_TOPIC(TRACE, Logger::CLUSTER) << "copy index estimates";
@ -662,6 +613,7 @@ void ClusterInfo::loadPlan() {
WRITE_LOCKER(writeLocker, _planProt.lock);
_plan = planBuilder;
_planVersion = newPlanVersion;
if (swapDatabases) {
_plannedDatabases.swap(newDatabases);
}
@ -718,6 +670,20 @@ void ClusterInfo::loadCurrent() {
VPackSlice currentSlice = currentBuilder->slice();
if (currentSlice.isObject()) {
uint64_t newCurrentVersion = 0;
VPackSlice currentVersionSlice = currentSlice.get("Version");
if (currentVersionSlice.isNumber()) {
try {
newCurrentVersion = currentVersionSlice.getNumber<uint64_t>();
} catch (...) {
}
}
if (newCurrentVersion == 0) {
LOG_TOPIC(WARN, Logger::CLUSTER)
<< "Attention: /arango/Current/Version in the agency is not set or "
"not a positive number.";
}
decltype(_currentDatabases) newDatabases;
decltype(_currentCollections) newCollections;
decltype(_shardIds) newShardIds;
@ -758,7 +724,7 @@ void ClusterInfo::loadCurrent() {
std::string const collectionName = collectionSlice.key.copyString();
auto collectionDataCurrent =
std::make_shared<CollectionInfoCurrent>();
std::make_shared<CollectionInfoCurrent>(newCurrentVersion);
for (auto const& shardSlice :
VPackObjectIterator(collectionSlice.value)) {
std::string const shardID = shardSlice.key.copyString();
@ -788,6 +754,7 @@ void ClusterInfo::loadCurrent() {
// Now set the new value:
WRITE_LOCKER(writeLocker, _currentProt.lock);
_current = currentBuilder;
_currentVersion = newCurrentVersion;
if (swapDatabases) {
_currentDatabases.swap(newDatabases);
}
@ -929,7 +896,7 @@ std::shared_ptr<CollectionInfoCurrent> ClusterInfo::getCollectionCurrent(
loadCurrent();
}
return std::make_shared<CollectionInfoCurrent>();
return std::make_shared<CollectionInfoCurrent>(0);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -56,23 +56,18 @@ class CollectionInfoCurrent {
friend class ClusterInfo;
public:
CollectionInfoCurrent();
CollectionInfoCurrent(uint64_t currentVersion);
CollectionInfoCurrent(ShardID const&, VPackSlice);
CollectionInfoCurrent(CollectionInfoCurrent const&) = delete;
CollectionInfoCurrent(CollectionInfoCurrent const&);
CollectionInfoCurrent(CollectionInfoCurrent&&) = delete;
CollectionInfoCurrent(CollectionInfoCurrent&&);
CollectionInfoCurrent& operator=(CollectionInfoCurrent const&) = delete;
CollectionInfoCurrent& operator=(CollectionInfoCurrent const&);
CollectionInfoCurrent& operator=(CollectionInfoCurrent&&);
CollectionInfoCurrent& operator=(CollectionInfoCurrent&&) = delete;
~CollectionInfoCurrent();
private:
void copyAllVPacks();
public:
bool add(ShardID const& shardID, VPackSlice slice) {
auto it = _vpacks.find(shardID);
@ -177,6 +172,14 @@ class CollectionInfoCurrent {
return std::string();
}
//////////////////////////////////////////////////////////////////////////////
/// @brief get version that underlies this info in Current in the agency
//////////////////////////////////////////////////////////////////////////////
uint64_t getCurrentVersion() const {
return _currentVersion;
}
//////////////////////////////////////////////////////////////////////////////
/// @brief local helper to return boolean flags
//////////////////////////////////////////////////////////////////////////////
@ -206,6 +209,9 @@ class CollectionInfoCurrent {
private:
std::unordered_map<ShardID, std::shared_ptr<VPackBuilder>> _vpacks;
uint64_t _currentVersion; // Version of Current in the agency that
// underpins the data presented in this object
};
class ClusterInfo {
@ -609,6 +615,12 @@ class ClusterInfo {
ProtectionData _planProt;
uint64_t _planVersion; // This is the version in the Plan which underlies
// the data in _plannedCollections, _shards and
// _shardKeys
uint64_t _currentVersion; // This is the version in Current which underlies
// the data in _currentDatabases,
// _currentCollections and _shardsIds
std::unordered_map<DatabaseID,
std::unordered_map<ServerID, VPackSlice>>
_currentDatabases; // from Current/Databases

View File

@ -706,6 +706,8 @@ static void JS_GetCollectionInfoCurrentClusterInfo(
ClusterInfo::instance()->getCollectionCurrent(TRI_ObjectToString(args[0]),
cid);
result->Set(TRI_V8_ASCII_STRING("currentVersion"),
v8::Number::New(isolate, (double) cic->getCurrentVersion()));
result->Set(TRI_V8_ASCII_STRING("type"),
v8::Number::New(isolate, (int)ci->type()));

View File

@ -28,6 +28,7 @@
#include "Aql/Query.h"
#include "Aql/QueryRegistry.h"
#include "Cluster/ClusterComm.h"
#include "Cluster/ClusterHelpers.h"
#include "Cluster/ClusterMethods.h"
#include "Replication/InitialSyncer.h"
#include "RestServer/QueryRegistryFeature.h"
@ -476,7 +477,22 @@ void RestReplicationHandler::handleCommandClusterInventory() {
resultBuilder.add(VPackValue("collections"));
resultBuilder.openArray();
for (auto const& c : cols) {
c->toVelocyPackForClusterInventory(resultBuilder, includeSystem);
// We want to check if the collection is usable and all followers
// are in sync:
auto shardMap = c->shardIds();
// shardMap is an unordered_map from ShardId (string) to a vector of
// servers (strings), wrapped in a shared_ptr
auto cic = ci->getCollectionCurrent(dbName,
basics::StringUtils::itoa(c->cid()));
// Check all shards:
bool isReady = true;
for (auto const& p : *shardMap) {
auto currentServerList = cic->servers(p.first /* shardId */);
if (!ClusterHelpers::compareServerLists(p.second, currentServerList)) {
isReady = false;
}
}
c->toVelocyPackForClusterInventory(resultBuilder, includeSystem, isReady);
}
resultBuilder.close(); // collections
TRI_voc_tick_t tick = TRI_CurrentTickServer();

View File

@ -166,7 +166,8 @@ LogicalCollection::LogicalCollection(LogicalCollection const& other)
_keyOptions(other._keyOptions),
_keyGenerator(KeyGenerator::factory(VPackSlice(keyOptions()))),
_physical(other.getPhysical()->clone(this)),
_clusterEstimateTTL(0) {
_clusterEstimateTTL(0),
_planVersion(other._planVersion) {
TRI_ASSERT(_physical != nullptr);
if (ServerState::instance()->isDBServer() ||
!ServerState::instance()->isRunningInCluster()) {
@ -206,7 +207,8 @@ LogicalCollection::LogicalCollection(TRI_vocbase_t* vocbase,
_keyGenerator(),
_physical(
EngineSelectorFeature::ENGINE->createPhysicalCollection(this, info)),
_clusterEstimateTTL(0) {
_clusterEstimateTTL(0),
_planVersion(0) {
// add keyoptions from slice
TRI_ASSERT(info.isObject());
VPackSlice keyOpts = info.get("keyOptions");
@ -795,7 +797,8 @@ void LogicalCollection::setStatus(TRI_vocbase_col_status_e status) {
}
void LogicalCollection::toVelocyPackForClusterInventory(VPackBuilder& result,
bool useSystem) const {
bool useSystem,
bool isReady) const {
if (_isSystem && !useSystem) {
return;
}
@ -823,6 +826,8 @@ void LogicalCollection::toVelocyPackForClusterInventory(VPackBuilder& result,
result.add(VPackValue("indexes"));
getIndexesVPack(result, false, false);
result.add("planVersion", VPackValue(getPlanVersion()));
result.add("isReady", VPackValue(isReady));
result.close(); // CollectionInfo
}

View File

@ -255,7 +255,8 @@ class LogicalCollection {
bool forPersistence) const;
virtual void toVelocyPackForClusterInventory(velocypack::Builder&,
bool useSystem) const;
bool useSystem,
bool isReady) const;
inline TRI_vocbase_t* vocbase() const { return _vocbase; }
@ -346,6 +347,14 @@ class LogicalCollection {
// with the checksum provided in the reference checksum
Result compareChecksums(velocypack::Slice checksumSlice, std::string const& referenceChecksum) const;
// Set and get _planVersion, this is only used if the object is used in
// ClusterInfo to represent a cluster wide collection in the agency.
void setPlanVersion(uint64_t v) {
_planVersion = v;
}
uint64_t getPlanVersion() const {
return _planVersion;
}
private:
void prepareIndexes(velocypack::Slice indexesSlice);
@ -431,6 +440,12 @@ class LogicalCollection {
std::unordered_map<std::string, double> _clusterEstimates;
double _clusterEstimateTTL; //only valid if above vector is not empty
basics::ReadWriteLock _clusterEstimatesLock;
uint64_t _planVersion; // Only set if setPlanVersion was called. This only
// happens in ClusterInfo when this object is used
// to represent a cluster wide collection. This is
// then the version in the agency Plan that underpins
// the information in this object. Otherwise 0.
};
} // namespace arangodb