1
0
Fork 0

Bug fix/fixes 1511 (#3711)

This commit is contained in:
Jan 2017-11-16 14:18:51 +01:00 committed by Frank Celler
parent 020e1b5b69
commit 5abf0c1185
27 changed files with 240 additions and 152 deletions

View File

@ -144,7 +144,11 @@ std::vector<index_t> State::logLeaderMulti(
30000, "Agency syntax requires array of transactions [[<queries>]]");
}
TRI_ASSERT(slice.length() == applicable.size());
if (slice.length() != applicable.size()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
30000, "Invalid transaction syntax");
}
MUTEX_LOCKER(mutexLocker, _logLock);
TRI_ASSERT(!_log.empty()); // log must never be empty
@ -159,8 +163,8 @@ std::vector<index_t> State::logLeaderMulti(
}
if (applicable[j]) {
std::string clientId((i.length()==3) ? i[2].copyString() : "");
idx[j] = logNonBlocking(_log.back().index+1, i[0], term, clientId, true);
std::string clientId((i.length() == 3) ? i[2].copyString() : "");
idx[j] = logNonBlocking(_log.back().index + 1, i[0], term, clientId, true);
}
++j;
}

View File

@ -156,24 +156,24 @@ std::vector<bool> Store::applyTransactions(query_t const& query) {
for (auto const& i : VPackArrayIterator(query->slice())) {
MUTEX_LOCKER(storeLocker, _storeLock);
switch (i.length()) {
case 1: // No precondition
success.push_back(applies(i[0]));
break;
case 2: // precondition + uuid
case 3:
if (check(i[1]).successful()) {
case 1: // No precondition
success.push_back(applies(i[0]));
} else { // precondition failed
LOG_TOPIC(TRACE, Logger::AGENCY) << "Precondition failed!";
break;
case 2: // precondition + uuid
case 3:
if (check(i[1]).successful()) {
success.push_back(applies(i[0]));
} else { // precondition failed
LOG_TOPIC(TRACE, Logger::AGENCY) << "Precondition failed!";
success.push_back(false);
}
break;
default: // Wrong
LOG_TOPIC(ERR, Logger::AGENCY)
<< "We can only handle log entry with or without precondition! "
<< " however, We received " << i.toJson();
success.push_back(false);
}
break;
default: // Wrong
LOG_TOPIC(ERR, Logger::AGENCY)
<< "We can only handle log entry with or without precondition! "
<< " However, We received " << i.toJson();
success.push_back(false);
break;
break;
}
}
@ -183,9 +183,10 @@ std::vector<bool> Store::applyTransactions(query_t const& query) {
_cv.signal();
}
} catch (std::exception const& e) { // Catch any erorrs
} catch (std::exception const& e) { // Catch any errors
LOG_TOPIC(ERR, Logger::AGENCY) << __FILE__ << ":" << __LINE__ << " "
<< e.what();
success.push_back(false);
}
} else {

View File

@ -85,7 +85,7 @@ class RemoteNode : public ExecutionNode {
auto c = new RemoteNode(plan, _id, _vocbase, _collection, _server, _ownName,
_queryId);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -169,7 +169,7 @@ class ScatterNode : public ExecutionNode {
bool withProperties) const override final {
auto c = new ScatterNode(plan, _id, _vocbase, _collection);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -238,7 +238,7 @@ class DistributeNode : public ExecutionNode {
_alternativeVarId, _createKeys,
_allowKeyConversionToObject);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -321,7 +321,7 @@ class GatherNode : public ExecutionNode {
bool withProperties) const override final {
auto c = new GatherNode(plan, _id, _vocbase, _collection);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -167,7 +167,7 @@ ExecutionNode* CollectNode::clone(ExecutionPlan* plan, bool withDependencies,
c->specialized();
}
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -390,9 +390,18 @@ void ExecutionNode::toVelocyPack(VPackBuilder& builder,
}
/// @brief execution Node clone utility to be called by derives
void ExecutionNode::cloneHelper(ExecutionNode* other, ExecutionPlan* plan,
void ExecutionNode::cloneHelper(ExecutionNode* other,
bool withDependencies,
bool withProperties) const {
ExecutionPlan* plan = other->plan();
if (plan == _plan) {
// same execution plan for source and target
// now assign a new id to the cloned node, otherwise it will leak
// upon node registration and its meaning is ambiguous
other->setId(plan->nextId());
}
plan->registerNode(other);
if (withProperties) {
@ -1185,7 +1194,7 @@ ExecutionNode* EnumerateCollectionNode::clone(ExecutionPlan* plan,
c->setProjection(_projection);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -1241,7 +1250,7 @@ ExecutionNode* EnumerateListNode::clone(ExecutionPlan* plan,
auto c = new EnumerateListNode(plan, _id, inVariable, outVariable);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -1377,7 +1386,7 @@ ExecutionNode* CalculationNode::clone(ExecutionPlan* plan,
conditionVariable, outVariable);
c->_canRemoveIfThrows = _canRemoveIfThrows;
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -1446,7 +1455,7 @@ ExecutionNode* SubqueryNode::clone(ExecutionPlan* plan, bool withDependencies,
auto c = new SubqueryNode(
plan, _id, _subquery->clone(plan, true, withProperties), outVariable);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -1636,7 +1645,7 @@ ExecutionNode* FilterNode::clone(ExecutionPlan* plan, bool withDependencies,
}
auto c = new FilterNode(plan, _id, inVariable);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -1685,7 +1694,7 @@ ExecutionNode* ReturnNode::clone(ExecutionPlan* plan, bool withDependencies,
auto c = new ReturnNode(plan, _id, inVariable);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -159,10 +159,6 @@ class ExecutionNode {
/// @brief return the node's id
inline size_t id() const { return _id; }
/// @brief set the id, use with care! The purpose is to use a cloned node
/// together with the original in the same plan.
void setId(size_t id) { _id = id; }
/// @brief return the type of the node
virtual NodeType getType() const = 0;
@ -376,8 +372,7 @@ class ExecutionNode {
bool withProperties) const = 0;
/// @brief execution Node clone utility to be called by derives
void cloneHelper(ExecutionNode* Other, ExecutionPlan* plan,
bool withDependencies, bool withProperties) const;
void cloneHelper(ExecutionNode* Other, bool withDependencies, bool withProperties) const;
/// @brief helper for cloning, use virtual clone methods for dependencies
void cloneDependencies(ExecutionPlan* plan, ExecutionNode* theClone,
@ -493,6 +488,10 @@ class ExecutionNode {
ExecutionPlan const* plan() const {
return _plan;
}
ExecutionPlan* plan() {
return _plan;
}
/// @brief static analysis, walker class and information collector
struct VarInfo {
@ -585,6 +584,10 @@ class ExecutionNode {
ExecutionNode const* getLoop() const;
protected:
/// @brief set the id, use with care! The purpose is to use a cloned node
/// together with the original in the same plan.
void setId(size_t id) { _id = id; }
/// @brief factory for sort elements
static void getSortElements(SortElementVector& elements, ExecutionPlan* plan,
arangodb::velocypack::Slice const& slice,
@ -678,7 +681,7 @@ class SingletonNode : public ExecutionNode {
bool withProperties) const override final {
auto c = new SingletonNode(plan, _id);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -851,7 +854,7 @@ class LimitNode : public ExecutionNode {
c->setFullCount();
}
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -1245,7 +1248,7 @@ class NoResultsNode : public ExecutionNode {
bool withProperties) const override final {
auto c = new NoResultsNode(plan, _id);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -256,29 +256,6 @@ ExecutionPlan* ExecutionPlan::instantiateFromVelocyPack(
return plan.release();
}
/// @brief clone the plan by recursively cloning starting from the root
class CloneNodeAdder final : public WalkerWorker<ExecutionNode> {
ExecutionPlan* _plan;
public:
bool success;
explicit CloneNodeAdder(ExecutionPlan* plan) : _plan(plan), success(true) {}
~CloneNodeAdder() {}
bool before(ExecutionNode* node) override final {
// We need to catch exceptions because the walk has to finish
// and either register the nodes or delete them.
try {
_plan->registerNode(node);
} catch (...) {
success = false;
}
return false;
}
};
/// @brief clone an existing execution plan
ExecutionPlan* ExecutionPlan::clone(Ast* ast) {
auto plan = std::make_unique<ExecutionPlan>(ast);
@ -288,12 +265,6 @@ ExecutionPlan* ExecutionPlan::clone(Ast* ast) {
plan->_appliedRules = _appliedRules;
plan->_isResponsibleForInitialize = _isResponsibleForInitialize;
CloneNodeAdder adder(plan.get());
plan->_root->walk(&adder);
if (!adder.success) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Could not clone plan");
}
// plan->findVarUsage();
// Let's not do it here, because supposedly the plan is modified as
// the very next thing anyway!
@ -611,6 +582,7 @@ CollectOptions ExecutionPlan::createCollectOptions(AstNode const* node) {
ExecutionNode* ExecutionPlan::registerNode(ExecutionNode* node) {
TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->id() > 0);
TRI_ASSERT(_ids.find(node->id()) == _ids.end());
try {
_ids.emplace(node->id(), node);

View File

@ -136,7 +136,7 @@ ExecutionNode* IndexNode::clone(ExecutionPlan* plan, bool withDependencies,
auto c = new IndexNode(plan, _id, _vocbase, _collection, outVariable,
_indexes, _condition->clone(), _reverse);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -115,7 +115,7 @@ ExecutionNode* RemoveNode::clone(ExecutionPlan* plan, bool withDependencies,
auto c = new RemoveNode(plan, _id, _vocbase, _collection, _options,
inVariable, outVariableOld);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -154,7 +154,7 @@ ExecutionNode* InsertNode::clone(ExecutionPlan* plan, bool withDependencies,
auto c = new InsertNode(plan, _id, _vocbase, _collection, _options,
inVariable, outVariableNew);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -210,7 +210,7 @@ ExecutionNode* UpdateNode::clone(ExecutionPlan* plan, bool withDependencies,
new UpdateNode(plan, _id, _vocbase, _collection, _options, inDocVariable,
inKeyVariable, outVariableOld, outVariableNew);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -267,7 +267,7 @@ ExecutionNode* ReplaceNode::clone(ExecutionPlan* plan, bool withDependencies,
new ReplaceNode(plan, _id, _vocbase, _collection, _options, inDocVariable,
inKeyVariable, outVariableOld, outVariableNew);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}
@ -320,7 +320,7 @@ ExecutionNode* UpsertNode::clone(ExecutionPlan* plan, bool withDependencies,
inDocVariable, insertVariable, updateVariable,
outVariableNew, _isReplace);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -295,7 +295,7 @@ ExecutionNode* ShortestPathNode::clone(ExecutionPlan* plan,
c->_fromCondition = _fromCondition->clone(_plan->getAst());
c->_toCondition = _toCondition->clone(_plan->getAst());
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -73,7 +73,7 @@ class SortNode : public ExecutionNode {
bool withProperties) const override final {
auto c = new SortNode(plan, _id, _elements, _stable);
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -463,7 +463,7 @@ ExecutionNode* TraversalNode::clone(ExecutionPlan* plan, bool withDependencies,
c->checkConditionsDefined();
#endif
cloneHelper(c, plan, withDependencies, withProperties);
cloneHelper(c, withDependencies, withProperties);
return static_cast<ExecutionNode*>(c);
}

View File

@ -100,7 +100,12 @@ void RestClusterHandler::handleCommandEndpoints() {
VPackSlice healthMap = result.slice()[0].get(path);
if (leaderId.empty()) {
generateError(Result(TRI_ERROR_FAILED, "Leadership challenge is ongoing"));
generateError(Result(TRI_ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING, "Leadership challenge is ongoing"));
// intentionally use an empty endpoint here. clients can check for the returned
// endpoint value, and can tell the following two cases apart:
// - endpoint value is not empty: there is a leader, and it is known
// - endpoint value is empty: leadership challenge is ongoing, current leader is unknown
_response->setHeader(StaticStrings::LeaderEndpoint, "");
return;
}

View File

@ -64,7 +64,7 @@ class ServerState {
REDIRECT = 3,
/// redirect to lead server if possible
READ_ONLY = 4,
INVALID = 255,
INVALID = 255, // this mode is used to indicate shutdown
};
public:

View File

@ -22,13 +22,12 @@
////////////////////////////////////////////////////////////////////////////////
#include "RestHandlerFactory.h"
#include "Cluster/ServerState.h"
#include "GeneralServer/RestHandler.h"
#include "Logger/Logger.h"
#include "Replication/ReplicationFeature.h"
#include "Replication/GlobalReplicationApplier.h"
#include "Rest/GeneralRequest.h"
#include "RestHandler/RestBaseHandler.h"
#include "RestHandler/RestDocumentHandler.h"
#include "RestHandler/RestVersionHandler.h"
@ -39,13 +38,13 @@ using namespace arangodb::rest;
static std::string const ROOT_PATH = "/";
namespace {
class MaintenanceHandler : public RestHandler {
bool _redirect;
class MaintenanceHandler : public RestBaseHandler {
ServerState::Mode _mode;
public:
explicit MaintenanceHandler(GeneralRequest* request,
GeneralResponse* response,
bool redirect)
: RestHandler(request, response), _redirect(redirect) {};
MaintenanceHandler(GeneralRequest* request,
GeneralResponse* response,
ServerState::Mode mode)
: RestBaseHandler(request, response), _mode(mode) {}
char const* name() const override final { return "MaintenanceHandler"; }
@ -56,30 +55,50 @@ class MaintenanceHandler : public RestHandler {
RestStatus execute() override {
// use this to redirect requests
if (_redirect) {
ReplicationFeature* replication = ReplicationFeature::INSTANCE;
if (replication != nullptr && replication->isAutomaticFailoverEnabled()) {
GlobalReplicationApplier* applier = replication->globalReplicationApplier();
if (applier != nullptr && applier->isRunning()) {
std::string endpoint = applier->endpoint();
// replace tcp:// with http://, and ssl:// with https://
endpoint = fixEndpointProtocol(endpoint);
resetResponse(rest::ResponseCode::TEMPORARY_REDIRECT);
_response->setHeader("Location", endpoint + _request->requestPath());
_response->setHeader("x-arango-endpoint", applier->endpoint());
return RestStatus::DONE;
switch (_mode) {
case ServerState::Mode::REDIRECT: {
std::string endpoint;
ReplicationFeature* replication = ReplicationFeature::INSTANCE;
if (replication != nullptr && replication->isAutomaticFailoverEnabled()) {
GlobalReplicationApplier* applier = replication->globalReplicationApplier();
if (applier != nullptr) {
endpoint = applier->endpoint();
// replace tcp:// with http://, and ssl:// with https://
endpoint = fixEndpointProtocol(endpoint);
}
}
generateError(Result(TRI_ERROR_CLUSTER_NOT_LEADER));
// return the endpoint of the actual leader
_response->setHeader(StaticStrings::LeaderEndpoint, endpoint);
break;
}
case ServerState::Mode::TRYAGAIN: {
generateError(Result(TRI_ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING));
// intentionally do not set "Location" header, but use a custom header that
// clients can inspect. if they find an empty endpoint, it means that there
// is an ongoing leadership challenge
_response->setHeader(StaticStrings::LeaderEndpoint, "");
break;
}
case ServerState::Mode::INVALID: {
generateError(Result(TRI_ERROR_SHUTTING_DOWN));
break;
}
case ServerState::Mode::MAINTENANCE:
default: {
resetResponse(rest::ResponseCode::SERVICE_UNAVAILABLE);
break;
}
}
resetResponse(rest::ResponseCode::SERVICE_UNAVAILABLE);
return RestStatus::DONE;
};
}
void handleError(const Exception& error) override {
resetResponse(rest::ResponseCode::SERVICE_UNAVAILABLE);
};
}
// replace tcp:// with http://, and ssl:// with https://
std::string fixEndpointProtocol(std::string const& endpoint) const {
@ -121,7 +140,7 @@ RestHandler* RestHandlerFactory::createHandler(
// In the shutdown phase we simply return 503:
if (application_features::ApplicationServer::isStopping()) {
return new MaintenanceHandler(req.release(), res.release(), false);
return new MaintenanceHandler(req.release(), res.release(), ServerState::Mode::INVALID);
}
// In the bootstrap phase, we would like that coordinators answer the
@ -134,7 +153,7 @@ RestHandler* RestHandlerFactory::createHandler(
(path.find("/_api/agency/agency-callbacks") == std::string::npos &&
path.find("/_api/aql") == std::string::npos)) {
LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "Maintenance mode: refused path: " << path;
return new MaintenanceHandler(req.release(), res.release(), false);
return new MaintenanceHandler(req.release(), res.release(), mode);
}
break;
}
@ -148,7 +167,7 @@ RestHandler* RestHandlerFactory::createHandler(
path.find("/_api/version") == std::string::npos &&
path.find("/_api/wal") == std::string::npos) {
LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "Maintenance mode: refused path: " << path;
return new MaintenanceHandler(req.release(), res.release(), true);
return new MaintenanceHandler(req.release(), res.release(), mode);
}
break;
}

View File

@ -37,14 +37,13 @@ void AcceptorTcp::open() {
boost::asio::ip::tcp::endpoint asioEndpoint;
boost::system::error_code err;
auto address = boost::asio::ip::address::from_string(hostname,err);
if(!err) {
if (!err) {
asioEndpoint = boost::asio::ip::tcp::endpoint(address,portNumber);
} else { // we need to resolve the string containing the ip
std::unique_ptr<boost::asio::ip::tcp::resolver::query> query;
if (_endpoint->domain() == AF_INET6) {
query.reset(new boost::asio::ip::tcp::resolver::query(boost::asio::ip::tcp::v6(), hostname, std::to_string(portNumber)));
} else if (_endpoint->domain() == AF_INET) {
query.reset(new boost::asio::ip::tcp::resolver::query(boost::asio::ip::tcp::v4(), hostname, std::to_string(portNumber)));
} else {
THROW_ARANGO_EXCEPTION(TRI_ERROR_IP_ADDRESS_INVALID);
@ -56,7 +55,7 @@ void AcceptorTcp::open() {
throw std::runtime_error(err.message());
}
if(boost::asio::ip::tcp::resolver::iterator{} == iter){
if (boost::asio::ip::tcp::resolver::iterator{} == iter) {
LOG_TOPIC(ERR, Logger::COMMUNICATION) << "unable to to resolve endpoint: endpoint is default constructed";
}
@ -68,12 +67,29 @@ void AcceptorTcp::open() {
boost::asio::ip::tcp::acceptor::reuse_address(
((EndpointIp*)_endpoint)->reuseAddress()));
#ifdef _WIN32
// on Windows everything is different of course:
// we need to set SO_EXCLUSIVEADDRUSE to prevent others from binding to our
// ip/port.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
int trueOption = 1;
if (::setsockopt(_acceptor.native(), SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char const*) &trueOption, sizeof(int)) != 0) {
LOG_TOPIC(ERR, Logger::COMMUNICATION) << "unable to set acceptor socket option: " << WSAGetLastError();
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, "unable to set acceptor socket option");
}
#endif
_acceptor.bind(asioEndpoint, err);
if (err) {
LOG_TOPIC(ERR, Logger::COMMUNICATION) << "unable to bind endpoint: " << err.message();
throw std::runtime_error(err.message());
}
_acceptor.listen();
_acceptor.listen(_endpoint->listenBacklog(), err);
if (err) {
LOG_TOPIC(ERR, Logger::COMMUNICATION) << "unable to bind endpoint: " << err.message();
throw std::runtime_error(err.message());
}
}
void AcceptorTcp::asyncAccept(AcceptHandler const& handler) {

View File

@ -103,18 +103,6 @@ static bool indexSupportsSort(Index const* idx,
return false;
}
/// @brief Return an Operation Result that parses the error information returned
/// by the DBServer.
static OperationResult dbServerResponseBad(
std::shared_ptr<VPackBuilder> resultBody) {
VPackSlice res = resultBody->slice();
return OperationResult(
arangodb::basics::VelocyPackHelper::getNumericValue<int>(
res, "errorNum", TRI_ERROR_INTERNAL),
arangodb::basics::VelocyPackHelper::getStringValue(
res, "errorMessage", "JSON sent to DBserver was bad"));
}
/// @brief Insert an error reported instead of the new document
static void createBabiesError(VPackBuilder& builder,
std::unordered_map<int, size_t>& countErrorCodes,
@ -1065,20 +1053,26 @@ Result transaction::Methods::documentFastPathLocal(
return res;
}
static OperationResult errorCodeFromClusterResult(std::shared_ptr<VPackBuilder> const& resultBody) {
// read the error number from the response
static OperationResult errorCodeFromClusterResult(std::shared_ptr<VPackBuilder> const& resultBody,
int defaultErrorCode) {
// read the error number from the response and use it if present
if (resultBody != nullptr) {
VPackSlice slice = resultBody->slice();
if (slice.isObject()) {
VPackSlice num = slice.get("errorNum");
VPackSlice msg = slice.get("errorMessage");
if (num.isNumber()) {
if (msg.isString()) {
// found an error number and an error message, so let's use it!
return OperationResult(num.getNumericValue<int>(), msg.copyString());
}
// we found an error number, so let's use it!
return OperationResult(num.getNumericValue<int>());
}
}
}
// default is to return "internal error"
return OperationResult(TRI_ERROR_INTERNAL);
return OperationResult(defaultErrorCode);
}
/// @brief Create Cluster Communication result for document
@ -1095,9 +1089,9 @@ OperationResult transaction::Methods::clusterResultDocument(
: TRI_ERROR_ARANGO_CONFLICT,
false, errorCounter);
case rest::ResponseCode::NOT_FOUND:
return OperationResult(TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
default:
return errorCodeFromClusterResult(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
}
}
@ -1113,15 +1107,15 @@ OperationResult transaction::Methods::clusterResultInsert(
resultBody->steal(), nullptr, "", TRI_ERROR_NO_ERROR,
responseCode == rest::ResponseCode::CREATED, errorCounter);
case rest::ResponseCode::PRECONDITION_FAILED:
return OperationResult(TRI_ERROR_ARANGO_CONFLICT);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_ARANGO_CONFLICT);
case rest::ResponseCode::BAD:
return dbServerResponseBad(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
case rest::ResponseCode::NOT_FOUND:
return OperationResult(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
case rest::ResponseCode::CONFLICT:
return OperationResult(TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED);
default:
return errorCodeFromClusterResult(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
}
}
@ -1146,11 +1140,11 @@ OperationResult transaction::Methods::clusterResultModify(
responseCode == rest::ResponseCode::CREATED,
errorCounter);
case rest::ResponseCode::BAD:
return dbServerResponseBad(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
case rest::ResponseCode::NOT_FOUND:
return OperationResult(TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
default:
return errorCodeFromClusterResult(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
}
}
@ -1170,11 +1164,11 @@ OperationResult transaction::Methods::clusterResultRemove(
: TRI_ERROR_NO_ERROR,
responseCode != rest::ResponseCode::ACCEPTED, errorCounter);
case rest::ResponseCode::BAD:
return dbServerResponseBad(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
case rest::ResponseCode::NOT_FOUND:
return OperationResult(TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
default:
return errorCodeFromClusterResult(resultBody);
return errorCodeFromClusterResult(resultBody, TRI_ERROR_INTERNAL);
}
}

View File

@ -861,7 +861,27 @@ AuthResult AuthInfo::checkPassword(std::string const& username,
AuthLevel AuthInfo::canUseDatabase(std::string const& username,
std::string const& dbname) {
loadFromDB();
READ_LOCKER(guard, _authInfoLock);
AuthLevel level;
{
READ_LOCKER(guard, _authInfoLock);
level = canUseDatabaseInternal(username, dbname, 0);
}
static_assert(AuthLevel::RO < AuthLevel::RW, "ro < rw");
if (level > AuthLevel::RO && !ServerState::writeOpsEnabled()) {
// no write operations allowed on this server at all
LOG_TOPIC(TRACE, Logger::FIXME) << "downgrading user rights";
return AuthLevel::RO;
}
// return actual level
return level;
}
// worker function for canUseDatabase
// must only be called with the read-lock on _authInfoLock being held
AuthLevel AuthInfo::canUseDatabaseInternal(std::string const& username,
std::string const& dbname,
size_t depth) const {
auto it = _authInfo.find(username);
if (it == _authInfo.end()) {
@ -872,23 +892,25 @@ AuthLevel AuthInfo::canUseDatabase(std::string const& username,
AuthLevel level = entry.databaseAuthLevel(dbname);
#ifdef USE_ENTERPRISE
// check all roles and use the highest permission from them
for (auto const& role : entry.roles()) {
if (level == AuthLevel::RW) {
return level;
// we already have highest permission
break;
}
AuthLevel roleLevel = canUseDatabase(role, dbname);
// recurse into function, but only one level deep.
// this allows us to avoid endless recursion without major overhead
if (depth == 0) {
AuthLevel roleLevel = canUseDatabaseInternal(role, dbname, depth + 1);
if (level == AuthLevel::NONE) {
level = roleLevel;
if (level == AuthLevel::NONE) {
// use the permission of the role we just found
level = roleLevel;
}
}
}
#endif
static_assert(AuthLevel::RO < AuthLevel::RW, "ro < rw");
if (level > AuthLevel::RO && !ServerState::writeOpsEnabled()) {
LOG_TOPIC(ERR, Logger::FIXME) << "downgrading user rights";
return AuthLevel::RO;
}
return level;
}

View File

@ -125,6 +125,11 @@ class AuthInfo {
std::string generateRawJwt(VPackBuilder const&);
private:
// worker function for canUseDatabase
// must only be called with the read-lock on _authInfoLock being held
AuthLevel canUseDatabaseInternal(std::string const& username,
std::string const& dbname, size_t depth) const;
// internal method called by canUseCollection
// asserts that collection name is non-empty and already translated
// from collection id to name

View File

@ -177,6 +177,8 @@
"ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED" : { "code" : 1492, "message" : "some agency operation failed" },
"ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_REPLICATION_FACTOR" : { "code" : 1493, "message" : "conflicting replication factor with distributeShardsLike parameter assignment" },
"ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_NUMBER_OF_SHARDS" : { "code" : 1494, "message" : "conflicting shard number with distributeShardsLike parameter assignment" },
"ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING" : { "code" : 1495, "message" : "leadership challenge is ongoing" },
"ERROR_CLUSTER_NOT_LEADER" : { "code" : 1496, "message" : "no leader" },
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },

View File

@ -105,6 +105,7 @@ std::string const StaticStrings::ExposedCorsHeaders(
"x-arango-errors, x-arango-async-id");
std::string const StaticStrings::HLCHeader("x-arango-hlc");
std::string const StaticStrings::KeepAlive("Keep-Alive");
std::string const StaticStrings::LeaderEndpoint("x-arango-endpoint");
std::string const StaticStrings::Location("location");
std::string const StaticStrings::MultiPartContentType("multipart/form-data");
std::string const StaticStrings::NoSniff("nosniff");

View File

@ -100,6 +100,7 @@ class StaticStrings {
static std::string const ExposedCorsHeaders;
static std::string const HLCHeader;
static std::string const KeepAlive;
static std::string const LeaderEndpoint;
static std::string const Location;
static std::string const MultiPartContentType;
static std::string const NoSniff;

View File

@ -213,6 +213,8 @@ ERROR_CLUSTER_SHARD_LEADER_RESIGNED,1491,"a (former) shard leader refuses to per
ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED,1492,"some agency operation failed","Will be raised if after various retries an agency operation could not be performed successfully."
ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_REPLICATION_FACTOR,1493,"conflicting replication factor with distributeShardsLike parameter assignment","Will be raised if intended replication factor does not match that of the prototype shard given in ditributeShardsLike parameter."
ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_NUMBER_OF_SHARDS,1494,"conflicting shard number with distributeShardsLike parameter assignment","Will be raised if intended number of shards does not match that of the prototype shard given in ditributeShardsLike parameter."
ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING,1495,"leadership challenge is ongoing","Will be raised when servers are currently competing for leadership, and the result is still unknown."
ERROR_CLUSTER_NOT_LEADER,1496,"no leader","Will be raised when an operation is sent to a non-leading server."
################################################################################
## ArangoDB query errors

View File

@ -173,6 +173,8 @@ void TRI_InitializeErrorMessages () {
REG_ERROR(ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED, "some agency operation failed");
REG_ERROR(ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_REPLICATION_FACTOR, "conflicting replication factor with distributeShardsLike parameter assignment");
REG_ERROR(ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_NUMBER_OF_SHARDS, "conflicting shard number with distributeShardsLike parameter assignment");
REG_ERROR(ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING, "leadership challenge is ongoing");
REG_ERROR(ERROR_CLUSTER_NOT_LEADER, "no leader");
REG_ERROR(ERROR_QUERY_KILLED, "query killed");
REG_ERROR(ERROR_QUERY_PARSE, "%s");
REG_ERROR(ERROR_QUERY_EMPTY, "query is empty");

View File

@ -429,6 +429,11 @@
/// - 1494: @LIT{conflicting shard number with distributeShardsLike parameter assignment}
/// Will be raised if intended number of shards does not match that of the
/// prototype shard given in ditributeShardsLike parameter.
/// - 1495: @LIT{leadership challenge is ongoing}
/// Will be raised when servers are currently competing for leadership, and
/// the result is still unknown.
/// - 1496: @LIT{no leader}
/// Will be raised when an operation is sent to a non-leading server.
/// - 1500: @LIT{query killed}
/// Will be raised when a running query is killed by an explicit admin
/// command.
@ -2530,6 +2535,27 @@ void TRI_InitializeErrorMessages ();
#define TRI_ERROR_CLUSTER_DISTRIBUTE_SHARDS_LIKE_NUMBER_OF_SHARDS (1494)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1495: ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING
///
/// leadership challenge is ongoing
///
/// Will be raised when servers are currently competing for leadership, and the
/// result is still unknown.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING (1495)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1496: ERROR_CLUSTER_NOT_LEADER
///
/// no leader
///
/// Will be raised when an operation is sent to a non-leading server.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_CLUSTER_NOT_LEADER (1496)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1500: ERROR_QUERY_KILLED
///

View File

@ -82,6 +82,8 @@ class Endpoint {
virtual int port() const = 0;
virtual std::string host() const = 0;
virtual std::string hostAndPort() const = 0;
int listenBacklog() const { return _listenBacklog; }
public:
std::string _errorMessage;

View File

@ -407,6 +407,8 @@ rest::ResponseCode GeneralResponse::responseCode(int code) {
return ResponseCode::SERVER_ERROR;
case TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE:
case TRI_ERROR_CLUSTER_LEADERSHIP_CHALLENGE_ONGOING:
case TRI_ERROR_CLUSTER_NOT_LEADER:
return ResponseCode::SERVICE_UNAVAILABLE;
case TRI_ERROR_CLUSTER_UNSUPPORTED: