diff --git a/CHANGELOG b/CHANGELOG index 4d400553f9..4102675669 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ v3.5.1 (XXXX-XX-XX) ------------------- +* Fixed "ArangoDB is not running in cluster mode" errors in active failover setups. + This affected at least /_admin/cluster/health. + * Made the mechanism in the Web UI of replacing and upgrading a foxx app more clear. * Fixed the AQL sort-limit optimization which was applied in some cases it should diff --git a/arangod/Cluster/v8-cluster.cpp b/arangod/Cluster/v8-cluster.cpp index 23f8f02e86..a8fa4b407f 100644 --- a/arangod/Cluster/v8-cluster.cpp +++ b/arangod/Cluster/v8-cluster.cpp @@ -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 const& args, AgencyCommResult const& result) { v8::Isolate* isolate = args.GetIsolate(); @@ -102,7 +116,7 @@ static void JS_CasAgency(v8::FunctionCallbackInfo 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 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()"); @@ -211,7 +225,7 @@ static void JS_IncreaseVersionAgency(v8::FunctionCallbackInfo 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()"); @@ -237,7 +251,7 @@ static void JS_GetAgency(v8::FunctionCallbackInfo 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()"); @@ -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 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(, )"); @@ -376,7 +390,7 @@ static void JS_SetAgency(v8::FunctionCallbackInfo 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(, , )"); @@ -415,7 +429,7 @@ static void JS_Agency(v8::FunctionCallbackInfo 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 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 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(, )"); @@ -540,7 +554,7 @@ static void JS_VersionAgency(v8::FunctionCallbackInfo 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 TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); - ONLY_IN_CLUSTER + onlyInCluster(); + if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("doesDatabaseExist()"); } @@ -583,12 +598,13 @@ static void JS_DoesDatabaseExistClusterInfo(v8::FunctionCallbackInfo static void JS_Databases(v8::FunctionCallbackInfo 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 res = ClusterInfo::instance()->databases(true); v8::Handle a = v8::Array::New(isolate, (int)res.size()); std::vector::iterator it; @@ -608,7 +624,8 @@ static void JS_FlushClusterInfo(v8::FunctionCallbackInfo 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 TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); - ONLY_IN_CLUSTER + onlyInCluster(); + if (args.Length() != 2) { TRI_V8_THROW_EXCEPTION_USAGE( "getCollectionInfo(, )"); @@ -698,7 +716,8 @@ static void JS_GetCollectionInfoCurrentClusterInfo(v8::FunctionCallbackInfo, , )"); @@ -774,7 +793,8 @@ static void JS_GetResponsibleServerClusterInfo(v8::FunctionCallbackInfo)"); } @@ -799,7 +819,8 @@ static void JS_GetResponsibleShardClusterInfo(v8::FunctionCallbackInfo 3) { TRI_V8_THROW_EXCEPTION_USAGE( "getResponsibleShard(, , " @@ -862,7 +883,8 @@ static void JS_GetServerEndpointClusterInfo(v8::FunctionCallbackInfo TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); - ONLY_IN_CLUSTER + onlyInCluster(); + if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("getServerEndpoint()"); } @@ -882,7 +904,8 @@ static void JS_GetServerNameClusterInfo(v8::FunctionCallbackInfo 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()"); } @@ -902,7 +925,8 @@ static void JS_GetDBServers(v8::FunctionCallbackInfo 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()"); } @@ -943,7 +967,7 @@ static void JS_GetCoordinators(v8::FunctionCallbackInfo 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()"); @@ -1023,7 +1047,8 @@ static void JS_IdServerState(v8::FunctionCallbackInfo 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()"); } @@ -1111,40 +1136,6 @@ static void JS_GetFoxxmasterSince(v8::FunctionCallbackInfo const& arg TRI_V8_TRY_CATCH_END } -//////////////////////////////////////////////////////////////////////////////// -/// @brief return the primary servers id (only for secondaries) -//////////////////////////////////////////////////////////////////////////////// - -static void JS_IdOfPrimaryServerState(v8::FunctionCallbackInfo 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 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 //////////////////////////////////////////////////////////////////////////////// @@ -1226,29 +1217,6 @@ static void JS_SetRoleServerState(v8::FunctionCallbackInfo const& arg TRI_V8_TRY_CATCH_END } -//////////////////////////////////////////////////////////////////////////////// -/// @brief redetermines the role from the agency -//////////////////////////////////////////////////////////////////////////////// - -static void JS_RedetermineRoleServerState(v8::FunctionCallbackInfo 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 //////////////////////////////////////////////////////////////////////////////// @@ -1285,7 +1253,8 @@ static void PrepareClusterCommRequest(v8::FunctionCallbackInfo const& v8::Local context = isolate->GetCurrentContext(); TRI_V8_CURRENT_GLOBALS_AND_SCOPE; - ONLY_IN_CLUSTER + onlyInClusterOrActiveFailover(); + TRI_ASSERT(args.Length() >= 4); reqType = arangodb::rest::RequestType::GET; @@ -1522,7 +1491,8 @@ static void Return_PrepareClusterCommResultForJS(v8::FunctionCallbackInfo 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( @@ -1579,7 +1549,8 @@ static void JS_AsyncRequest(v8::FunctionCallbackInfo const& args) { static void JS_SyncRequest(v8::FunctionCallbackInfo 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( @@ -1640,7 +1611,8 @@ static void JS_SyncRequest(v8::FunctionCallbackInfo const& args) { static void JS_Enquire(v8::FunctionCallbackInfo 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)"); @@ -1672,7 +1644,8 @@ static void JS_Wait(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::Local context = isolate->GetCurrentContext(); TRI_V8_CURRENT_GLOBALS_AND_SCOPE; - ONLY_IN_CLUSTER + + onlyInClusterOrActiveFailover(); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("wait(obj)"); @@ -1737,7 +1710,8 @@ static void JS_Drop(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::Local context = isolate->GetCurrentContext(); TRI_V8_CURRENT_GLOBALS_AND_SCOPE; - ONLY_IN_CLUSTER + + onlyInCluster(); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("drop(obj)"); @@ -1838,7 +1812,8 @@ static void JS_ClusterDownload(v8::FunctionCallbackInfo const& args) static void JS_GetShardDistribution(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); - ONLY_IN_CLUSTER + + onlyInCluster(); v8::HandleScope scope(isolate); auto& vocbase = GetContextVocBase(isolate); @@ -1857,7 +1832,8 @@ static void JS_GetShardDistribution(v8::FunctionCallbackInfo const& a static void JS_GetCollectionShardDistribution(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); - ONLY_IN_CLUSTER + + onlyInCluster(); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE( @@ -2010,11 +1986,6 @@ void TRI_InitV8Cluster(v8::Isolate* isolate, v8::Handle 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, @@ -2023,9 +1994,6 @@ void TRI_InitV8Cluster(v8::Isolate* isolate, v8::Handle 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); diff --git a/tests/js/client/active-failover/basic.js b/tests/js/client/active-failover/basic.js index 1356df8f0a..2a729ff5c1 100644 --- a/tests/js/client/active-failover/basic.js +++ b/tests/js/client/active-failover/basic.js @@ -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'); + }, + }; }