mirror of https://gitee.com/bigwinds/arangodb
725 lines
25 KiB
C++
725 lines
25 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 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 <velocypack/Builder.h>
|
|
#include <velocypack/Parser.h>
|
|
#include <velocypack/Slice.h>
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
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<v8::Value> 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<v8::Object> result = v8::Object::New(isolate);
|
|
|
|
VPackBuilder builder;
|
|
auto res = engine->createLoggerState(nullptr,builder);
|
|
if(res.fail()){
|
|
TRI_V8_THROW_EXCEPTION(res);
|
|
}
|
|
v8::Handle<v8::Value>resultValue = TRI_VPackToV8(isolate, builder.slice());
|
|
result = v8::Handle<v8::Object>::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<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
v8::Handle<v8::Array> result;
|
|
|
|
VPackBuilder builder;
|
|
Result res = EngineSelectorFeature::ENGINE->createTickRanges(builder);
|
|
if (res.fail()) {
|
|
TRI_V8_THROW_EXCEPTION(res);
|
|
}
|
|
|
|
v8::Handle<v8::Value>resultValue = TRI_VPackToV8(isolate, builder.slice());
|
|
result = v8::Handle<v8::Array>::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<v8::Value> 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<TRI_voc_tick_t>(isolate, tick));
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the last WAL entries
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void JS_LastLoggerReplication( v8::FunctionCallbackInfo<v8::Value> 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(<fromTick>, <toTick>)");
|
|
}
|
|
|
|
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<VPackBuilder>();
|
|
Result res = EngineSelectorFeature::ENGINE->lastLogger(
|
|
vocbase, transactionContext, tickStart, tickEnd, builderSPtr);
|
|
|
|
v8::Handle<v8::Value> 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<v8::Value> 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(<configuration>)");
|
|
}
|
|
|
|
// treat the argument as an object from now on
|
|
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::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<v8::Object> result = v8::Object::New(isolate);
|
|
std::unique_ptr<InitialSyncer> 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<TRI_voc_tick_t>(isolate, syncer->stealBarrier()));
|
|
}
|
|
|
|
result->Set(TRI_V8_ASCII_STRING(isolate, "lastLogTick"),
|
|
TRI_V8UInt64String<TRI_voc_tick_t>(isolate, syncer->getLastLogTick()));
|
|
|
|
std::map<TRI_voc_cid_t, std::string>::const_iterator it;
|
|
std::map<TRI_voc_cid_t, std::string> const& c =
|
|
syncer->getProcessedCollections();
|
|
|
|
uint32_t j = 0;
|
|
v8::Handle<v8::Array> collections = v8::Array::New(isolate);
|
|
for (it = c.begin(); it != c.end(); ++it) {
|
|
std::string const cidString = StringUtils::itoa((*it).first);
|
|
|
|
v8::Handle<v8::Object> 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<v8::Value> const& args) {
|
|
SynchronizeReplication(args, APPLIER_DATABASE);
|
|
}
|
|
|
|
static void JS_SynchronizeGlobalReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> 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<v8::Value> 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(<configure>)");
|
|
}
|
|
|
|
// treat the argument as an object from now on
|
|
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::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("<database> 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("<collection> 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("<from> 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<v8::Object> 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<v8::Value> 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<ReplicationFeature>("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<v8::Value> 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<v8::Value> 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(<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<v8::Value> result = TRI_VPackToV8(isolate, builder.slice());
|
|
|
|
TRI_V8_RETURN(result);
|
|
}
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
static void JS_ConfigureApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
ConfigureApplierReplication(args, APPLIER_DATABASE);
|
|
}
|
|
|
|
static void JS_ConfigureGlobalApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
ConfigureApplierReplication(args, APPLIER_GLOBAL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief start the replication applier manually
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void StartApplierReplication(v8::FunctionCallbackInfo<v8::Value> const& args,
|
|
ApplierType applierType) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
|
|
if (args.Length() > 2) {
|
|
TRI_V8_THROW_EXCEPTION_USAGE("start(<from>)");
|
|
}
|
|
|
|
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<v8::Value> const& args) {
|
|
StartApplierReplication(args, APPLIER_DATABASE);
|
|
}
|
|
|
|
static void JS_StartGlobalApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
StartApplierReplication(args, APPLIER_GLOBAL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief shuts down the replication applier manually
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void StopApplierReplication(v8::FunctionCallbackInfo<v8::Value> 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<v8::Value> const& args) {
|
|
StopApplierReplication(args, APPLIER_DATABASE);
|
|
}
|
|
|
|
static void JS_StopGlobalApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
StopApplierReplication(args, APPLIER_GLOBAL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the state of the replication applier
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void StateApplierReplication(v8::FunctionCallbackInfo<v8::Value> 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<v8::Value> result = TRI_VPackToV8(isolate, builder.slice());
|
|
|
|
TRI_V8_RETURN(result);
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
static void JS_StateApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
StateApplierReplication(args, APPLIER_DATABASE);
|
|
}
|
|
|
|
static void JS_StateGlobalApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
StateApplierReplication(args, APPLIER_GLOBAL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief stop the replication applier and "forget" all state
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void ForgetApplierReplication(v8::FunctionCallbackInfo<v8::Value> 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<v8::Value> const& args) {
|
|
ForgetApplierReplication(args, APPLIER_DATABASE);
|
|
}
|
|
|
|
static void JS_ForgetGlobalApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
ForgetApplierReplication(args, APPLIER_GLOBAL);
|
|
}
|
|
|
|
static void JS_FailoverEnabledGlobalApplierReplication(
|
|
v8::FunctionCallbackInfo<v8::Value> 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<v8::Context> 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);
|
|
}
|