1
0
Fork 0

Bug fix/allow agency ops in active failover (#9881)

* allow agency operations in active failover too

* Added regression test

* Allowed more calls in active failover for the health endpoint to work

* Updated CHANGELOG
This commit is contained in:
Tobias Gödderz 2019-09-09 16:53:57 +02:00 committed by Michael Hackstein
parent 98fea326ea
commit 9e9fa3a4f1
3 changed files with 99 additions and 106 deletions

View File

@ -1,6 +1,9 @@
devel
-----
* Fixed "ArangoDB is not running in cluster mode" errors in active failover setups.
This affected at least /_admin/cluster/health.
* Fixed the removal (including a collection drop) of an orphanCollection from a
graph definition when using the arango shell. The boolean
flag whether to drop the collection or not was not transferred properly.
@ -16,7 +19,7 @@ devel
above inconsistencies. Also creation of databases are now secured against
coordinator outages, they will either be fully created or not visible and
eventually dropped. This does not require any change on the client code.
* Added UI support to create documents in a collection using smartGraphAttribute
and/or smartJoinAttribute.

View File

@ -28,11 +28,13 @@
#include "Agency/AgencyComm.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/Exceptions.h"
#include "Basics/StringBuffer.h"
#include "Cluster/ClusterComm.h"
#include "Cluster/ClusterInfo.h"
#include "Cluster/ServerState.h"
#include "GeneralServer/AuthenticationFeature.h"
#include "Replication/ReplicationFeature.h"
#include "Sharding/ShardDistributionReporter.h"
#include "V8/v8-buffer.h"
#include "V8/v8-conv.h"
@ -54,12 +56,24 @@ using namespace arangodb::basics;
CreateAgencyException(args, data); \
return;
#define ONLY_IN_CLUSTER \
if (!ServerState::instance()->isRunningInCluster()) { \
TRI_V8_THROW_EXCEPTION_INTERNAL( \
"ArangoDB is not running in cluster mode"); \
static void onlyInCluster() {
if (ServerState::instance()->isRunningInCluster()) {
return;
}
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "ArangoDB is not running in cluster mode");
}
static void onlyInClusterOrActiveFailover() {
auto replicationFeature = ReplicationFeature::INSTANCE;
if (replicationFeature != nullptr && replicationFeature->isActiveFailoverEnabled()) {
// active failover enabled
return;
}
return onlyInCluster();
}
static void CreateAgencyException(v8::FunctionCallbackInfo<v8::Value> const& args,
AgencyCommResult const& result) {
v8::Isolate* isolate = args.GetIsolate();
@ -102,7 +116,7 @@ static void JS_CasAgency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() < 3) {
TRI_V8_THROW_EXCEPTION_USAGE(
@ -164,7 +178,7 @@ static void JS_CreateDirectoryAgency(v8::FunctionCallbackInfo<v8::Value> const&
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("createDirectory(<key>)");
@ -211,7 +225,7 @@ static void JS_IncreaseVersionAgency(v8::FunctionCallbackInfo<v8::Value> const&
TRI_V8_TRY_CATCH_BEGIN(isolate)
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("increaseVersion(<key>)");
@ -237,7 +251,7 @@ static void JS_GetAgency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate)
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE("get(<key>)");
@ -279,7 +293,7 @@ static void JS_APIAgency(std::string const& envelope,
TRI_V8_TRY_CATCH_BEGIN(isolate)
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE(std::string(envelope) + "([[...]])");
@ -344,7 +358,7 @@ static void JS_RemoveAgency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE("remove(<key>, <recursive>)");
@ -376,7 +390,7 @@ static void JS_SetAgency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE("set(<key>, <value>, <ttl>)");
@ -415,7 +429,7 @@ static void JS_Agency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate)
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() > 0) {
TRI_V8_THROW_EXCEPTION_USAGE("agency()");
@ -458,7 +472,7 @@ static void JS_EndpointsAgency(v8::FunctionCallbackInfo<v8::Value> const& args)
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("endpoints()");
@ -503,7 +517,7 @@ static void JS_UniqidAgency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() > 2) {
TRI_V8_THROW_EXCEPTION_USAGE("uniqid(<count>, <timeout>)");
@ -540,7 +554,7 @@ static void JS_VersionAgency(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER;
onlyInClusterOrActiveFailover();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("version()");
@ -561,7 +575,8 @@ static void JS_DoesDatabaseExistClusterInfo(v8::FunctionCallbackInfo<v8::Value>
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("doesDatabaseExist(<database-id>)");
}
@ -583,12 +598,13 @@ static void JS_DoesDatabaseExistClusterInfo(v8::FunctionCallbackInfo<v8::Value>
static void JS_Databases(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
onlyInCluster();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("databases()");
}
ONLY_IN_CLUSTER
std::vector<DatabaseID> res = ClusterInfo::instance()->databases(true);
v8::Handle<v8::Array> a = v8::Array::New(isolate, (int)res.size());
std::vector<DatabaseID>::iterator it;
@ -608,7 +624,8 @@ static void JS_FlushClusterInfo(v8::FunctionCallbackInfo<v8::Value> const& args)
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("flush()");
}
@ -627,7 +644,8 @@ static void JS_GetCollectionInfoClusterInfo(v8::FunctionCallbackInfo<v8::Value>
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"getCollectionInfo(<database-id>, <collection-id>)");
@ -698,7 +716,8 @@ static void JS_GetCollectionInfoCurrentClusterInfo(v8::FunctionCallbackInfo<v8::
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 3) {
TRI_V8_THROW_EXCEPTION_USAGE(
"getCollectionInfoCurrent(<database-id>, <collection-id>, <shardID>)");
@ -774,7 +793,8 @@ static void JS_GetResponsibleServerClusterInfo(v8::FunctionCallbackInfo<v8::Valu
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("getResponsibleServer(<shard-id>)");
}
@ -799,7 +819,8 @@ static void JS_GetResponsibleServersClusterInfo(v8::FunctionCallbackInfo<v8::Val
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1 || !args[0]->IsArray()) {
TRI_V8_THROW_EXCEPTION_USAGE("getResponsibleServers(<shard-ids>)");
}
@ -835,7 +856,8 @@ static void JS_GetResponsibleShardClusterInfo(v8::FunctionCallbackInfo<v8::Value
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() < 2 || args.Length() > 3) {
TRI_V8_THROW_EXCEPTION_USAGE(
"getResponsibleShard(<collection-id>, <document>, "
@ -898,7 +920,8 @@ static void JS_GetServerEndpointClusterInfo(v8::FunctionCallbackInfo<v8::Value>
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("getServerEndpoint(<server-id>)");
}
@ -918,7 +941,8 @@ static void JS_GetServerNameClusterInfo(v8::FunctionCallbackInfo<v8::Value> cons
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("getServerName(<endpoint>)");
}
@ -938,7 +962,8 @@ static void JS_GetDBServers(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("getDBServers()");
}
@ -979,7 +1004,7 @@ static void JS_GetCoordinators(v8::FunctionCallbackInfo<v8::Value> const& args)
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("getCoordinators()");
@ -1059,7 +1084,8 @@ static void JS_IdServerState(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInClusterOrActiveFailover();
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("id()");
}
@ -1147,40 +1173,6 @@ static void JS_GetFoxxmasterSince(v8::FunctionCallbackInfo<v8::Value> const& arg
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the primary servers id (only for secondaries)
////////////////////////////////////////////////////////////////////////////////
static void JS_IdOfPrimaryServerState(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("idOfPrimary()");
}
TRI_V8_RETURN_STRING(""); // no more secondaries
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the javascript startup path
////////////////////////////////////////////////////////////////////////////////
static void JS_JavaScriptPathServerState(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("javaScriptPath()");
}
std::string const path = ServerState::instance()->getJavaScriptPath();
TRI_V8_RETURN_STD_STRING(path);
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether the cluster is initialized
////////////////////////////////////////////////////////////////////////////////
@ -1262,29 +1254,6 @@ static void JS_SetRoleServerState(v8::FunctionCallbackInfo<v8::Value> const& arg
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief redetermines the role from the agency
////////////////////////////////////////////////////////////////////////////////
static void JS_RedetermineRoleServerState(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("redetermineRole()");
}
/*bool changed = ServerState::instance()->redetermineRole();
if (changed) {
TRI_V8_RETURN_TRUE();
} else {
}*/
TRI_V8_RETURN_FALSE();
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the server state
////////////////////////////////////////////////////////////////////////////////
@ -1321,7 +1290,8 @@ static void PrepareClusterCommRequest(v8::FunctionCallbackInfo<v8::Value> const&
v8::Local<v8::Context> context = isolate->GetCurrentContext();
TRI_V8_CURRENT_GLOBALS_AND_SCOPE;
ONLY_IN_CLUSTER
onlyInClusterOrActiveFailover();
TRI_ASSERT(args.Length() >= 4);
reqType = arangodb::rest::RequestType::GET;
@ -1552,7 +1522,8 @@ static void Return_PrepareClusterCommResultForJS(v8::FunctionCallbackInfo<v8::Va
static void JS_AsyncRequest(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInClusterOrActiveFailover();
if (args.Length() < 4 || args.Length() > 7) {
TRI_V8_THROW_EXCEPTION_USAGE(
@ -1609,7 +1580,8 @@ static void JS_AsyncRequest(v8::FunctionCallbackInfo<v8::Value> const& args) {
static void JS_SyncRequest(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() < 4 || args.Length() > 7) {
TRI_V8_THROW_EXCEPTION_USAGE(
@ -1670,7 +1642,8 @@ static void JS_SyncRequest(v8::FunctionCallbackInfo<v8::Value> const& args) {
static void JS_Enquire(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("enquire(operationID)");
@ -1702,7 +1675,8 @@ static void JS_Wait(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
TRI_V8_CURRENT_GLOBALS_AND_SCOPE;
ONLY_IN_CLUSTER
onlyInClusterOrActiveFailover();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("wait(obj)");
@ -1767,7 +1741,8 @@ static void JS_Drop(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
TRI_V8_CURRENT_GLOBALS_AND_SCOPE;
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE("drop(obj)");
@ -1868,7 +1843,8 @@ static void JS_ClusterDownload(v8::FunctionCallbackInfo<v8::Value> const& args)
static void JS_GetShardDistribution(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
v8::HandleScope scope(isolate);
auto& vocbase = GetContextVocBase(isolate);
@ -1887,7 +1863,8 @@ static void JS_GetShardDistribution(v8::FunctionCallbackInfo<v8::Value> const& a
static void JS_GetCollectionShardDistribution(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
ONLY_IN_CLUSTER
onlyInCluster();
if (args.Length() != 1) {
TRI_V8_THROW_EXCEPTION_USAGE(
@ -2043,11 +2020,6 @@ void TRI_InitV8Cluster(v8::Isolate* isolate, v8::Handle<v8::Context> context) {
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "getFoxxmasterSince"),
JS_GetFoxxmasterSince);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "idOfPrimary"),
JS_IdOfPrimaryServerState);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "javaScriptPath"),
JS_JavaScriptPathServerState);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "initialized"),
JS_InitializedServerState);
TRI_AddMethodVocbase(isolate, rt,
@ -2056,9 +2028,6 @@ void TRI_InitV8Cluster(v8::Isolate* isolate, v8::Handle<v8::Context> context) {
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "role"), JS_RoleServerState);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "setRole"),
JS_SetRoleServerState, true);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "redetermineRole"),
JS_RedetermineRoleServerState, true);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "status"), JS_StatusServerState);
v8g->ServerStateTempl.Reset(isolate, rt);

View File

@ -26,7 +26,8 @@
const jsunity = require('jsunity');
const internal = require('internal');
const fs = require('fs');
const console = require('console');
const expect = require('chai').expect;
const arangosh = require('@arangodb/arangosh');
const crypto = require('@arangodb/crypto');
@ -74,12 +75,12 @@ function getUrl(endpoint) {
function baseUrl() {
return getUrl(arango.getEndpoint());
};
}
function connectToServer(leader) {
arango.reconnect(leader, "_system", "root", "");
db._flushCache();
};
}
// getEndponts works with any server
function getClusterEndpoints() {
@ -477,7 +478,7 @@ function ActiveFailoverSuite() {
assertTrue(checkInSync(currentLead, servers));
assertEqual(checkData(currentLead), 10000);
}
},
// Try to cleanup everything that was created
/*testCleanup: function () {
@ -494,6 +495,26 @@ function ActiveFailoverSuite() {
assertTrue(checkInSync(lead, servers));
}*/
// Regression test. This endpoint was broken due to added checks in v8-cluster.cpp,
// which allowed certain calls only in cluster mode, but not in active failover.
testClusterHealth: function () {
console.warn({currentLead: getUrl(currentLead)});
const res = request.get({
url: getUrl(currentLead) + "/_admin/cluster/health",
auth: {
bearer: jwtRoot,
},
timeout: 30
});
console.warn(JSON.stringify(res));
console.warn(res.json);
expect(res).to.be.an.instanceof(request.Response);
// expect(res).to.be.have.property('statusCode', 200);
expect(res).to.have.property('json');
expect(res.json).to.include({error: false, code: 200});
expect(res.json).to.have.property('Health');
},
};
}