1
0
Fork 0
arangodb/arangod/Cluster/ServerState.cpp

1043 lines
33 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS 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 Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#include "ServerState.h"
#include "Basics/ReadLocker.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/WriteLocker.h"
#include "Logger/Logger.h"
#include "Cluster/AgencyComm.h"
#include "Cluster/ClusterInfo.h"
#include <iomanip>
#include <sstream>
using namespace arangodb;
using namespace arangodb::basics;
////////////////////////////////////////////////////////////////////////////////
/// @brief single instance of ServerState - will live as long as the server is
/// running
////////////////////////////////////////////////////////////////////////////////
static ServerState Instance;
ServerState::ServerState()
: _id(),
_dataPath(),
_logPath(),
_arangodPath(),
_dbserverConfig(),
_coordinatorConfig(),
_address(),
_lock(),
_role(),
_idOfPrimary(""),
_state(STATE_UNDEFINED),
_initialized(false),
_clusterEnabled(false),
_foxxmaster(""),
_foxxmasterQueueupdate(false) {
storeRole(ROLE_UNDEFINED);
}
ServerState::~ServerState() {}
////////////////////////////////////////////////////////////////////////////////
/// @brief create the (sole) instance
////////////////////////////////////////////////////////////////////////////////
ServerState* ServerState::instance() { return &Instance; }
////////////////////////////////////////////////////////////////////////////////
/// @brief get the string representation of a role
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::roleToString(ServerState::RoleEnum role) {
switch (role) {
case ROLE_UNDEFINED:
return "UNDEFINED";
case ROLE_SINGLE:
return "SINGLE";
case ROLE_PRIMARY:
return "PRIMARY";
case ROLE_SECONDARY:
return "SECONDARY";
case ROLE_COORDINATOR:
return "COORDINATOR";
case ROLE_AGENT:
return "AGENT";
}
TRI_ASSERT(false);
return "";
}
////////////////////////////////////////////////////////////////////////////////
/// @brief convert a string to a role
////////////////////////////////////////////////////////////////////////////////
ServerState::RoleEnum ServerState::stringToRole(std::string const& value) {
if (value == "SINGLE") {
return ROLE_SINGLE;
} else if (value == "PRIMARY") {
return ROLE_PRIMARY;
} else if (value == "SECONDARY") {
return ROLE_SECONDARY;
} else if (value == "COORDINATOR") {
return ROLE_COORDINATOR;
}
return ROLE_UNDEFINED;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief convert a string representation to a state
////////////////////////////////////////////////////////////////////////////////
ServerState::StateEnum ServerState::stringToState(std::string const& value) {
if (value == "SHUTDOWN") {
return STATE_SHUTDOWN;
}
// TODO MAX: do we need to understand other states, too?
return STATE_UNDEFINED;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the string representation of a state
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::stateToString(StateEnum state) {
// TODO MAX: cleanup
switch (state) {
case STATE_UNDEFINED:
return "UNDEFINED";
case STATE_STARTUP:
return "STARTUP";
case STATE_SERVINGASYNC:
return "SERVING";
case STATE_SERVINGSYNC:
return "SERVING";
case STATE_STOPPING:
return "STOPPING";
case STATE_STOPPED:
return "STOPPED";
case STATE_SYNCING:
return "SYNCING";
case STATE_INSYNC:
return "INSYNC";
case STATE_LOSTPRIMARY:
return "LOSTPRIMARY";
case STATE_SERVING:
return "SERVING";
case STATE_SHUTDOWN:
return "SHUTDOWN";
}
TRI_ASSERT(false);
return "";
}
////////////////////////////////////////////////////////////////////////////////
/// @brief find and set our role
////////////////////////////////////////////////////////////////////////////////
void ServerState::findAndSetRoleBlocking() {
while (true) {
auto role = determineRole(_localInfo, _id);
std::string roleString = roleToString(role);
LOG_TOPIC(DEBUG, Logger::CLUSTER) << "Found my role: " << roleString;
if (storeRole(role)) {
break;
}
sleep(1);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief flush the server state (used for testing)
////////////////////////////////////////////////////////////////////////////////
void ServerState::flush() {
findAndSetRoleBlocking();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the server role
////////////////////////////////////////////////////////////////////////////////
ServerState::RoleEnum ServerState::getRole() {
auto role = loadRole();
if (role != ServerState::ROLE_UNDEFINED || !_clusterEnabled) {
return role;
}
TRI_ASSERT(!_id.empty());
findAndSetRoleBlocking();
return loadRole();
}
bool ServerState::unregister() {
TRI_ASSERT(!getId().empty());
std::string const& id = getId();
std::string localInfoEncoded = StringUtils::urlEncode(_localInfo);
AgencyOperation deleteLocalIdMap(
"Target/MapLocalToID/" + localInfoEncoded,
AgencySimpleOperationType::DELETE_OP);
std::vector<AgencyOperation> operations = {deleteLocalIdMap};
auto role = loadRole();
const std::string agencyKey = roleToAgencyKey(role);
TRI_ASSERT(isClusterRole(role));
if (role == ROLE_COORDINATOR || role == ROLE_PRIMARY) {
operations.push_back(
AgencyOperation(
"Plan/" + agencyKey + "/" + id, AgencySimpleOperationType::DELETE_OP));
operations.push_back(
AgencyOperation(
"Current/" + agencyKey + "/"
+ id, AgencySimpleOperationType::DELETE_OP));
}
AgencyWriteTransaction unregisterTransaction(operations);
AgencyComm comm;
AgencyCommResult result;
result = comm.sendTransactionWithFailover(unregisterTransaction);
return result.successful();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief try to register with a role
////////////////////////////////////////////////////////////////////////////////
bool ServerState::registerWithRole(ServerState::RoleEnum role) {
if (!getId().empty()) {
LOG_TOPIC(INFO, Logger::CLUSTER)
<< "Registering with role and localinfo. Supplied id is being ignored";
return false;
}
AgencyComm comm;
AgencyCommResult result;
std::string localInfoEncoded = StringUtils::urlEncode(_localInfo);
result = comm.getValues("Target/MapLocalToID/" + localInfoEncoded);
std::string id;
bool found = true;
if (!result.successful()) {
found = false;
} else {
VPackSlice idSlice = result.slice()[0].get(std::vector<std::string>(
{comm.prefix(), "Target", "MapLocalToID", localInfoEncoded}));
if (!idSlice.isString()) {
found = false;
} else {
id = idSlice.copyString();
}
}
if (!found) {
LOG_TOPIC(DEBUG, Logger::CLUSTER)
<< "Determining id from localinfo failed."
<< "Continuing with registering ourselves for the first time";
id = createIdForRole(comm, role);
}
const std::string agencyKey = roleToAgencyKey(role);
const std::string planKey = "Plan/" + agencyKey + "/" + id;
const std::string currentKey = "Current/" + agencyKey + "/" + id;
auto builder = std::make_shared<VPackBuilder>();
result = comm.getValues(planKey);
found = true;
if (!result.successful()) {
found = false;
} else {
VPackSlice plan = result.slice()[0].get(std::vector<std::string>(
{ comm.prefix(), "Plan", agencyKey, id }));
if (!plan.isString()) {
found = false;
} else {
builder->add(plan);
}
}
if (!found) {
// mop: hmm ... we are registered but not part of the Plan :O
// create a plan for ourselves :)
builder->add(VPackValue("none"));
VPackSlice plan = builder->slice();
comm.setValue(planKey, plan, 0.0);
if (!result.successful()) {
LOG_TOPIC(ERR, Logger::CLUSTER)
<< "Couldn't create plan " << result.errorMessage();
return false;
}
}
result = comm.setValue(currentKey, builder->slice(), 0.0);
if (!result.successful()) {
LOG_TOPIC(ERR, Logger::CLUSTER)
<< "Could not talk to agency! " << result.errorMessage();
return false;
}
_id = id;
findAndSetRoleBlocking();
LOG_TOPIC(DEBUG, Logger::CLUSTER)
<< "We successfully announced ourselves as " << roleToString(role)
<< " and our id is " << id;
return true;
}
//////////////////////////////////////////////////////////////////////////////
/// @brief get the key for a role in the agency
//////////////////////////////////////////////////////////////////////////////
std::string ServerState::roleToAgencyKey(ServerState::RoleEnum role) {
switch (role) {
case ROLE_PRIMARY:
return "DBServers";
case ROLE_COORDINATOR:
return "Coordinators";
case ROLE_SECONDARY:
case ROLE_UNDEFINED:
case ROLE_SINGLE:
case ROLE_AGENT: {}
}
return "INVALID_CLUSTER_ROLE";
}
//////////////////////////////////////////////////////////////////////////////
/// @brief create an id for a specified role
//////////////////////////////////////////////////////////////////////////////
std::string ServerState::createIdForRole(
AgencyComm comm, ServerState::RoleEnum role) {
std::string const agencyKey = roleToAgencyKey(role);
std::string const serverIdPrefix = agencyKey.substr(0, agencyKey.length() - 1);
std::string id;
VPackBuilder builder;
builder.add(VPackValue("none"));
VPackSlice idValue = builder.slice();
AgencyCommResult createResult;
do {
AgencyCommResult result = comm.getValues("Plan/" + agencyKey);
if (!result.successful()) {
LOG(FATAL) << "Couldn't fetch Plan/" << agencyKey
<< " from agency. Agency is not initialized?";
FATAL_ERROR_EXIT();
}
VPackSlice servers = result.slice()[0].get(std::vector<std::string>(
{comm.prefix(), "Plan", agencyKey}));
if (!servers.isObject()) {
LOG(FATAL) << "Plan/" << agencyKey << " in agency is no object. "
<< "Agency not initialized?";
FATAL_ERROR_EXIT();
}
// mop: it is not our first run. wait a bit.
if (!id.empty()) {
sleep(1);
}
size_t idCounter = 1;
VPackSlice entry;
do {
std::ostringstream idss;
idss << std::setw(3) << std::setfill('0') << idCounter++;
id = serverIdPrefix + idss.str();
entry = servers.get(id);
LOG_TOPIC(TRACE, Logger::STARTUP) << id << " found in existing keys: "
<< (!entry.isNone());
} while (!entry.isNone());
createResult =
comm.casValue("Plan/" + agencyKey + "/" + id, idValue, false, 0.0, 0.0);
} while(!createResult.successful());
VPackBuilder localIdBuilder;
localIdBuilder.add(VPackValue(id));
VPackSlice localIdValue = localIdBuilder.slice();
AgencyCommResult mapResult = comm.setValue(
"Target/MapLocalToID/"
+ StringUtils::urlEncode(_localInfo), localIdValue, 0.0);
if (!mapResult.successful()) {
LOG(FATAL) << "Couldn't register Id as localId";
FATAL_ERROR_EXIT();
}
return id;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the server role
////////////////////////////////////////////////////////////////////////////////
void ServerState::setRole(ServerState::RoleEnum role) { storeRole(role); }
////////////////////////////////////////////////////////////////////////////////
/// @brief get the server local info
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getLocalInfo() {
READ_LOCKER(readLocker, _lock);
return _localInfo;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the server local info
////////////////////////////////////////////////////////////////////////////////
void ServerState::setLocalInfo(std::string const& localInfo) {
if (localInfo.empty()) {
return;
}
WRITE_LOCKER(writeLocker, _lock);
_localInfo = localInfo;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the server id
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getId() {
READ_LOCKER(readLocker, _lock);
return _id;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the server id
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getPrimaryId() {
READ_LOCKER(readLocker, _lock);
return _idOfPrimary;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the server id
////////////////////////////////////////////////////////////////////////////////
void ServerState::setId(std::string const& id) {
if (id.empty()) {
return;
}
WRITE_LOCKER(writeLocker, _lock);
_id = id;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the server description
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getDescription() {
READ_LOCKER(readLocker, _lock);
return _description;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the server description
////////////////////////////////////////////////////////////////////////////////
void ServerState::setDescription(std::string const& description) {
if (description.empty()) {
return;
}
WRITE_LOCKER(writeLocker, _lock);
_description = description;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the server address
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getAddress() {
READ_LOCKER(readLocker, _lock);
return _address;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the server address
////////////////////////////////////////////////////////////////////////////////
void ServerState::setAddress(std::string const& address) {
if (address.empty()) {
return;
}
WRITE_LOCKER(writeLocker, _lock);
_address = address;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the current state
////////////////////////////////////////////////////////////////////////////////
ServerState::StateEnum ServerState::getState() {
READ_LOCKER(readLocker, _lock);
return _state;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the current state
////////////////////////////////////////////////////////////////////////////////
void ServerState::setState(StateEnum state) {
bool result = false;
auto role = loadRole();
WRITE_LOCKER(writeLocker, _lock);
if (state == _state) {
return;
}
if (role == ROLE_PRIMARY) {
result = checkPrimaryState(state);
} else if (role == ROLE_SECONDARY) {
result = checkSecondaryState(state);
} else if (role == ROLE_COORDINATOR) {
result = checkCoordinatorState(state);
}
if (result) {
LOG_TOPIC(DEBUG, Logger::CLUSTER)
<< "changing state of " << ServerState::roleToString(role)
<< " server from " << ServerState::stateToString(_state)
<< " to " << ServerState::stateToString(state);
_state = state;
} else {
LOG_TOPIC(ERR, Logger::CLUSTER)
<< "invalid state transition for " << ServerState::roleToString(role)
<< " server from " << ServerState::stateToString(_state)
<< " to " << ServerState::stateToString(state);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the data path
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getDataPath() {
READ_LOCKER(readLocker, _lock);
return _dataPath;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the data path
////////////////////////////////////////////////////////////////////////////////
void ServerState::setDataPath(std::string const& value) {
WRITE_LOCKER(writeLocker, _lock);
_dataPath = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the log path
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getLogPath() {
READ_LOCKER(readLocker, _lock);
return _logPath;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the log path
////////////////////////////////////////////////////////////////////////////////
void ServerState::setLogPath(std::string const& value) {
WRITE_LOCKER(writeLocker, _lock);
_logPath = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the arangod path
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getArangodPath() {
READ_LOCKER(readLocker, _lock);
return _arangodPath;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the arangod path
////////////////////////////////////////////////////////////////////////////////
void ServerState::setArangodPath(std::string const& value) {
WRITE_LOCKER(writeLocker, _lock);
_arangodPath = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the JavaScript startup path
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getJavaScriptPath() {
READ_LOCKER(readLocker, _lock);
return _javaScriptStartupPath;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the arangod path
////////////////////////////////////////////////////////////////////////////////
void ServerState::setJavaScriptPath(std::string const& value) {
WRITE_LOCKER(writeLocker, _lock);
_javaScriptStartupPath = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the DBserver config
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getDBserverConfig() {
READ_LOCKER(readLocker, _lock);
return _dbserverConfig;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the DBserver config
////////////////////////////////////////////////////////////////////////////////
void ServerState::setDBserverConfig(std::string const& value) {
WRITE_LOCKER(writeLocker, _lock);
_dbserverConfig = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the coordinator config
////////////////////////////////////////////////////////////////////////////////
std::string ServerState::getCoordinatorConfig() {
READ_LOCKER(readLocker, _lock);
return _coordinatorConfig;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets the coordinator config
////////////////////////////////////////////////////////////////////////////////
void ServerState::setCoordinatorConfig(std::string const& value) {
WRITE_LOCKER(writeLocker, _lock);
_coordinatorConfig = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief redetermine the server role, we do this after a plan change.
/// This is needed for automatic failover. This calls determineRole with
/// previous values of _info and _id. In particular, the _id will usually
/// already be set. If the current role cannot be determined from the
/// agency or is not unique, then the system keeps the old role.
/// Returns true if there is a change and false otherwise.
////////////////////////////////////////////////////////////////////////////////
bool ServerState::redetermineRole() {
std::string saveIdOfPrimary = _idOfPrimary;
RoleEnum role = determineRole(_localInfo, _id);
std::string roleString = roleToString(role);
LOG_TOPIC(INFO, Logger::CLUSTER)
<< "Redetermined role from agency: " << roleString;
if (role == ServerState::ROLE_UNDEFINED) {
return false;
}
RoleEnum oldRole = loadRole();
if (role != oldRole) {
LOG_TOPIC(INFO, Logger::CLUSTER) << "Changed role to: " << roleString;
if (!storeRole(role)) {
return false;
}
return true;
}
if (_idOfPrimary != saveIdOfPrimary) {
LOG_TOPIC(INFO, Logger::CLUSTER) << "The ID of our primary has changed!";
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief determine the server role by fetching data from the agency
/// Note: this method must be called under the _lock
////////////////////////////////////////////////////////////////////////////////
ServerState::RoleEnum ServerState::determineRole(std::string const& info,
std::string& id) {
if (id.empty()) {
int res = lookupLocalInfoToId(info, id);
if (res != TRI_ERROR_NO_ERROR) {
LOG_TOPIC(ERR, Logger::CLUSTER) << "Could not lookupLocalInfoToId";
return ServerState::ROLE_UNDEFINED;
}
// When we get here, we have have successfully looked up our id
LOG_TOPIC(DEBUG, Logger::CLUSTER) << "Learned my own Id: " << id;
setId(id);
}
ServerState::RoleEnum role = checkCoordinatorsList(id);
if (role == ServerState::ROLE_UNDEFINED) {
role = checkServersList(id);
}
// mop: role might still be undefined
return role;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief validate a state transition for a primary server
////////////////////////////////////////////////////////////////////////////////
bool ServerState::checkPrimaryState(StateEnum state) {
if (state == STATE_STARTUP) {
// startup state can only be set once
return (_state == STATE_UNDEFINED);
} else if (state == STATE_SERVINGASYNC) {
return (_state == STATE_STARTUP || _state == STATE_STOPPED);
} else if (state == STATE_SERVINGSYNC) {
return (_state == STATE_STARTUP || _state == STATE_SERVINGASYNC ||
_state == STATE_STOPPED);
} else if (state == STATE_STOPPING) {
return (_state == STATE_SERVINGSYNC || _state == STATE_SERVINGASYNC);
} else if (state == STATE_STOPPED) {
return (_state == STATE_STOPPING);
} else if (state == STATE_SHUTDOWN) {
return (_state == STATE_STARTUP || _state == STATE_STOPPED ||
_state == STATE_SERVINGSYNC || _state == STATE_SERVINGASYNC);
}
// anything else is invalid
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief validate a state transition for a secondary server
////////////////////////////////////////////////////////////////////////////////
bool ServerState::checkSecondaryState(StateEnum state) {
if (state == STATE_STARTUP) {
// startup state can only be set once
return (_state == STATE_UNDEFINED);
} else if (state == STATE_SYNCING) {
return (_state == STATE_STARTUP || _state == STATE_LOSTPRIMARY);
} else if (state == STATE_INSYNC) {
return (_state == STATE_SYNCING);
} else if (state == STATE_LOSTPRIMARY) {
return (_state == STATE_SYNCING || _state == STATE_INSYNC);
} else if (state == STATE_SERVING) {
return (_state == STATE_STARTUP);
} else if (state == STATE_SHUTDOWN) {
return (_state == STATE_STARTUP || _state == STATE_SYNCING ||
_state == STATE_INSYNC || _state == STATE_LOSTPRIMARY);
}
// anything else is invalid
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief validate a state transition for a coordinator server
////////////////////////////////////////////////////////////////////////////////
bool ServerState::checkCoordinatorState(StateEnum state) {
if (state == STATE_STARTUP) {
// startup state can only be set once
return (_state == STATE_UNDEFINED);
} else if (state == STATE_SERVING) {
return (_state == STATE_STARTUP);
} else if (state == STATE_SHUTDOWN) {
return (_state == STATE_STARTUP || _state == STATE_SERVING);
}
// anything else is invalid
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief lookup the server role by scanning Plan/Coordinators for our id
////////////////////////////////////////////////////////////////////////////////
ServerState::RoleEnum ServerState::checkCoordinatorsList(
std::string const& id) {
// fetch value at Plan/Coordinators
// we need to do this to determine the server's role
std::string const key = "Plan/Coordinators";
AgencyComm comm;
AgencyCommResult result = comm.getValues(key);
if (!result.successful()) {
std::string const endpoints = AgencyComm::getEndpointsString();
LOG_TOPIC(TRACE, Logger::CLUSTER)
<< "Could not fetch configuration from agency endpoints ("
<< endpoints << "): got status code " << result._statusCode
<< ", message: " << result.errorMessage() << ", key: " << key;
return ServerState::ROLE_UNDEFINED;
}
VPackSlice coordinators = result.slice()[0].get(std::vector<std::string>(
{comm.prefix(), "Plan", "Coordinators"}));
if (!coordinators.isObject()) {
LOG_TOPIC(TRACE, Logger::CLUSTER)
<< "Got an invalid JSON response for Plan/Coordinators";
return ServerState::ROLE_UNDEFINED;
}
// check if we can find ourselves in the list returned by the agency
VPackSlice me = coordinators.get(id);
if (!me.isNone()) {
// we are in the list. this means we are a primary server
return ServerState::ROLE_COORDINATOR;
}
return ServerState::ROLE_UNDEFINED;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief lookup the server id by using the local info
////////////////////////////////////////////////////////////////////////////////
int ServerState::lookupLocalInfoToId(std::string const& localInfo,
std::string& id) {
// fetch value at Plan/DBServers
// we need to do this to determine the server's role
std::string const key = "Target/MapLocalToID";
int count = 0;
while (++count <= 600) {
AgencyComm comm;
AgencyCommResult result = comm.getValues(key);
if (!result.successful()) {
std::string const endpoints = AgencyComm::getEndpointsString();
LOG_TOPIC(DEBUG, Logger::STARTUP)
<< "Could not fetch configuration from agency endpoints ("
<< endpoints << "): got status code " << result._statusCode
<< ", message: " << result.errorMessage() << ", key: " << key;
} else {
VPackSlice slice = result.slice()[0].get(std::vector<std::string>(
{comm.prefix(), "Target", "MapLocalToID"}));
if (!slice.isObject()) {
LOG_TOPIC(DEBUG, Logger::STARTUP) << "Target/MapLocalToID corrupt: "
<< "no object.";
} else {
slice = slice.get(localInfo);
if (slice.isObject()) {
id = arangodb::basics::VelocyPackHelper::getStringValue(slice, "ID",
"");
if (id.empty()) {
LOG_TOPIC(ERR, Logger::STARTUP) << "ID not set!";
return TRI_ERROR_CLUSTER_COULD_NOT_DETERMINE_ID;
}
std::string description =
arangodb::basics::VelocyPackHelper::getStringValue(
slice, "Description", "");
if (!description.empty()) {
setDescription(description);
}
return TRI_ERROR_NO_ERROR;
}
}
}
sleep(1);
};
return TRI_ERROR_CLUSTER_COULD_NOT_DETERMINE_ID;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief lookup the server role by scanning Plan/DBServers for our id
////////////////////////////////////////////////////////////////////////////////
ServerState::RoleEnum ServerState::checkServersList(std::string const& id) {
// fetch value at Plan/DBServers
// we need to do this to determine the server's role
std::string const key = "Plan/DBServers";
AgencyComm comm;
AgencyCommResult result = comm.getValues(key);
if (!result.successful()) {
std::string const endpoints = AgencyComm::getEndpointsString();
LOG_TOPIC(TRACE, Logger::CLUSTER)
<< "Could not fetch configuration from agency endpoints (" << endpoints
<< "): got status code " << result._statusCode << ", message: "
<< result.errorMessage() << ", key: " << key;
return ServerState::ROLE_UNDEFINED;
}
ServerState::RoleEnum role = ServerState::ROLE_UNDEFINED;
VPackSlice dbservers = result.slice()[0].get(std::vector<std::string>(
{comm.prefix(), "Plan", "DBServers"}));
if (!dbservers.isObject()) {
LOG_TOPIC(TRACE, Logger::CLUSTER)
<< "Got an invalid JSON response for Plan/DBServers";
return ServerState::ROLE_UNDEFINED;
}
// check if we can find ourselves in the list returned by the agency
VPackSlice me = dbservers.get(id);
if (!me.isNone()) {
// we are in the list. this means we are a primary server
role = ServerState::ROLE_PRIMARY;
} else {
// check if we are a secondary...
for (auto const& s : VPackObjectIterator(dbservers)) {
VPackSlice slice = s.value;
std::string name =
arangodb::basics::VelocyPackHelper::getStringValue(slice, "");
if (name == id) {
role = ServerState::ROLE_SECONDARY;
_idOfPrimary = s.key.copyString();
break;
}
}
}
return role;
}
//////////////////////////////////////////////////////////////////////////////
/// @brief store the server role
//////////////////////////////////////////////////////////////////////////////
bool ServerState::storeRole(RoleEnum role) {
if (isClusterRole(role)) {
AgencyComm comm;
AgencyCommResult result;
if (role == ServerState::ROLE_COORDINATOR) {
VPackBuilder builder;
try {
builder.add(VPackValue("none"));
} catch (...) {
LOG(FATAL) << "out of memory"; FATAL_ERROR_EXIT();
}
// register coordinator
AgencyCommResult result =
comm.setValue("Current/Coordinators/" + _id, builder.slice(), 0.0);
if (!result.successful()) {
LOG(FATAL)
<< "unable to register coordinator in agency"; FATAL_ERROR_EXIT();
}
} else if (role == ServerState::ROLE_PRIMARY) {
VPackBuilder builder;
try {
builder.add(VPackValue("none"));
} catch (...) {
LOG(FATAL) << "out of memory"; FATAL_ERROR_EXIT();
}
// register server
AgencyCommResult result =
comm.setValue("Current/DBServers/" + _id, builder.slice(), 0.0);
if (!result.successful()) {
LOG(FATAL)
<< "unable to register db server in agency"; FATAL_ERROR_EXIT();
}
} else if (role == ServerState::ROLE_SECONDARY) {
std::string keyName = _id;
VPackBuilder builder;
try {
builder.add(VPackValue(keyName));
} catch (...) {
LOG(FATAL) << "out of memory"; FATAL_ERROR_EXIT();
}
std::string myId (
"Current/DBServers/" + ServerState::instance()->getPrimaryId());
AgencyOperation addMe(
myId, AgencyValueOperationType::SET, builder.slice());
AgencyOperation incrementVersion(
"Plan/Version", AgencySimpleOperationType::INCREMENT_OP);
AgencyPrecondition precondition(myId, AgencyPrecondition::EMPTY, true);
AgencyWriteTransaction trx({addMe, incrementVersion}, precondition);
// register server
AgencyCommResult result = comm.sendTransactionWithFailover(trx, 0.0);
if (!result.successful()) {
// mop: fail gracefully (allow retry)
return false;
}
}
}
_role.store(role, std::memory_order_release);
return true;
}
bool ServerState::isFoxxmaster() {
return !isRunningInCluster() || _foxxmaster == getId();
}
std::string const& ServerState::getFoxxmaster() {
return _foxxmaster;
}
void ServerState::setFoxxmaster(std::string const& foxxmaster) {
if (_foxxmaster != foxxmaster) {
setFoxxmasterQueueupdate(true);
}
_foxxmaster = foxxmaster;
}
bool ServerState::getFoxxmasterQueueupdate() {
return _foxxmasterQueueupdate;
}
void ServerState::setFoxxmasterQueueupdate(bool value) {
_foxxmasterQueueupdate = value;
}