1
0
Fork 0

Try to sort out ClusterInfo protection for good.

This commit is contained in:
Max Neunhoeffer 2015-08-03 15:07:59 +02:00
parent e8b4e510d8
commit d0cfab6248
2 changed files with 373 additions and 222 deletions

View File

@ -264,12 +264,6 @@ ClusterInfo* ClusterInfo::instance () {
ClusterInfo::ClusterInfo () ClusterInfo::ClusterInfo ()
: _agency(), : _agency(),
_serversValid(false),
_DBServersValid(false),
_coordinatorsValid(false),
_plannedDatabases(),
_currentDatabases(),
_collectionsValid(false),
_uniqid() { _uniqid() {
_uniqid._currentValue = _uniqid._upperValue = 0ULL; _uniqid._currentValue = _uniqid._upperValue = 0ULL;
@ -282,8 +276,8 @@ ClusterInfo::ClusterInfo ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ClusterInfo::~ClusterInfo () { ClusterInfo::~ClusterInfo () {
clearPlannedDatabases(); clearPlannedDatabases(_plannedDatabases);
clearCurrentDatabases(); clearCurrentDatabases(_currentDatabases);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -329,21 +323,13 @@ uint64_t ClusterInfo::uniqid (uint64_t count) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ClusterInfo::flush () { void ClusterInfo::flush () {
WRITE_LOCKER(_lock); loadServers();
loadCurrentDBServers();
_collectionsValid = false; loadCurrentCoordinators();
_collectionsCurrentValid = false; loadPlannedDatabases();
_serversValid = false; loadCurrentDatabases();
_DBServersValid = false; loadPlannedCollections();
_coordinatorsValid = false; loadCurrentCollections();
_collections.clear();
_collectionsCurrent.clear();
_servers.clear();
_shardIds.clear();
clearPlannedDatabases();
clearCurrentDatabases();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -354,26 +340,38 @@ bool ClusterInfo::doesDatabaseExist (DatabaseID const& databaseID,
bool reload) { bool reload) {
int tries = 0; int tries = 0;
if (reload) { if (reload ||
! _plannedDatabasesProt.isValid ||
! _currentDatabasesProt.isValid ||
! _DBServersProt.isValid) {
loadPlannedDatabases(); loadPlannedDatabases();
loadCurrentDatabases(); loadCurrentDatabases();
loadCurrentDBServers(); loadCurrentDBServers();
++tries; ++tries; // no need to reload if the database is not found
} }
// From now on we know that all data has been valid once, so no need
// to check the isValid flags again under the lock.
while (true) { while (true) {
{ {
READ_LOCKER(_lock); size_t expectedSize;
const size_t expectedSize = _DBServers.size(); {
READ_LOCKER(_DBServersProt.lock);
expectedSize = _DBServers.size();
}
// look up database by name // look up database by name:
READ_LOCKER(_plannedDatabasesProt.lock);
// _plannedDatabases is a map-type<DatabaseID, TRI_json_t*> // _plannedDatabases is a map-type<DatabaseID, TRI_json_t*>
auto it = _plannedDatabases.find(databaseID); auto it = _plannedDatabases.find(databaseID);
if (it != _plannedDatabases.end()) { if (it != _plannedDatabases.end()) {
// found the database in Plan // found the database in Plan
// _currentDatabases is a map-type<ServerID, TRI_json_t*> READ_LOCKER(_currentDatabasesProt.lock);
// _currentDatabases is
// a map-type<DatabaseID, a map-type<ServerID, TRI_json_t*>>
auto it2 = _currentDatabases.find(databaseID); auto it2 = _currentDatabases.find(databaseID);
if (it2 != _currentDatabases.end()) { if (it2 != _currentDatabases.end()) {
@ -403,15 +401,27 @@ bool ClusterInfo::doesDatabaseExist (DatabaseID const& databaseID,
vector<DatabaseID> ClusterInfo::listDatabases (bool reload) { vector<DatabaseID> ClusterInfo::listDatabases (bool reload) {
vector<DatabaseID> result; vector<DatabaseID> result;
if (reload) { if (reload ||
! _plannedDatabasesProt.isValid ||
! _currentDatabasesProt.isValid ||
! _DBServersProt.isValid) {
loadPlannedDatabases(); loadPlannedDatabases();
loadCurrentDatabases(); loadCurrentDatabases();
loadCurrentDBServers(); loadCurrentDBServers();
} }
READ_LOCKER(_lock); // From now on we know that all data has been valid once, so no need
const size_t expectedSize = _DBServers.size(); // to check the isValid flags again under the lock.
size_t expectedSize;
{
READ_LOCKER(_DBServersProt.lock);
expectedSize = _DBServers.size();
}
{
READ_LOCKER(_plannedDatabasesProt.lock);
READ_LOCKER(_currentDatabasesProt.lock);
// _plannedDatabases is a map-type<DatabaseID, TRI_json_t*> // _plannedDatabases is a map-type<DatabaseID, TRI_json_t*>
auto it = _plannedDatabases.begin(); auto it = _plannedDatabases.begin();
@ -428,19 +438,20 @@ vector<DatabaseID> ClusterInfo::listDatabases (bool reload) {
++it; ++it;
} }
}
return result; return result;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief flushes the list of planned databases /// @brief actually clears a list of planned databases
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ClusterInfo::clearPlannedDatabases () { void ClusterInfo::clearPlannedDatabases (
// _plannedDatabases is a map-type<DatabaseID, TRI_json_t*> std::unordered_map<DatabaseID, TRI_json_t*>& databases) {
auto it = _plannedDatabases.begin();
while (it != _plannedDatabases.end()) { auto it = databases.begin();
while (it != databases.end()) {
TRI_json_t* json = (*it).second; TRI_json_t* json = (*it).second;
if (json != nullptr) { if (json != nullptr) {
@ -448,20 +459,79 @@ void ClusterInfo::clearPlannedDatabases () {
} }
++it; ++it;
} }
databases.clear();
_plannedDatabases.clear();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief flushes the list of current databases /// @brief (re-)load the information about planned databases
/// Usually one does not have to call this directly.
////////////////////////////////////////////////////////////////////////////////
//
static const std::string prefixPlannedDatabases = "Plan/Databases";
void ClusterInfo::loadPlannedDatabases () {
uint64_t storedVersion = _plannedDatabasesProt.version;
MUTEX_LOCKER(_plannedDatabasesProt.mutex);
if (_plannedDatabasesProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{
AgencyCommLocker locker("Plan", "READ");
if (locker.successful()) {
result = _agency.getValues(prefixPlannedDatabases, true);
}
}
if (result.successful()) {
result.parse(prefixPlannedDatabases + "/", false);
decltype(_plannedDatabases) newDatabases;
// result._values is a std::map<std::string, AgencyCommResultEntry>
auto it = result._values.begin();
while (it != result._values.end()) {
string const& name = (*it).first;
TRI_json_t* options = (*it).second._json;
// steal the json
(*it).second._json = nullptr;
newDatabases.insert(std::make_pair(name, options));
++it;
}
// Now set the new value:
{
WRITE_LOCKER(_plannedDatabasesProt.lock);
_plannedDatabases.swap(newDatabases);
_plannedDatabasesProt.version++; // such that others notice our change
_plannedDatabasesProt.isValid = true; // will never be reset to false
}
clearPlannedDatabases(newDatabases); // delete the old stuff
return;
}
LOG_TRACE("Error while loading %s", prefixPlannedDatabases.c_str());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief deletes a list of current databases
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void ClusterInfo::clearCurrentDatabases () { void ClusterInfo::clearCurrentDatabases (
// _currentDatabases is std::unordered_map<DatabaseID,
// a map-type<DatabaseID, a map-type<ServerID, TRI_json_t*>> std::unordered_map<ServerID, TRI_json_t*>>&
auto it = _currentDatabases.begin(); databases) {
while (it != _currentDatabases.end()) { auto it = databases.begin();
while (it != databases.end()) {
auto it2 = (*it).second.begin(); auto it2 = (*it).second.begin();
while (it2 != (*it).second.end()) { while (it2 != (*it).second.end()) {
@ -476,51 +546,7 @@ void ClusterInfo::clearCurrentDatabases () {
++it; ++it;
} }
_currentDatabases.clear(); databases.clear();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief (re-)load the information about planned databases
/// Usually one does not have to call this directly.
////////////////////////////////////////////////////////////////////////////////
//
static const std::string prefixPlannedDatabases = "Plan/Databases";
void ClusterInfo::loadPlannedDatabases () {
AgencyCommResult result;
{
AgencyCommLocker locker("Plan", "READ");
if (locker.successful()) {
result = _agency.getValues(prefixPlannedDatabases, true);
}
}
if (result.successful()) {
result.parse(prefixPlannedDatabases + "/", false);
WRITE_LOCKER(_lock);
clearPlannedDatabases();
// result._values is a std::map<std::string, AgencyCommResultEntry>
auto it = result._values.begin();
while (it != result._values.end()) {
string const& name = (*it).first;
TRI_json_t* options = (*it).second._json;
// steal the json
(*it).second._json = nullptr;
_plannedDatabases.insert(std::make_pair(name, options));
++it;
}
return;
}
LOG_TRACE("Error while loading %s", prefixPlannedDatabases.c_str());
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -529,10 +555,18 @@ void ClusterInfo::loadPlannedDatabases () {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static const std::string prefixCurrentDatabases = "Current/Databases"; static const std::string prefixCurrentDatabases = "Current/Databases";
void ClusterInfo::loadCurrentDatabases () { void ClusterInfo::loadCurrentDatabases () {
AgencyCommResult result; uint64_t storedVersion = _currentDatabasesProt.version;
MUTEX_LOCKER(_currentDatabasesProt.mutex);
if (_currentDatabasesProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{ {
AgencyCommLocker locker("Plan", "READ"); AgencyCommLocker locker("Plan", "READ");
@ -544,8 +578,7 @@ void ClusterInfo::loadCurrentDatabases () {
if (result.successful()) { if (result.successful()) {
result.parse(prefixCurrentDatabases + "/", false); result.parse(prefixCurrentDatabases + "/", false);
WRITE_LOCKER(_lock); decltype(_currentDatabases) newDatabases;
clearCurrentDatabases();
std::map<std::string, AgencyCommResultEntry>::iterator it = result._values.begin(); std::map<std::string, AgencyCommResultEntry>::iterator it = result._values.begin();
@ -563,12 +596,12 @@ void ClusterInfo::loadCurrentDatabases () {
// _currentDatabases is // _currentDatabases is
// a map-type<DatabaseID, a map-type<ServerID, TRI_json_t*>> // a map-type<DatabaseID, a map-type<ServerID, TRI_json_t*>>
auto it2 = _currentDatabases.find(database); auto it2 = newDatabases.find(database);
if (it2 == _currentDatabases.end()) { if (it2 == newDatabases.end()) {
// insert an empty list for this database // insert an empty list for this database
decltype(it2->second) empty; decltype(it2->second) empty;
it2 = _currentDatabases.insert(std::make_pair(database, empty)).first; it2 = newDatabases.insert(std::make_pair(database, empty)).first;
} }
if (parts.size() == 2) { if (parts.size() == 2) {
@ -582,6 +615,14 @@ void ClusterInfo::loadCurrentDatabases () {
++it; ++it;
} }
// Now set the new value:
{
WRITE_LOCKER(_currentDatabasesProt.lock);
_currentDatabases.swap(newDatabases);
_currentDatabasesProt.version++; // such that others notice our change
_currentDatabasesProt.isValid = true; // will never be reset to false
}
clearCurrentDatabases(newDatabases); // delete the old stuff
return; return;
} }
@ -594,10 +635,18 @@ void ClusterInfo::loadCurrentDatabases () {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static const std::string prefixPlannedCollections = "Plan/Collections"; static const std::string prefixPlannedCollections = "Plan/Collections";
void ClusterInfo::loadPlannedCollections (bool acquireLock) { void ClusterInfo::loadPlannedCollections (bool acquireLock) {
AgencyCommResult result; uint64_t storedVersion = _plannedCollectionsProt.version;
MUTEX_LOCKER(_plannedCollectionsProt.mutex);
if (_plannedCollectionsProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{ {
if (acquireLock) { if (acquireLock) {
AgencyCommLocker locker("Plan", "READ"); AgencyCommLocker locker("Plan", "READ");
@ -614,9 +663,9 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) {
if (result.successful()) { if (result.successful()) {
result.parse(prefixPlannedCollections + "/", false); result.parse(prefixPlannedCollections + "/", false);
WRITE_LOCKER(_lock); decltype(_plannedCollections) newCollections;
_collections.clear(); decltype(_shards) newShards;
_shards.clear(); decltype(_shardKeys) newShardKeys;
std::map<std::string, AgencyCommResultEntry>::iterator it = result._values.begin(); std::map<std::string, AgencyCommResultEntry>::iterator it = result._values.begin();
@ -636,13 +685,13 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) {
const std::string collection = parts[1]; const std::string collection = parts[1];
// check whether we have created an entry for the database already // check whether we have created an entry for the database already
AllCollections::iterator it2 = _collections.find(database); AllCollections::iterator it2 = newCollections.find(database);
if (it2 == _collections.end()) { if (it2 == newCollections.end()) {
// not yet, so create an entry for the database // not yet, so create an entry for the database
DatabaseCollections empty; DatabaseCollections empty;
_collections.emplace(std::make_pair(database, empty)); newCollections.emplace(std::make_pair(database, empty));
it2 = _collections.find(database); it2 = newCollections.find(database);
} }
TRI_json_t* json = (*it).second._json; TRI_json_t* json = (*it).second._json;
@ -652,7 +701,7 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) {
shared_ptr<CollectionInfo> collectionData (new CollectionInfo(json)); shared_ptr<CollectionInfo> collectionData (new CollectionInfo(json));
vector<string>* shardKeys = new vector<string>; vector<string>* shardKeys = new vector<string>;
*shardKeys = collectionData->shardKeys(); *shardKeys = collectionData->shardKeys();
_shardKeys.insert( newShardKeys.insert(
make_pair(collection, shared_ptr<vector<string> > (shardKeys))); make_pair(collection, shared_ptr<vector<string> > (shardKeys)));
map<ShardID, ServerID> shardIDs = collectionData->shardIds(); map<ShardID, ServerID> shardIDs = collectionData->shardIds();
vector<string>* shards = new vector<string>; vector<string>* shards = new vector<string>;
@ -660,7 +709,7 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) {
for (it3 = shardIDs.begin(); it3 != shardIDs.end(); ++it3) { for (it3 = shardIDs.begin(); it3 != shardIDs.end(); ++it3) {
shards->push_back(it3->first); shards->push_back(it3->first);
} }
_shards.emplace( newShards.emplace(
std::make_pair(collection, shared_ptr<vector<string> >(shards))); std::make_pair(collection, shared_ptr<vector<string> >(shards)));
// insert the collection into the existing map, insert it under its // insert the collection into the existing map, insert it under its
@ -672,12 +721,20 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) {
collectionData)); collectionData));
} }
_collectionsValid = true;
// Now set the new value:
{
WRITE_LOCKER(_plannedCollectionsProt.lock);
_plannedCollections.swap(newCollections);
_shards.swap(newShards);
_shardKeys.swap(newShardKeys);
_plannedCollectionsProt.version++; // such that others notice our change
_plannedCollectionsProt.isValid = true; // will never be reset to false
}
return; return;
} }
LOG_TRACE("Error while loading %s", prefixPlannedCollections.c_str()); LOG_TRACE("Error while loading %s", prefixPlannedCollections.c_str());
_collectionsValid = false;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -690,18 +747,18 @@ shared_ptr<CollectionInfo> ClusterInfo::getCollection
CollectionID const& collectionID) { CollectionID const& collectionID) {
int tries = 0; int tries = 0;
if (! _collectionsValid) { if (! _plannedCollectionsProt.isValid) {
loadPlannedCollections(true); loadPlannedCollections(true);
++tries; ++tries;
} }
while (true) { // left by break while (true) { // left by break
{ {
READ_LOCKER(_lock); READ_LOCKER(_plannedCollectionsProt.lock);
// look up database by id // look up database by id
AllCollections::const_iterator it = _collections.find(databaseID); AllCollections::const_iterator it = _plannedCollections.find(databaseID);
if (it != _collections.end()) { if (it != _plannedCollections.end()) {
// look up collection by id (or by name) // look up collection by id (or by name)
DatabaseCollections::const_iterator it2 = (*it).second.find(collectionID); DatabaseCollections::const_iterator it2 = (*it).second.find(collectionID);
@ -773,11 +830,11 @@ const std::vector<shared_ptr<CollectionInfo> > ClusterInfo::getCollections
// always reload // always reload
loadPlannedCollections(true); loadPlannedCollections(true);
READ_LOCKER(_lock); READ_LOCKER(_plannedCollectionsProt.lock);
// look up database by id // look up database by id
AllCollections::const_iterator it = _collections.find(databaseID); AllCollections::const_iterator it = _plannedCollections.find(databaseID);
if (it == _collections.end()) { if (it == _plannedCollections.end()) {
return result; return result;
} }
@ -807,8 +864,15 @@ const std::vector<shared_ptr<CollectionInfo> > ClusterInfo::getCollections
static const std::string prefixCurrentCollections = "Current/Collections"; static const std::string prefixCurrentCollections = "Current/Collections";
void ClusterInfo::loadCurrentCollections (bool acquireLock) { void ClusterInfo::loadCurrentCollections (bool acquireLock) {
AgencyCommResult result; uint64_t storedVersion = _currentCollectionsProt.version;
MUTEX_LOCKER(_currentCollectionsProt.mutex);
if (_currentCollectionsProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{ {
if (acquireLock) { if (acquireLock) {
AgencyCommLocker locker("Current", "READ"); AgencyCommLocker locker("Current", "READ");
@ -825,9 +889,8 @@ void ClusterInfo::loadCurrentCollections (bool acquireLock) {
if (result.successful()) { if (result.successful()) {
result.parse(prefixCurrentCollections + "/", false); result.parse(prefixCurrentCollections + "/", false);
WRITE_LOCKER(_lock); decltype(_currentCollections) newCollections;
_collectionsCurrent.clear(); decltype(_shardIds) newShardIds;
_shardIds.clear();
std::map<std::string, AgencyCommResultEntry>::iterator it = result._values.begin(); std::map<std::string, AgencyCommResultEntry>::iterator it = result._values.begin();
@ -849,13 +912,13 @@ void ClusterInfo::loadCurrentCollections (bool acquireLock) {
const std::string shardID = parts[2]; const std::string shardID = parts[2];
// check whether we have created an entry for the database already // check whether we have created an entry for the database already
AllCollectionsCurrent::iterator it2 = _collectionsCurrent.find(database); AllCollectionsCurrent::iterator it2 = newCollections.find(database);
if (it2 == _collectionsCurrent.end()) { if (it2 == newCollections.end()) {
// not yet, so create an entry for the database // not yet, so create an entry for the database
DatabaseCollectionsCurrent empty; DatabaseCollectionsCurrent empty;
_collectionsCurrent.insert(std::make_pair(database, empty)); newCollections.insert(std::make_pair(database, empty));
it2 = _collectionsCurrent.find(database); it2 = newCollections.find(database);
} }
TRI_json_t* json = (*it).second._json; TRI_json_t* json = (*it).second._json;
@ -886,15 +949,22 @@ void ClusterInfo::loadCurrentCollections (bool acquireLock) {
std::string DBserver = triagens::basics::JsonHelper::getStringValue std::string DBserver = triagens::basics::JsonHelper::getStringValue
(json, "DBServer", ""); (json, "DBServer", "");
if (DBserver != "") { if (DBserver != "") {
_shardIds.insert(make_pair(shardID, DBserver)); newShardIds.insert(make_pair(shardID, DBserver));
} }
} }
_collectionsCurrentValid = true;
// Now set the new value:
{
WRITE_LOCKER(_currentCollectionsProt.lock);
_currentCollections.swap(newCollections);
_shardIds.swap(newShardIds);
_currentCollectionsProt.version++; // such that others notice our change
_currentCollectionsProt.isValid = true; // will never be reset to false
}
return; return;
} }
LOG_TRACE("Error while loading %s", prefixCurrentCollections.c_str()); LOG_TRACE("Error while loading %s", prefixCurrentCollections.c_str());
_collectionsCurrentValid = false;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -908,18 +978,18 @@ shared_ptr<CollectionInfoCurrent> ClusterInfo::getCollectionCurrent
CollectionID const& collectionID) { CollectionID const& collectionID) {
int tries = 0; int tries = 0;
if (! _collectionsCurrentValid) { if (! _currentCollectionsProt.isValid) {
loadCurrentCollections(true); loadCurrentCollections(true);
++tries; ++tries;
} }
while (true) { while (true) {
{ {
READ_LOCKER(_lock); READ_LOCKER(_currentCollectionsProt.lock);
// look up database by id // look up database by id
AllCollectionsCurrent::const_iterator it = _collectionsCurrent.find(databaseID); AllCollectionsCurrent::const_iterator it = _currentCollections.find(databaseID);
if (it != _collectionsCurrent.end()) { if (it != _currentCollections.end()) {
// look up collection by id // look up collection by id
DatabaseCollectionsCurrent::const_iterator it2 = (*it).second.find(collectionID); DatabaseCollectionsCurrent::const_iterator it2 = (*it).second.find(collectionID);
@ -976,6 +1046,9 @@ int ClusterInfo::createDatabaseCoordinator (string const& name,
} }
} }
// Now update our own cache of planned databases:
loadPlannedDatabases();
// Now wait for it to appear and be complete: // Now wait for it to appear and be complete:
res.clear(); res.clear();
res = ac.getValues("Current/Version", false); res = ac.getValues("Current/Version", false);
@ -1022,6 +1095,7 @@ int ClusterInfo::createDatabaseCoordinator (string const& name,
errorMsg = "Error in creation of database:" + tmpMsg; errorMsg = "Error in creation of database:" + tmpMsg;
return TRI_ERROR_CLUSTER_COULD_NOT_CREATE_DATABASE; return TRI_ERROR_CLUSTER_COULD_NOT_CREATE_DATABASE;
} }
loadCurrentDatabases(); // update our cache
return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg); return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg);
} }
} }
@ -1087,7 +1161,9 @@ int ClusterInfo::dropDatabaseCoordinator (string const& name, string& errorMsg,
} }
} }
_collectionsValid = false; // Load our own caches:
loadPlannedDatabases();
loadPlannedCollections(true);
// Now wait for it to appear and be complete: // Now wait for it to appear and be complete:
res.clear(); res.clear();
@ -1153,9 +1229,9 @@ int ClusterInfo::createCollectionCoordinator (string const& databaseName,
// check if a collection with the same name is already planned // check if a collection with the same name is already planned
loadPlannedCollections(false); loadPlannedCollections(false);
READ_LOCKER(_lock); READ_LOCKER(_plannedCollectionsProt.lock);
AllCollections::const_iterator it = _collections.find(databaseName); AllCollections::const_iterator it = _plannedCollections.find(databaseName);
if (it != _collections.end()) { if (it != _plannedCollections.end()) {
const std::string name = JsonHelper::getStringValue(json, "name", ""); const std::string name = JsonHelper::getStringValue(json, "name", "");
DatabaseCollections::const_iterator it2 = (*it).second.find(name); DatabaseCollections::const_iterator it2 = (*it).second.find(name);
@ -1184,6 +1260,9 @@ int ClusterInfo::createCollectionCoordinator (string const& databaseName,
} }
} }
// Update our cache:
loadPlannedCollections();
// Now wait for it to appear and be complete: // Now wait for it to appear and be complete:
AgencyCommResult res = ac.getValues("Current/Version", false); AgencyCommResult res = ac.getValues("Current/Version", false);
if (!res.successful()) { if (!res.successful()) {
@ -1226,6 +1305,7 @@ int ClusterInfo::createCollectionCoordinator (string const& databaseName,
errorMsg = "Error in creation of collection:" + tmpMsg; errorMsg = "Error in creation of collection:" + tmpMsg;
return TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION; return TRI_ERROR_CLUSTER_COULD_NOT_CREATE_COLLECTION;
} }
loadPlannedCollections();
return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg); return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg);
} }
} }
@ -1276,7 +1356,8 @@ int ClusterInfo::dropCollectionCoordinator (string const& databaseName,
} }
} }
flush(); // Update our own cache:
loadPlannedCollections(true);
// Now wait for it to appear and be complete: // Now wait for it to appear and be complete:
res.clear(); res.clear();
@ -1307,6 +1388,7 @@ int ClusterInfo::dropCollectionCoordinator (string const& databaseName,
return setErrormsg( return setErrormsg(
TRI_ERROR_CLUSTER_COULD_NOT_REMOVE_COLLECTION_IN_CURRENT, errorMsg); TRI_ERROR_CLUSTER_COULD_NOT_REMOVE_COLLECTION_IN_CURRENT, errorMsg);
} }
loadCurrentCollections();
return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg); return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg);
} }
} }
@ -1499,10 +1581,15 @@ int ClusterInfo::ensureIndexCoordinator (string const& databaseName,
{ {
loadPlannedCollections(false); loadPlannedCollections(false);
READ_LOCKER(_lock);
shared_ptr<CollectionInfo> c = getCollection(databaseName, collectionID); shared_ptr<CollectionInfo> c = getCollection(databaseName, collectionID);
// Note that nobody is removing this collection in the plan, since
// we hold the write lock in the agency, therefore it does not matter
// that getCollection fetches the read lock and releases it before
// we get it again.
//
READ_LOCKER(_plannedCollectionsProt.lock);
if (c->empty()) { if (c->empty()) {
return setErrormsg(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, errorMsg); return setErrormsg(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, errorMsg);
} }
@ -1604,8 +1691,8 @@ int ClusterInfo::ensureIndexCoordinator (string const& databaseName,
} }
} }
// wipe cache // reload our own cache:
flush(); loadPlannedCollections(true);
TRI_ASSERT(numberOfShards > 0); TRI_ASSERT(numberOfShards > 0);
@ -1672,6 +1759,8 @@ int ClusterInfo::ensureIndexCoordinator (string const& databaseName,
resultJson = newIndex; resultJson = newIndex;
TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, resultJson, "isNewlyCreated", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, true)); TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, resultJson, "isNewlyCreated", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, true));
loadCurrentCollections();
return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg); return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg);
} }
} }
@ -1717,10 +1806,10 @@ int ClusterInfo::dropIndexCoordinator (string const& databaseName,
{ {
loadPlannedCollections(false); loadPlannedCollections(false);
READ_LOCKER(_lock);
shared_ptr<CollectionInfo> c = getCollection(databaseName, collectionID); shared_ptr<CollectionInfo> c = getCollection(databaseName, collectionID);
READ_LOCKER(_plannedCollectionsProt.lock);
if (c->empty()) { if (c->empty()) {
return setErrormsg(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, errorMsg); return setErrormsg(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, errorMsg);
} }
@ -1804,8 +1893,8 @@ int ClusterInfo::dropIndexCoordinator (string const& databaseName,
} }
} }
// wipe cache // load our own cache:
flush(); loadPlannedCollections();
TRI_ASSERT(numberOfShards > 0); TRI_ASSERT(numberOfShards > 0);
@ -1851,6 +1940,7 @@ int ClusterInfo::dropIndexCoordinator (string const& databaseName,
} }
if (! found) { if (! found) {
loadCurrentCollections();
return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg); return setErrormsg(TRI_ERROR_NO_ERROR, errorMsg);
} }
} }
@ -1870,10 +1960,18 @@ int ClusterInfo::dropIndexCoordinator (string const& databaseName,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static const std::string prefixServers = "Current/ServersRegistered"; static const std::string prefixServers = "Current/ServersRegistered";
void ClusterInfo::loadServers () { void ClusterInfo::loadServers () {
AgencyCommResult result; uint64_t storedVersion = _serversProt.version;
MUTEX_LOCKER(_serversProt.mutex);
if (_serversProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{ {
AgencyCommLocker locker("Current", "READ"); AgencyCommLocker locker("Current", "READ");
@ -1885,8 +1983,7 @@ void ClusterInfo::loadServers () {
if (result.successful()) { if (result.successful()) {
result.parse(prefixServers + "/", false); result.parse(prefixServers + "/", false);
WRITE_LOCKER(_lock); decltype(_servers) newServers;
_servers.clear();
std::map<std::string, AgencyCommResultEntry>::const_iterator it = result._values.begin(); std::map<std::string, AgencyCommResultEntry>::const_iterator it = result._values.begin();
@ -1897,21 +1994,22 @@ void ClusterInfo::loadServers () {
if (nullptr != sub) { if (nullptr != sub) {
std::string server = triagens::basics::JsonHelper::getStringValue(sub, ""); std::string server = triagens::basics::JsonHelper::getStringValue(sub, "");
_servers.emplace(std::make_pair((*it).first, server)); newServers.emplace(std::make_pair((*it).first, server));
} }
++it; ++it;
} }
_serversValid = true; // Now set the new value:
{
WRITE_LOCKER(_serversProt.lock);
_servers.swap(newServers);
_serversProt.version++; // such that others notice our change
_serversProt.isValid = true; // will never be reset to false
}
return; return;
} }
LOG_TRACE("Error while loading %s", prefixServers.c_str()); LOG_TRACE("Error while loading %s", prefixServers.c_str());
_serversValid = false;
return;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1923,14 +2021,14 @@ void ClusterInfo::loadServers () {
std::string ClusterInfo::getServerEndpoint (ServerID const& serverID) { std::string ClusterInfo::getServerEndpoint (ServerID const& serverID) {
int tries = 0; int tries = 0;
if (! _serversValid) { if (! _serversProt.isValid) {
loadServers(); loadServers();
tries++; tries++;
} }
while (true) { while (true) {
{ {
READ_LOCKER(_lock); READ_LOCKER(_serversProt.lock);
// _servers is a map-type <ServerId, string> // _servers is a map-type <ServerId, string>
auto it = _servers.find(serverID); auto it = _servers.find(serverID);
@ -1959,14 +2057,14 @@ std::string ClusterInfo::getServerEndpoint (ServerID const& serverID) {
std::string ClusterInfo::getServerName (std::string const& endpoint) { std::string ClusterInfo::getServerName (std::string const& endpoint) {
int tries = 0; int tries = 0;
if (! _serversValid) { if (! _serversProt.isValid) {
loadServers(); loadServers();
tries++; tries++;
} }
while (true) { while (true) {
{ {
READ_LOCKER(_lock); READ_LOCKER(_serversProt.lock);
for (auto const& it : _servers) { for (auto const& it : _servers) {
if (it.second == endpoint) { if (it.second == endpoint) {
return it.first; return it.first;
@ -1994,8 +2092,15 @@ static const std::string prefixCurrentCoordinators = "Current/Coordinators";
void ClusterInfo::loadCurrentCoordinators () { void ClusterInfo::loadCurrentCoordinators () {
AgencyCommResult result; uint64_t storedVersion = _coordinatorsProt.version;
MUTEX_LOCKER(_coordinatorsProt.mutex);
if (_coordinatorsProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{ {
AgencyCommLocker locker("Current", "READ"); AgencyCommLocker locker("Current", "READ");
@ -2007,23 +2112,25 @@ void ClusterInfo::loadCurrentCoordinators () {
if (result.successful()) { if (result.successful()) {
result.parse(prefixCurrentCoordinators + "/", false); result.parse(prefixCurrentCoordinators + "/", false);
WRITE_LOCKER(_lock); decltype(_coordinators) newCoordinators;
_coordinators.clear();
std::map<std::string, AgencyCommResultEntry>::const_iterator it = result._values.begin(); std::map<std::string, AgencyCommResultEntry>::const_iterator it = result._values.begin();
for (; it != result._values.end(); ++it) { for (; it != result._values.end(); ++it) {
_coordinators.emplace(std::make_pair((*it).first, triagens::basics::JsonHelper::getStringValue((*it).second._json, ""))); newCoordinators.emplace(std::make_pair((*it).first, triagens::basics::JsonHelper::getStringValue((*it).second._json, "")));
} }
_coordinatorsValid = true; // Now set the new value:
{
WRITE_LOCKER(_coordinatorsProt.lock);
_coordinators.swap(newCoordinators);
_coordinatorsProt.version++; // such that others notice our change
_coordinatorsProt.isValid = true; // will never be reset to false
}
return; return;
} }
LOG_TRACE("Error while loading %s", prefixCurrentCoordinators.c_str()); LOG_TRACE("Error while loading %s", prefixCurrentCoordinators.c_str());
_coordinatorsValid = false;
return; return;
} }
@ -2036,8 +2143,15 @@ static const std::string prefixCurrentDBServers = "Current/DBServers";
void ClusterInfo::loadCurrentDBServers () { void ClusterInfo::loadCurrentDBServers () {
AgencyCommResult result; uint64_t storedVersion = _DBServersProt.version;
MUTEX_LOCKER(_DBServersProt.mutex);
if (_DBServersProt.version > storedVersion) {
// Somebody else did, what we intended to do, so just return
return;
}
// Now contact the agency:
AgencyCommResult result;
{ {
AgencyCommLocker locker("Current", "READ"); AgencyCommLocker locker("Current", "READ");
@ -2049,23 +2163,25 @@ void ClusterInfo::loadCurrentDBServers () {
if (result.successful()) { if (result.successful()) {
result.parse(prefixCurrentDBServers + "/", false); result.parse(prefixCurrentDBServers + "/", false);
WRITE_LOCKER(_lock); decltype(_DBServers) newDBServers;
_DBServers.clear();
std::map<std::string, AgencyCommResultEntry>::const_iterator it = result._values.begin(); std::map<std::string, AgencyCommResultEntry>::const_iterator it = result._values.begin();
for (; it != result._values.end(); ++it) { for (; it != result._values.end(); ++it) {
_DBServers.emplace(std::make_pair((*it).first, triagens::basics::JsonHelper::getStringValue((*it).second._json, ""))); newDBServers.emplace(std::make_pair((*it).first, triagens::basics::JsonHelper::getStringValue((*it).second._json, "")));
} }
_DBServersValid = true; // Now set the new value:
{
WRITE_LOCKER(_DBServersProt.lock);
newDBServers.swap(newDBServers);
_DBServersProt.version++; // such that others notice our change
_DBServersProt.isValid = true; // will never be reset to false
}
return; return;
} }
LOG_TRACE("Error while loading %s", prefixCurrentDBServers.c_str()); LOG_TRACE("Error while loading %s", prefixCurrentDBServers.c_str());
_DBServersValid = false;
return; return;
} }
@ -2076,14 +2192,17 @@ void ClusterInfo::loadCurrentDBServers () {
std::vector<ServerID> ClusterInfo::getCurrentDBServers () { std::vector<ServerID> ClusterInfo::getCurrentDBServers () {
std::vector<ServerID> result; std::vector<ServerID> result;
int tries = 0; int tries = 0;
if (! _DBServersProt.isValid) {
loadCurrentDBServers();
tries++;
}
while (true) { while (true) {
{ {
// return a consistent state of servers // return a consistent state of servers
READ_LOCKER(_lock); READ_LOCKER(_DBServersProt.lock);
if (_DBServersValid) {
result.reserve(_DBServers.size()); result.reserve(_DBServers.size());
for (auto& it : _DBServers) { for (auto& it : _DBServers) {
@ -2092,7 +2211,6 @@ std::vector<ServerID> ClusterInfo::getCurrentDBServers () {
return result; return result;
} }
}
if (++tries >= 2) { if (++tries >= 2) {
break; break;
@ -2150,14 +2268,14 @@ std::string ClusterInfo::getTargetServerEndpoint (ServerID const& serverID) {
ServerID ClusterInfo::getResponsibleServer (ShardID const& shardID) { ServerID ClusterInfo::getResponsibleServer (ShardID const& shardID) {
int tries = 0; int tries = 0;
if (! _collectionsCurrentValid) { if (! _currentCollectionsProt.isValid) {
loadCurrentCollections(true); loadCurrentCollections(true);
tries++; tries++;
} }
while (true) { while (true) {
{ {
READ_LOCKER(_lock); READ_LOCKER(_currentCollectionsProt.lock);
// _shardIds is a map-type <ShardId, ServerId> // _shardIds is a map-type <ShardId, ServerId>
auto it = _shardIds.find(shardID); auto it = _shardIds.find(shardID);
@ -2203,7 +2321,7 @@ int ClusterInfo::getResponsibleShard (CollectionID const& collectionID,
// Note that currently we take the number of shards and the shardKeys // Note that currently we take the number of shards and the shardKeys
// from Plan, since they are immutable. Later we will have to switch // from Plan, since they are immutable. Later we will have to switch
// this to Current, when we allow to add and remove shards. // this to Current, when we allow to add and remove shards.
if (! _collectionsValid) { if (! _plannedCollectionsProt.isValid) {
loadPlannedCollections(); loadPlannedCollections();
} }
@ -2216,7 +2334,7 @@ int ClusterInfo::getResponsibleShard (CollectionID const& collectionID,
while (true) { while (true) {
{ {
// Get the sharding keys and the number of shards: // Get the sharding keys and the number of shards:
READ_LOCKER(_lock); READ_LOCKER(_plannedCollectionsProt.lock);
// _shards is a map-type <CollectionId, shared_ptr<vector<string>>> // _shards is a map-type <CollectionId, shared_ptr<vector<string>>>
auto it = _shards.find(collectionID); auto it = _shards.find(collectionID);
@ -2243,7 +2361,7 @@ int ClusterInfo::getResponsibleShard (CollectionID const& collectionID,
if (++tries >= 2) { if (++tries >= 2) {
break; break;
} }
loadPlannedCollections(); loadPlannedCollections(true);
} }
if (! found) { if (! found) {
@ -2272,14 +2390,17 @@ int ClusterInfo::getResponsibleShard (CollectionID const& collectionID,
std::vector<ServerID> ClusterInfo::getCurrentCoordinators () { std::vector<ServerID> ClusterInfo::getCurrentCoordinators () {
std::vector<ServerID> result; std::vector<ServerID> result;
int tries = 0; int tries = 0;
if (! _coordinatorsProt.isValid) {
loadCurrentCoordinators();
tries++;
}
while (true) { while (true) {
{ {
// return a consistent state of servers // return a consistent state of servers
READ_LOCKER(_lock); READ_LOCKER(_coordinatorsProt.lock);
if (_coordinatorsValid) {
result.reserve(_coordinators.size()); result.reserve(_coordinators.size());
for (auto& it : _coordinators) { for (auto& it : _coordinators) {
@ -2288,7 +2409,6 @@ std::vector<ServerID> ClusterInfo::getCurrentCoordinators () {
return result; return result;
} }
}
if (++tries >= 2) { if (++tries >= 2) {
break; break;

View File

@ -1010,16 +1010,20 @@ namespace triagens {
private: private:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief flushes the list of planned databases /// @brief actually clears a list of planned databases
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void clearPlannedDatabases (); void clearPlannedDatabases (
std::unordered_map<DatabaseID, TRI_json_t*>& databases);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief flushes the list of current databases /// @brief actually clears a list of current databases
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void clearCurrentDatabases (); void clearCurrentDatabases (
std::unordered_map<DatabaseID,
std::unordered_map<ServerID, TRI_json_t*>>&
databases);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief get an operation timeout /// @brief get an operation timeout
@ -1055,30 +1059,58 @@ namespace triagens {
private: private:
AgencyComm _agency; AgencyComm _agency;
triagens::basics::ReadWriteLock _lock;
// Cached data from the agency, we reload whenever necessary: // Cached data from the agency, we reload whenever necessary:
// We group the data, each group has an atomic "valid-flag"
// which is used for lazy loading in the beginning. It starts
// as false, is set to true at each reload and is never reset
// to false in the lifetime of the server. The variable is
// atomic to be able to check it without acquiring
// the read lock (see below). Flush is just an explicit reload
// for all data and is only used in tests.
// Furthermore, each group has a mutex that protects against
// simultaneously contacting the agency for an update.
// In addition, each group has an atomic version number, this is used
// to prevent a stampede if multiple threads notice concurrently
// that an update from the agency is necessary. Finally, there is
// a read/write lock which protects the actual data structure.
// We encapsulate this protection in the struct ProtectionData:
struct ProtectionData {
std::atomic<bool> isValid;
triagens::basics::Mutex mutex;
std::atomic<uint64_t> version;
triagens::basics::ReadWriteLock lock;
ProtectionData () : isValid(false), version(0) {
}
};
// The servers, first all, we only need Current here: // The servers, first all, we only need Current here:
std::unordered_map<ServerID, std::string> std::unordered_map<ServerID, std::string>
_servers; // from Current/ServersRegistered _servers; // from Current/ServersRegistered
bool ProtectionData _serversProt;
_serversValid;
// The DBServers, also from Current: // The DBServers, also from Current:
std::unordered_map<ServerID, ServerID> std::unordered_map<ServerID, ServerID>
_DBServers; // from Current/DBServers _DBServers; // from Current/DBServers
bool _DBServersValid; ProtectionData _DBServersProt;
// The Coordinators, also from Current: // The Coordinators, also from Current:
std::unordered_map<ServerID, ServerID> std::unordered_map<ServerID, ServerID>
_coordinators; // from Current/Coordinators _coordinators; // from Current/Coordinators
bool _coordinatorsValid; ProtectionData _coordinatorsProt;
// First the databases, there is Plan and Current information: // First the databases, there is Plan and Current information:
std::unordered_map<DatabaseID, struct TRI_json_t*> std::unordered_map<DatabaseID, struct TRI_json_t*>
_plannedDatabases; // from Plan/Databases _plannedDatabases; // from Plan/Databases
ProtectionData _plannedDatabasesProt;
std::unordered_map<DatabaseID, std::unordered_map<DatabaseID,
std::unordered_map<ServerID, struct TRI_json_t*>> std::unordered_map<ServerID, struct TRI_json_t*>>
_currentDatabases; // from Current/Databases _currentDatabases; // from Current/Databases
ProtectionData _currentDatabasesProt;
// Finally, we need information about collections, again we have // Finally, we need information about collections, again we have
// data from Plan and from Current. // data from Plan and from Current.
@ -1090,8 +1122,8 @@ namespace triagens {
// The Plan state: // The Plan state:
AllCollections AllCollections
_collections; // from Plan/Collections/ _plannedCollections; // from Plan/Collections/
bool _collectionsValid; ProtectionData _plannedCollectionsProt;
std::unordered_map<CollectionID, std::unordered_map<CollectionID,
std::shared_ptr<std::vector<std::string>>> std::shared_ptr<std::vector<std::string>>>
_shards; // from Plan/Collections/ _shards; // from Plan/Collections/
@ -1102,9 +1134,8 @@ namespace triagens {
// The Current state: // The Current state:
AllCollectionsCurrent AllCollectionsCurrent
_collectionsCurrent; // from Current/Collections/ _currentCollections; // from Current/Collections/
bool ProtectionData _currentCollectionsProt;
_collectionsCurrentValid;
std::unordered_map<ShardID, ServerID> std::unordered_map<ShardID, ServerID>
_shardIds; // from Current/Collections/ _shardIds; // from Current/Collections/