//////////////////////////////////////////////////////////////////////////////// /// 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 Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// #include "v8-replication.h" #include "ApplicationFeatures/ApplicationServer.h" #include "Basics/ReadLocker.h" #include "Cluster/ClusterFeature.h" #include "Logger/Logger.h" #include "Replication/DatabaseTailingSyncer.h" #include "Replication/DatabaseInitialSyncer.h" #include "Replication/DatabaseReplicationApplier.h" #include "Replication/GlobalInitialSyncer.h" #include "Replication/GlobalReplicationApplier.h" #include "Replication/ReplicationApplierConfiguration.h" #include "Replication/ReplicationFeature.h" #include "Rest/Version.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/ServerIdFeature.h" #include "StorageEngine/EngineSelectorFeature.h" #include "StorageEngine/StorageEngine.h" #include "Transaction/V8Context.h" #include "Utils/DatabaseGuard.h" #include "V8/v8-conv.h" #include "V8/v8-globals.h" #include "V8/v8-utils.h" #include "V8/v8-vpack.h" #include "V8Server/v8-vocbaseprivate.h" #include #include #include #include using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::rest; //////////////////////////////////////////////////////////////////////////////// /// @brief get the state of the replication logger //////////////////////////////////////////////////////////////////////////////// static void JS_StateLoggerReplication( v8::FunctionCallbackInfo const& args) { // FIXME: use code in RestReplicationHandler and get rid of storage-engine // depended code here // TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); StorageEngine* engine = EngineSelectorFeature::ENGINE; v8::Handle result = v8::Object::New(isolate); VPackBuilder builder; auto res = engine->createLoggerState(nullptr,builder); if(res.fail()){ TRI_V8_THROW_EXCEPTION(res); } v8::HandleresultValue = TRI_VPackToV8(isolate, builder.slice()); result = v8::Handle::Cast(resultValue); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief get the tick ranges that can be provided by the replication logger //////////////////////////////////////////////////////////////////////////////// static void JS_TickRangesLoggerReplication( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); v8::Handle result; VPackBuilder builder; Result res = EngineSelectorFeature::ENGINE->createTickRanges(builder); if (res.fail()) { TRI_V8_THROW_EXCEPTION(res); } v8::HandleresultValue = TRI_VPackToV8(isolate, builder.slice()); result = v8::Handle::Cast(resultValue); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief get the first tick that can be provided by the replication logger //////////////////////////////////////////////////////////////////////////////// static void JS_FirstTickLoggerReplication( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_voc_tick_t tick = UINT64_MAX; Result res = EngineSelectorFeature::ENGINE->firstTick(tick); if(res.fail()){ TRI_V8_THROW_EXCEPTION(res); } if (tick == UINT64_MAX) { TRI_V8_RETURN(v8::Null(isolate)); } TRI_V8_RETURN(TRI_V8UInt64String(isolate, tick)); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief get the last WAL entries //////////////////////////////////////////////////////////////////////////////// static void JS_LastLoggerReplication( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (args.Length() != 2) { TRI_V8_THROW_EXCEPTION_USAGE("REPLICATION_LOGGER_LAST(, )"); } TRI_voc_tick_t tickStart = TRI_ObjectToUInt64(args[0], true); TRI_voc_tick_t tickEnd = TRI_ObjectToUInt64(args[1], true); if (tickEnd <= tickStart) { TRI_V8_THROW_EXCEPTION_USAGE("tickStart < tickEnd"); } auto transactionContext = transaction::V8Context::Create(vocbase, false); auto builderSPtr = std::make_shared(); Result res = EngineSelectorFeature::ENGINE->lastLogger( vocbase, transactionContext, tickStart, tickEnd, builderSPtr); v8::Handle result; if(res.fail()){ result = v8::Null(isolate); TRI_V8_THROW_EXCEPTION(res); } result = TRI_VPackToV8(isolate, builderSPtr->slice(), transactionContext->getVPackOptions()); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } enum ApplierType { APPLIER_DATABASE, APPLIER_GLOBAL }; //////////////////////////////////////////////////////////////////////////////// /// @brief sync data from a remote master //////////////////////////////////////////////////////////////////////////////// static void SynchronizeReplication( v8::FunctionCallbackInfo const& args, ApplierType applierType) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 1 || !args[0]->IsObject()) { TRI_V8_THROW_EXCEPTION_USAGE("synchronize()"); } // treat the argument as an object from now on v8::Handle object = v8::Handle::Cast(args[0]); VPackBuilder builder; int res = TRI_V8ToVPack(isolate, builder, args[0], false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::string databaseName; if (applierType == APPLIER_DATABASE) { databaseName = vocbase->name(); } bool keepBarrier = false; if (object->Has(TRI_V8_ASCII_STRING(isolate, "keepBarrier"))) { keepBarrier = TRI_ObjectToBoolean(object->Get(TRI_V8_ASCII_STRING(isolate, "keepBarrier"))); } ReplicationApplierConfiguration configuration = ReplicationApplierConfiguration::fromVelocyPack(builder.slice(), databaseName); configuration.validate(); v8::Handle result = v8::Object::New(isolate); std::unique_ptr syncer; if (applierType == APPLIER_DATABASE) { // database-specific synchronization syncer.reset(new DatabaseInitialSyncer(*vocbase, configuration)); if (object->Has(TRI_V8_ASCII_STRING(isolate, "leaderId"))) { syncer->setLeaderId(TRI_ObjectToString(object->Get(TRI_V8_ASCII_STRING(isolate, "leaderId")))); } } else if (applierType == APPLIER_GLOBAL) { configuration._skipCreateDrop = false; syncer.reset(new GlobalInitialSyncer(configuration)); } else { TRI_ASSERT(false); } try { Result r = syncer->run(configuration._incremental); if (r.fail()) { LOG_TOPIC(ERR, Logger::REPLICATION) << "initial sync failed for database '" << vocbase->name() << "': " << r.errorMessage(); TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), "cannot sync from remote endpoint: " + r.errorMessage() + ". last progress message was '" + syncer->progress() + "'"); } if (keepBarrier) { result->Set(TRI_V8_ASCII_STRING(isolate, "barrierId"), TRI_V8UInt64String(isolate, syncer->stealBarrier())); } result->Set(TRI_V8_ASCII_STRING(isolate, "lastLogTick"), TRI_V8UInt64String(isolate, syncer->getLastLogTick())); std::map::const_iterator it; std::map const& c = syncer->getProcessedCollections(); uint32_t j = 0; v8::Handle collections = v8::Array::New(isolate); for (it = c.begin(); it != c.end(); ++it) { std::string const cidString = StringUtils::itoa((*it).first); v8::Handle ci = v8::Object::New(isolate); ci->Set(TRI_V8_ASCII_STRING(isolate, "id"), TRI_V8_STD_STRING(isolate, cidString)); ci->Set(TRI_V8_ASCII_STRING(isolate, "name"), TRI_V8_STD_STRING(isolate, (*it).second)); collections->Set(j++, ci); } result->Set(TRI_V8_ASCII_STRING(isolate, "collections"), collections); } catch (arangodb::basics::Exception const& ex) { TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), std::string("cannot sync from remote endpoint: ") + ex.what() + ". last progress message was '" + syncer->progress() + "'"); } catch (std::exception const& ex) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, std::string("cannot sync from remote endpoint: ") + ex.what() + ". last progress message was '" + syncer->progress() + "'"); } catch (...) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, std::string("cannot sync from remote endpoint: unknown exception. last progress message was '") + syncer->progress() + "'"); } // Now check forSynchronousReplication flag and tell ClusterInfo // about a new follower. TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } static void JS_SynchronizeReplication( v8::FunctionCallbackInfo const& args) { SynchronizeReplication(args, APPLIER_DATABASE); } static void JS_SynchronizeGlobalReplication( v8::FunctionCallbackInfo const& args) { SynchronizeReplication(args, APPLIER_GLOBAL); } /// @brief finalize the synchronization of a collection by tailing the WAL /// and filtering on the collection name until no more data is available static void JS_SynchronizeReplicationFinalize( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 1 || !args[0]->IsObject()) { TRI_V8_THROW_EXCEPTION_USAGE("syncCollectionFinalize()"); } // treat the argument as an object from now on v8::Handle object = v8::Handle::Cast(args[0]); VPackBuilder builder; int res = TRI_V8ToVPack(isolate, builder, args[0], false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } std::string database; if (object->Has(TRI_V8_ASCII_STRING(isolate, "database"))) { database = TRI_ObjectToString(object->Get(TRI_V8_ASCII_STRING(isolate, "database"))); } if (database.empty()) { TRI_V8_THROW_EXCEPTION_PARAMETER(" must be a valid database name"); } std::string collection; if (object->Has(TRI_V8_ASCII_STRING(isolate, "collection"))) { collection = TRI_ObjectToString(object->Get(TRI_V8_ASCII_STRING(isolate, "collection"))); } if (collection.empty()) { TRI_V8_THROW_EXCEPTION_PARAMETER(" must be a valid collection name"); } TRI_voc_tick_t fromTick = 0; if (object->Has(TRI_V8_ASCII_STRING(isolate, "from"))) { fromTick = TRI_ObjectToUInt64(object->Get(TRI_V8_ASCII_STRING(isolate, "from")), true); } if (fromTick == 0) { TRI_V8_THROW_EXCEPTION_PARAMETER(" must be a valid start tick"); } ReplicationApplierConfiguration configuration = ReplicationApplierConfiguration::fromVelocyPack(builder.slice(), database); // will throw if invalid configuration.validate(); DatabaseGuard guard(database); DatabaseTailingSyncer syncer(guard.database(), configuration, fromTick, true, 0); if (object->Has(TRI_V8_ASCII_STRING(isolate, "leaderId"))) { syncer.setLeaderId(TRI_ObjectToString(object->Get(TRI_V8_ASCII_STRING(isolate, "leaderId")))); } v8::Handle result = v8::Object::New(isolate); Result r; try { r = syncer.syncCollectionFinalize(collection); } catch (arangodb::basics::Exception const& ex) { r = Result(ex.code(), ex.what()); } catch (std::exception const& ex) { r = Result(TRI_ERROR_INTERNAL, ex.what()); } catch (...) { r = Result(TRI_ERROR_INTERNAL, "unknown exception"); } if (r.fail()) { LOG_TOPIC(ERR, Logger::REPLICATION) << "syncCollectionFinalize failed: " << r.errorMessage(); std::string errorMsg = std::string("cannot sync data for shard '") + collection + "' from remote endpoint: " + r.errorMessage(); TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), errorMsg); } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief return the server's id //////////////////////////////////////////////////////////////////////////////// static void JS_ServerIdReplication( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); std::string const serverId = StringUtils::itoa(ServerIdFeature::getId()); TRI_V8_RETURN_STD_STRING(serverId); TRI_V8_TRY_CATCH_END } static ReplicationApplier* getContinuousApplier(v8::Isolate* isolate, ApplierType applierType) { ReplicationApplier* applier = nullptr; if (applierType == APPLIER_DATABASE) { // database-specific applier TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } applier = vocbase->replicationApplier(); } else { // applier type global auto replicationFeature = application_features::ApplicationServer::getFeature("Replication"); applier = replicationFeature->globalReplicationApplier(); } if (applier == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unable to find replicationApplier"); } return applier; } //////////////////////////////////////////////////////////////////////////////// /// @brief configure the replication applier manually //////////////////////////////////////////////////////////////////////////////// static void ConfigureApplierReplication(v8::FunctionCallbackInfo const& args, ApplierType applierType) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); ReplicationApplier* applier = getContinuousApplier(isolate, applierType); // get current configuration ReplicationApplierConfiguration configuration = applier->configuration(); if (args.Length() == 0) { // no argument: return the current configuration VPackBuilder builder; builder.openObject(); configuration.toVelocyPack(builder, true, true); builder.close(); v8::Handle result = TRI_VPackToV8(isolate, builder.slice()); TRI_V8_RETURN(result); } else { // set the configuration if (args.Length() != 1 || !args[0]->IsObject()) { TRI_V8_THROW_EXCEPTION_USAGE( "properties()"); } VPackBuilder builder; int res = TRI_V8ToVPack(isolate, builder, args[0], false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } std::string databaseName; if (applierType == APPLIER_DATABASE) { TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } databaseName = vocbase->name(); } // merge the passed configuration into the existing one configuration = ReplicationApplierConfiguration::fromVelocyPack(configuration, builder.slice(), databaseName); // will throw if invalid configuration.validate(); // finally store the new configuration applier->reconfigure(configuration); // and return it builder.clear(); builder.openObject(); configuration.toVelocyPack(builder, true, true); builder.close(); v8::Handle result = TRI_VPackToV8(isolate, builder.slice()); TRI_V8_RETURN(result); } TRI_V8_TRY_CATCH_END } static void JS_ConfigureApplierReplication( v8::FunctionCallbackInfo const& args) { ConfigureApplierReplication(args, APPLIER_DATABASE); } static void JS_ConfigureGlobalApplierReplication( v8::FunctionCallbackInfo const& args) { ConfigureApplierReplication(args, APPLIER_GLOBAL); } //////////////////////////////////////////////////////////////////////////////// /// @brief start the replication applier manually //////////////////////////////////////////////////////////////////////////////// static void StartApplierReplication(v8::FunctionCallbackInfo const& args, ApplierType applierType) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() > 2) { TRI_V8_THROW_EXCEPTION_USAGE("start()"); } TRI_voc_tick_t initialTick = 0; bool useTick = false; if (args.Length() >= 1) { initialTick = TRI_ObjectToUInt64(args[0], true); useTick = true; } TRI_voc_tick_t barrierId = 0; if (args.Length() >= 2) { barrierId = TRI_ObjectToUInt64(args[1], true); } ReplicationApplier* applier = getContinuousApplier(isolate, applierType); applier->startTailing(initialTick, useTick, barrierId); TRI_V8_RETURN_TRUE(); TRI_V8_TRY_CATCH_END } static void JS_StartApplierReplication( v8::FunctionCallbackInfo const& args) { StartApplierReplication(args, APPLIER_DATABASE); } static void JS_StartGlobalApplierReplication( v8::FunctionCallbackInfo const& args) { StartApplierReplication(args, APPLIER_GLOBAL); } //////////////////////////////////////////////////////////////////////////////// /// @brief shuts down the replication applier manually //////////////////////////////////////////////////////////////////////////////// static void StopApplierReplication(v8::FunctionCallbackInfo const& args, ApplierType applierType) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("stop()"); } ReplicationApplier* applier = getContinuousApplier(isolate, applierType); applier->stopAndJoin(); TRI_V8_RETURN_TRUE(); TRI_V8_TRY_CATCH_END } static void JS_StopApplierReplication( v8::FunctionCallbackInfo const& args) { StopApplierReplication(args, APPLIER_DATABASE); } static void JS_StopGlobalApplierReplication( v8::FunctionCallbackInfo const& args) { StopApplierReplication(args, APPLIER_GLOBAL); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the state of the replication applier //////////////////////////////////////////////////////////////////////////////// static void StateApplierReplication(v8::FunctionCallbackInfo const& args, ApplierType applierType) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("state()"); } ReplicationApplier* applier = getContinuousApplier(isolate, applierType); VPackBuilder builder; builder.openObject(); applier->toVelocyPack(builder); builder.close(); v8::Handle result = TRI_VPackToV8(isolate, builder.slice()); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } static void JS_StateApplierReplication( v8::FunctionCallbackInfo const& args) { StateApplierReplication(args, APPLIER_DATABASE); } static void JS_StateGlobalApplierReplication( v8::FunctionCallbackInfo const& args) { StateApplierReplication(args, APPLIER_GLOBAL); } //////////////////////////////////////////////////////////////////////////////// /// @brief stop the replication applier and "forget" all state //////////////////////////////////////////////////////////////////////////////// static void ForgetApplierReplication(v8::FunctionCallbackInfo const& args, ApplierType applierType) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("forget()"); } ReplicationApplier* applier = getContinuousApplier(isolate, applierType); applier->forget(); TRI_V8_RETURN_TRUE(); TRI_V8_TRY_CATCH_END } static void JS_ForgetApplierReplication( v8::FunctionCallbackInfo const& args) { ForgetApplierReplication(args, APPLIER_DATABASE); } static void JS_ForgetGlobalApplierReplication( v8::FunctionCallbackInfo const& args) { ForgetApplierReplication(args, APPLIER_GLOBAL); } static void JS_FailoverEnabledGlobalApplierReplication( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); auto replicationFeature = ReplicationFeature::INSTANCE; if (replicationFeature != nullptr && replicationFeature->isActiveFailoverEnabled()) { TRI_V8_RETURN_TRUE(); } TRI_V8_RETURN_FALSE(); TRI_V8_TRY_CATCH_END } void TRI_InitV8Replication(v8::Isolate* isolate, v8::Handle context, TRI_vocbase_t* vocbase, size_t threadNumber, TRI_v8_global_t* v8g) { // replication functions. not intended to be used by end users // logger functions TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_LOGGER_STATE"), JS_StateLoggerReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_LOGGER_LAST"), JS_LastLoggerReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_LOGGER_TICK_RANGES"), JS_TickRangesLoggerReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_LOGGER_FIRST_TICK"), JS_FirstTickLoggerReplication, true); // applier functions TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_APPLIER_CONFIGURE"), JS_ConfigureApplierReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_APPLIER_CONFIGURE"), JS_ConfigureGlobalApplierReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_APPLIER_START"), JS_StartApplierReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_APPLIER_START"), JS_StartGlobalApplierReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_APPLIER_STOP"), JS_StopApplierReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_APPLIER_STOP"), JS_StopGlobalApplierReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_APPLIER_STATE"), JS_StateApplierReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_APPLIER_STATE"), JS_StateGlobalApplierReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_APPLIER_FORGET"), JS_ForgetApplierReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_APPLIER_FORGET"), JS_ForgetGlobalApplierReplication, true); TRI_AddGlobalFunctionVocbase( isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_APPLIER_FAILOVER_ENABLED"), JS_FailoverEnabledGlobalApplierReplication, true); // other functions TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_SYNCHRONIZE"), JS_SynchronizeReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "GLOBAL_REPLICATION_SYNCHRONIZE"), JS_SynchronizeGlobalReplication, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_SYNCHRONIZE_FINALIZE"), JS_SynchronizeReplicationFinalize, true); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "REPLICATION_SERVER_ID"), JS_ServerIdReplication, true); }