2350 lines
88 KiB
C++
2350 lines
88 KiB
C++
/*
|
|
* fdbserver.actor.cpp
|
|
*
|
|
* This source file is part of the FoundationDB open source project
|
|
*
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
// There's something in one of the files below that defines a macros
|
|
// a macro that makes boost interprocess break on Windows.
|
|
#define BOOST_DATE_TIME_NO_LIB
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
|
|
|
#include "fdbclient/ActorLineageProfiler.h"
|
|
#include "fdbclient/ClusterConnectionFile.h"
|
|
#include "fdbclient/IKnobCollection.h"
|
|
#include "fdbclient/NativeAPI.actor.h"
|
|
#include "fdbclient/SystemData.h"
|
|
#include "fdbclient/versions.h"
|
|
#include "fdbclient/BuildFlags.h"
|
|
#include "fdbclient/WellKnownEndpoints.h"
|
|
#include "fdbclient/SimpleIni.h"
|
|
#include "fdbrpc/AsyncFileCached.actor.h"
|
|
#include "fdbrpc/FlowProcess.actor.h"
|
|
#include "fdbrpc/Net2FileSystem.h"
|
|
#include "fdbrpc/PerfMetric.h"
|
|
#include "fdbrpc/fdbrpc.h"
|
|
#include "fdbrpc/simulator.h"
|
|
#include "fdbserver/ConflictSet.h"
|
|
#include "fdbserver/CoordinationInterface.h"
|
|
#include "fdbserver/CoroFlow.h"
|
|
#include "fdbserver/DataDistribution.actor.h"
|
|
#include "fdbserver/FDBExecHelper.actor.h"
|
|
#include "fdbserver/IKeyValueStore.h"
|
|
#include "fdbserver/MoveKeys.actor.h"
|
|
#include "fdbserver/NetworkTest.h"
|
|
#include "fdbserver/RemoteIKeyValueStore.actor.h"
|
|
#include "fdbserver/RestoreWorkerInterface.actor.h"
|
|
#include "fdbserver/ServerDBInfo.h"
|
|
#include "fdbserver/SimulatedCluster.h"
|
|
#include "fdbserver/Status.h"
|
|
#include "fdbserver/TesterInterface.actor.h"
|
|
#include "fdbserver/WorkerInterface.actor.h"
|
|
#include "fdbserver/pubsub.h"
|
|
#include "fdbserver/workloads/workloads.actor.h"
|
|
#include "flow/ArgParseUtil.h"
|
|
#include "flow/DeterministicRandom.h"
|
|
#include "flow/Platform.h"
|
|
#include "flow/ProtocolVersion.h"
|
|
#include "flow/SimpleOpt.h"
|
|
#include "flow/SystemMonitor.h"
|
|
#include "flow/TLSConfig.actor.h"
|
|
#include "flow/Tracing.h"
|
|
#include "flow/WriteOnlySet.h"
|
|
#include "flow/UnitTest.h"
|
|
#include "flow/FaultInjection.h"
|
|
#include "flow/flow.h"
|
|
#include "flow/network.h"
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
|
#include <execinfo.h>
|
|
#include <signal.h>
|
|
#include <sys/prctl.h>
|
|
#ifdef ALLOC_INSTRUMENTATION
|
|
#include <cxxabi.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#define NOMINMAX
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
#include "flow/actorcompiler.h" // This must be the last #include.
|
|
|
|
using namespace std::literals;
|
|
|
|
// clang-format off
|
|
enum {
|
|
OPT_CONNFILE, OPT_SEEDCONNFILE, OPT_SEEDCONNSTRING, OPT_ROLE, OPT_LISTEN, OPT_PUBLICADDR, OPT_DATAFOLDER, OPT_LOGFOLDER, OPT_PARENTPID, OPT_TRACER, OPT_NEWCONSOLE,
|
|
OPT_NOBOX, OPT_TESTFILE, OPT_RESTARTING, OPT_RESTORING, OPT_RANDOMSEED, OPT_KEY, OPT_MEMLIMIT, OPT_VMEMLIMIT, OPT_STORAGEMEMLIMIT, OPT_CACHEMEMLIMIT, OPT_MACHINEID,
|
|
OPT_DCID, OPT_MACHINE_CLASS, OPT_BUGGIFY, OPT_VERSION, OPT_BUILD_FLAGS, OPT_CRASHONERROR, OPT_HELP, OPT_NETWORKIMPL, OPT_NOBUFSTDOUT, OPT_BUFSTDOUTERR,
|
|
OPT_TRACECLOCK, OPT_NUMTESTERS, OPT_DEVHELP, OPT_ROLLSIZE, OPT_MAXLOGS, OPT_MAXLOGSSIZE, OPT_KNOB, OPT_UNITTESTPARAM, OPT_TESTSERVERS, OPT_TEST_ON_SERVERS, OPT_METRICSCONNFILE,
|
|
OPT_METRICSPREFIX, OPT_LOGGROUP, OPT_LOCALITY, OPT_IO_TRUST_SECONDS, OPT_IO_TRUST_WARN_ONLY, OPT_FILESYSTEM, OPT_PROFILER_RSS_SIZE, OPT_KVFILE,
|
|
OPT_TRACE_FORMAT, OPT_WHITELIST_BINPATH, OPT_BLOB_CREDENTIAL_FILE, OPT_CONFIG_PATH, OPT_USE_TEST_CONFIG_DB, OPT_FAULT_INJECTION, OPT_PROFILER, OPT_PRINT_SIMTIME, OPT_FLOW_PROCESS_NAME, OPT_FLOW_PROCESS_ENDPOINT
|
|
};
|
|
|
|
CSimpleOpt::SOption g_rgOptions[] = {
|
|
{ OPT_CONNFILE, "-C", SO_REQ_SEP },
|
|
{ OPT_CONNFILE, "--cluster-file", SO_REQ_SEP },
|
|
{ OPT_SEEDCONNFILE, "--seed-cluster-file", SO_REQ_SEP },
|
|
{ OPT_SEEDCONNSTRING, "--seed-connection-string", SO_REQ_SEP },
|
|
{ OPT_ROLE, "-r", SO_REQ_SEP },
|
|
{ OPT_ROLE, "--role", SO_REQ_SEP },
|
|
{ OPT_PUBLICADDR, "-p", SO_REQ_SEP },
|
|
{ OPT_PUBLICADDR, "--public-address", SO_REQ_SEP },
|
|
{ OPT_LISTEN, "-l", SO_REQ_SEP },
|
|
{ OPT_LISTEN, "--listen-address", SO_REQ_SEP },
|
|
#ifdef __linux__
|
|
{ OPT_FILESYSTEM, "--data-filesystem", SO_REQ_SEP },
|
|
{ OPT_PROFILER_RSS_SIZE, "--rsssize", SO_REQ_SEP },
|
|
#endif
|
|
{ OPT_DATAFOLDER, "-d", SO_REQ_SEP },
|
|
{ OPT_DATAFOLDER, "--datadir", SO_REQ_SEP },
|
|
{ OPT_LOGFOLDER, "-L", SO_REQ_SEP },
|
|
{ OPT_LOGFOLDER, "--logdir", SO_REQ_SEP },
|
|
{ OPT_ROLLSIZE, "-Rs", SO_REQ_SEP },
|
|
{ OPT_ROLLSIZE, "--logsize", SO_REQ_SEP },
|
|
{ OPT_MAXLOGS, "--maxlogs", SO_REQ_SEP },
|
|
{ OPT_MAXLOGSSIZE, "--maxlogssize", SO_REQ_SEP },
|
|
{ OPT_LOGGROUP, "--loggroup", SO_REQ_SEP },
|
|
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
|
|
{ OPT_TRACER, "--tracer", SO_REQ_SEP },
|
|
#ifdef _WIN32
|
|
{ OPT_NEWCONSOLE, "-n", SO_NONE },
|
|
{ OPT_NEWCONSOLE, "--newconsole", SO_NONE },
|
|
{ OPT_NOBOX, "-q", SO_NONE },
|
|
{ OPT_NOBOX, "--no-dialog", SO_NONE },
|
|
#endif
|
|
{ OPT_KVFILE, "--kvfile", SO_REQ_SEP },
|
|
{ OPT_TESTFILE, "-f", SO_REQ_SEP },
|
|
{ OPT_TESTFILE, "--testfile", SO_REQ_SEP },
|
|
{ OPT_RESTARTING, "-R", SO_NONE },
|
|
{ OPT_RESTARTING, "--restarting", SO_NONE },
|
|
{ OPT_RANDOMSEED, "-s", SO_REQ_SEP },
|
|
{ OPT_RANDOMSEED, "--seed", SO_REQ_SEP },
|
|
{ OPT_KEY, "-k", SO_REQ_SEP },
|
|
{ OPT_KEY, "--key", SO_REQ_SEP },
|
|
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
|
|
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
|
|
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
|
|
{ OPT_STORAGEMEMLIMIT, "-M", SO_REQ_SEP },
|
|
{ OPT_STORAGEMEMLIMIT, "--storage-memory", SO_REQ_SEP },
|
|
{ OPT_CACHEMEMLIMIT, "--cache-memory", SO_REQ_SEP },
|
|
{ OPT_MACHINEID, "-i", SO_REQ_SEP },
|
|
{ OPT_MACHINEID, "--machine-id", SO_REQ_SEP },
|
|
{ OPT_DCID, "-a", SO_REQ_SEP },
|
|
{ OPT_DCID, "--datacenter-id", SO_REQ_SEP },
|
|
{ OPT_MACHINE_CLASS, "-c", SO_REQ_SEP },
|
|
{ OPT_MACHINE_CLASS, "--class", SO_REQ_SEP },
|
|
{ OPT_BUGGIFY, "-b", SO_REQ_SEP },
|
|
{ OPT_BUGGIFY, "--buggify", SO_REQ_SEP },
|
|
{ OPT_VERSION, "-v", SO_NONE },
|
|
{ OPT_VERSION, "--version", SO_NONE },
|
|
{ OPT_BUILD_FLAGS, "--build-flags", SO_NONE },
|
|
{ OPT_CRASHONERROR, "--crash", SO_NONE },
|
|
{ OPT_NETWORKIMPL, "-N", SO_REQ_SEP },
|
|
{ OPT_NETWORKIMPL, "--network", SO_REQ_SEP },
|
|
{ OPT_NOBUFSTDOUT, "--unbufferedout", SO_NONE },
|
|
{ OPT_BUFSTDOUTERR, "--bufferedout", SO_NONE },
|
|
{ OPT_TRACECLOCK, "--traceclock", SO_REQ_SEP },
|
|
{ OPT_NUMTESTERS, "--num-testers", SO_REQ_SEP },
|
|
{ OPT_HELP, "-?", SO_NONE },
|
|
{ OPT_HELP, "-h", SO_NONE },
|
|
{ OPT_HELP, "--help", SO_NONE },
|
|
{ OPT_DEVHELP, "--dev-help", SO_NONE },
|
|
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
|
|
{ OPT_UNITTESTPARAM, "--test-", SO_REQ_SEP },
|
|
{ OPT_LOCALITY, "--locality-", SO_REQ_SEP },
|
|
{ OPT_TESTSERVERS, "--testservers", SO_REQ_SEP },
|
|
{ OPT_TEST_ON_SERVERS, "--testonservers", SO_NONE },
|
|
{ OPT_METRICSCONNFILE, "--metrics-cluster", SO_REQ_SEP },
|
|
{ OPT_METRICSPREFIX, "--metrics-prefix", SO_REQ_SEP },
|
|
{ OPT_IO_TRUST_SECONDS, "--io-trust-seconds", SO_REQ_SEP },
|
|
{ OPT_IO_TRUST_WARN_ONLY, "--io-trust-warn-only", SO_NONE },
|
|
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
|
|
{ OPT_WHITELIST_BINPATH, "--whitelist-binpath", SO_REQ_SEP },
|
|
{ OPT_BLOB_CREDENTIAL_FILE, "--blob-credential-file", SO_REQ_SEP },
|
|
{ OPT_CONFIG_PATH, "--config-path", SO_REQ_SEP },
|
|
{ OPT_USE_TEST_CONFIG_DB, "--use-test-config-db", SO_NONE },
|
|
{ OPT_FAULT_INJECTION, "-fi", SO_REQ_SEP },
|
|
{ OPT_FAULT_INJECTION, "--fault-injection", SO_REQ_SEP },
|
|
{ OPT_PROFILER, "--profiler-", SO_REQ_SEP },
|
|
{ OPT_PRINT_SIMTIME, "--print-sim-time", SO_NONE },
|
|
{ OPT_FLOW_PROCESS_NAME, "--process-name", SO_REQ_SEP },
|
|
{ OPT_FLOW_PROCESS_ENDPOINT, "--process-endpoint", SO_REQ_SEP },
|
|
|
|
#ifndef TLS_DISABLED
|
|
TLS_OPTION_FLAGS
|
|
#endif
|
|
|
|
SO_END_OF_OPTIONS
|
|
};
|
|
|
|
// clang-format on
|
|
|
|
extern void dsltest();
|
|
extern void pingtest();
|
|
extern void copyTest();
|
|
extern void versionedMapTest();
|
|
extern void createTemplateDatabase();
|
|
// FIXME: this really belongs in a header somewhere since it is actually used.
|
|
extern IPAddress determinePublicIPAutomatically(ClusterConnectionString& ccs);
|
|
|
|
extern const char* getSourceVersion();
|
|
|
|
extern void flushTraceFileVoid();
|
|
|
|
extern const int MAX_CLUSTER_FILE_BYTES;
|
|
|
|
#ifdef ALLOC_INSTRUMENTATION
|
|
extern uint8_t* g_extra_memory;
|
|
#endif
|
|
|
|
bool enableFailures = true;
|
|
|
|
#define test_assert(x) \
|
|
if (!(x)) { \
|
|
std::cout << "Test failed: " #x << std::endl; \
|
|
return false; \
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#include <sddl.h>
|
|
|
|
// It is your
|
|
// responsibility to properly initialize the
|
|
// structure and to free the structure's
|
|
// lpSecurityDescriptor member when you have
|
|
// finished using it. To free the structure's
|
|
// lpSecurityDescriptor member, call the
|
|
// LocalFree function.
|
|
BOOL CreatePermissiveReadWriteDACL(SECURITY_ATTRIBUTES* pSA) {
|
|
UNSTOPPABLE_ASSERT(pSA != nullptr);
|
|
|
|
TCHAR* szSD = TEXT("D:") // Discretionary ACL
|
|
TEXT("(A;OICI;GR;;;AU)") // Allow read/write/execute to authenticated users
|
|
TEXT("(A;OICI;GA;;;BA)"); // Allow full control to administrators
|
|
|
|
return ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
szSD, SDDL_REVISION_1, &(pSA->lpSecurityDescriptor), nullptr);
|
|
}
|
|
#endif
|
|
|
|
class WorldReadablePermissions {
|
|
public:
|
|
WorldReadablePermissions() {
|
|
#ifdef _WIN32
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = FALSE;
|
|
if (!CreatePermissiveReadWriteDACL(&sa)) {
|
|
TraceEvent("Win32DACLCreationFail").GetLastError();
|
|
throw platform_error();
|
|
}
|
|
permission.set_permissions(&sa);
|
|
#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__))
|
|
// There is nothing to do here, since the default permissions are fine
|
|
#else
|
|
#error Port me!
|
|
#endif
|
|
}
|
|
|
|
virtual ~WorldReadablePermissions() {
|
|
#ifdef _WIN32
|
|
LocalFree(sa.lpSecurityDescriptor);
|
|
#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__))
|
|
// There is nothing to do here, since the default permissions are fine
|
|
#else
|
|
#error Port me!
|
|
#endif
|
|
}
|
|
|
|
boost::interprocess::permissions permission;
|
|
|
|
private:
|
|
WorldReadablePermissions(const WorldReadablePermissions& rhs) {}
|
|
#ifdef _WIN32
|
|
SECURITY_ATTRIBUTES sa;
|
|
#endif
|
|
};
|
|
|
|
UID getSharedMemoryMachineId() {
|
|
// new UID to use if an existing one is not found
|
|
UID newUID = deterministicRandom()->randomUniqueID();
|
|
|
|
#if DEBUG_DETERMINISM
|
|
// Don't use shared memory if DEBUG_DETERMINISM is set
|
|
return newUID;
|
|
#else
|
|
UID* machineId = nullptr;
|
|
int numTries = 0;
|
|
|
|
// Permissions object defaults to 0644 on *nix, but on windows defaults to allowing access to only the creator.
|
|
// On windows, this means that we have to create an elaborate workaround for DACLs
|
|
WorldReadablePermissions p;
|
|
std::string sharedMemoryIdentifier = "fdbserver_shared_memory_id";
|
|
loop {
|
|
try {
|
|
// "0" is the default parameter "addr"
|
|
boost::interprocess::managed_shared_memory segment(
|
|
boost::interprocess::open_or_create, sharedMemoryIdentifier.c_str(), 1000, 0, p.permission);
|
|
machineId = segment.find_or_construct<UID>("machineId")(newUID);
|
|
if (!machineId)
|
|
criticalError(
|
|
FDB_EXIT_ERROR, "SharedMemoryError", "Could not locate or create shared memory - 'machineId'");
|
|
return *machineId;
|
|
} catch (boost::interprocess::interprocess_exception&) {
|
|
try {
|
|
// If the shared memory already exists, open it read-only in case it was created by another user
|
|
boost::interprocess::managed_shared_memory segment(boost::interprocess::open_read_only,
|
|
sharedMemoryIdentifier.c_str());
|
|
machineId = segment.find<UID>("machineId").first;
|
|
if (!machineId)
|
|
criticalError(FDB_EXIT_ERROR, "SharedMemoryError", "Could not locate shared memory - 'machineId'");
|
|
return *machineId;
|
|
} catch (boost::interprocess::interprocess_exception& ex) {
|
|
// Retry in case the shared memory was deleted in between the call to open_or_create and open_read_only
|
|
// Don't keep trying forever in case this is caused by some other problem
|
|
if (++numTries == 10)
|
|
criticalError(FDB_EXIT_ERROR,
|
|
"SharedMemoryError",
|
|
format("Could not open shared memory - %s", ex.what()).c_str());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ACTOR void failAfter(Future<Void> trigger, ISimulator::ProcessInfo* m = g_simulator.getCurrentProcess()) {
|
|
wait(trigger);
|
|
if (enableFailures) {
|
|
printf("Killing machine: %s at %f\n", m->address.toString().c_str(), now());
|
|
g_simulator.killProcess(m, ISimulator::KillInstantly);
|
|
}
|
|
}
|
|
|
|
void failAfter(Future<Void> trigger, Endpoint e) {
|
|
if (g_network == &g_simulator)
|
|
failAfter(trigger, g_simulator.getProcess(e));
|
|
}
|
|
|
|
ACTOR Future<Void> histogramReport() {
|
|
loop {
|
|
wait(delay(SERVER_KNOBS->HISTOGRAM_REPORT_INTERVAL));
|
|
|
|
GetHistogramRegistry().logReport();
|
|
}
|
|
}
|
|
|
|
void testSerializationSpeed() {
|
|
double tstart;
|
|
double build = 0, serialize = 0, deserialize = 0, copy = 0, deallocate = 0;
|
|
double bytes = 0;
|
|
double testBegin = timer();
|
|
for (int a = 0; a < 10000; a++) {
|
|
{
|
|
tstart = timer();
|
|
|
|
Arena batchArena;
|
|
VectorRef<CommitTransactionRef> batch;
|
|
batch.resize(batchArena, 1000);
|
|
for (int t = 0; t < batch.size(); t++) {
|
|
CommitTransactionRef& tr = batch[t];
|
|
tr.read_snapshot = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
tr.mutations.push_back_deep(
|
|
batchArena,
|
|
MutationRef(MutationRef::SetValue, LiteralStringRef("KeyABCDE"), LiteralStringRef("SomeValu")));
|
|
tr.mutations.push_back_deep(
|
|
batchArena,
|
|
MutationRef(MutationRef::ClearRange, LiteralStringRef("BeginKey"), LiteralStringRef("EndKeyAB")));
|
|
}
|
|
|
|
build += timer() - tstart;
|
|
|
|
tstart = timer();
|
|
|
|
BinaryWriter wr(IncludeVersion());
|
|
wr << batch;
|
|
|
|
bytes += wr.getLength();
|
|
|
|
serialize += timer() - tstart;
|
|
|
|
for (int i = 0; i < 1; i++) {
|
|
tstart = timer();
|
|
Arena arena;
|
|
StringRef data(arena, StringRef((const uint8_t*)wr.getData(), wr.getLength()));
|
|
copy += timer() - tstart;
|
|
|
|
tstart = timer();
|
|
ArenaReader rd(arena, data, IncludeVersion());
|
|
VectorRef<CommitTransactionRef> batch2;
|
|
rd >> arena >> batch2;
|
|
|
|
deserialize += timer() - tstart;
|
|
}
|
|
|
|
tstart = timer();
|
|
}
|
|
deallocate += timer() - tstart;
|
|
}
|
|
double elapsed = (timer() - testBegin);
|
|
printf("Test speed: %0.1f MB/sec (%0.0f/sec)\n", bytes / 1e6 / elapsed, 1000000 / elapsed);
|
|
printf(" Build: %0.1f MB/sec\n", bytes / 1e6 / build);
|
|
printf(" Serialize: %0.1f MB/sec\n", bytes / 1e6 / serialize);
|
|
printf(" Copy: %0.1f MB/sec\n", bytes / 1e6 / copy);
|
|
printf(" Deserialize: %0.1f MB/sec\n", bytes / 1e6 / deserialize);
|
|
printf(" Deallocate: %0.1f MB/sec\n", bytes / 1e6 / deallocate);
|
|
printf(" Bytes: %0.1f MB\n", bytes / 1e6);
|
|
printf("\n");
|
|
}
|
|
|
|
std::string toHTML(const StringRef& binaryString) {
|
|
std::string s;
|
|
|
|
for (int i = 0; i < binaryString.size(); i++) {
|
|
uint8_t c = binaryString[i];
|
|
if (c == '<')
|
|
s += "<";
|
|
else if (c == '>')
|
|
s += ">";
|
|
else if (c == '&')
|
|
s += "&";
|
|
else if (c == '"')
|
|
s += """;
|
|
else if (c == ' ')
|
|
s += " ";
|
|
else if (c > 32 && c < 127)
|
|
s += c;
|
|
else
|
|
s += format("<span class=\"binary\">[%02x]</span>", c);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
ACTOR Future<Void> dumpDatabase(Database cx, std::string outputFilename, KeyRange range = allKeys) {
|
|
try {
|
|
state Transaction tr(cx);
|
|
loop {
|
|
state FILE* output = fopen(outputFilename.c_str(), "wt");
|
|
try {
|
|
state KeySelectorRef iter = firstGreaterOrEqual(range.begin);
|
|
state Arena arena;
|
|
fprintf(output, "<html><head><style type=\"text/css\">.binary {color:red}</style></head><body>\n");
|
|
Version ver = wait(tr.getReadVersion());
|
|
fprintf(output, "<h3>Database version: %" PRId64 "</h3>", ver);
|
|
|
|
loop {
|
|
RangeResult results = wait(tr.getRange(iter, firstGreaterOrEqual(range.end), 1000));
|
|
for (int r = 0; r < results.size(); r++) {
|
|
std::string key = toHTML(results[r].key), value = toHTML(results[r].value);
|
|
fprintf(output, "<p>%s <b>:=</b> %s</p>\n", key.c_str(), value.c_str());
|
|
}
|
|
if (results.size() < 1000)
|
|
break;
|
|
iter = firstGreaterThan(KeyRef(arena, results[results.size() - 1].key));
|
|
}
|
|
fprintf(output, "</body></html>");
|
|
fclose(output);
|
|
TraceEvent("DatabaseDumped").detail("Filename", outputFilename);
|
|
return Void();
|
|
} catch (Error& e) {
|
|
fclose(output);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
} catch (Error& e) {
|
|
TraceEvent(SevError, "DumpDatabaseError").error(e).detail("Filename", outputFilename);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void memoryTest();
|
|
void skipListTest();
|
|
|
|
Future<Void> startSystemMonitor(std::string dataFolder,
|
|
Optional<Standalone<StringRef>> dcId,
|
|
Optional<Standalone<StringRef>> zoneId,
|
|
Optional<Standalone<StringRef>> machineId) {
|
|
initializeSystemMonitorMachineState(
|
|
SystemMonitorMachineState(dataFolder, dcId, zoneId, machineId, g_network->getLocalAddress().ip));
|
|
|
|
systemMonitor();
|
|
return recurring(&systemMonitor, SERVER_KNOBS->SYSTEM_MONITOR_FREQUENCY, TaskPriority::FlushTrace);
|
|
}
|
|
|
|
void testIndexedSet();
|
|
|
|
#ifdef _WIN32
|
|
void parentWatcher(void* parentHandle) {
|
|
HANDLE parent = (HANDLE)parentHandle;
|
|
int signal = WaitForSingleObject(parent, INFINITE);
|
|
CloseHandle(parentHandle);
|
|
if (signal == WAIT_OBJECT_0)
|
|
criticalError(FDB_EXIT_SUCCESS, "ParentProcessExited", "Parent process exited");
|
|
TraceEvent(SevError, "ParentProcessWaitFailed").detail("RetCode", signal).GetLastError();
|
|
}
|
|
#else
|
|
void* parentWatcher(void* arg) {
|
|
int* parent_pid = (int*)arg;
|
|
while (1) {
|
|
sleep(1);
|
|
if (getppid() != *parent_pid)
|
|
criticalError(FDB_EXIT_SUCCESS, "ParentProcessExited", "Parent process exited");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void printBuildInformation() {
|
|
printf("%s", jsonBuildInformation().c_str());
|
|
}
|
|
|
|
static void printVersion() {
|
|
printf("FoundationDB " FDB_VT_PACKAGE_NAME " (v" FDB_VT_VERSION ")\n");
|
|
printf("source version %s\n", getSourceVersion());
|
|
printf("protocol %" PRIx64 "\n", currentProtocolVersion.version());
|
|
}
|
|
|
|
static void printHelpTeaser(const char* name) {
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", name);
|
|
}
|
|
|
|
static void printOptionUsage(std::string option, std::string description) {
|
|
static const std::string OPTION_INDENT(" ");
|
|
static const std::string DESCRIPTION_INDENT(" ");
|
|
static const int WIDTH = 80;
|
|
|
|
boost::algorithm::trim(option);
|
|
boost::algorithm::trim(description);
|
|
|
|
std::string result = OPTION_INDENT + option + "\n";
|
|
|
|
std::stringstream sstream(description);
|
|
if (sstream.eof()) {
|
|
printf("%s", result.c_str());
|
|
return;
|
|
}
|
|
|
|
std::string currWord;
|
|
sstream >> currWord;
|
|
|
|
std::string currLine(DESCRIPTION_INDENT + ' ' + currWord);
|
|
int currLength = currLine.size();
|
|
|
|
while (!sstream.eof()) {
|
|
sstream >> currWord;
|
|
|
|
if (currLength + static_cast<int>(currWord.size()) + 1 > WIDTH) {
|
|
result += currLine + '\n';
|
|
currLine = DESCRIPTION_INDENT + ' ' + currWord;
|
|
} else {
|
|
currLine += ' ' + currWord;
|
|
}
|
|
currLength = currLine.size();
|
|
}
|
|
result += currLine + '\n';
|
|
|
|
printf("%s", result.c_str());
|
|
}
|
|
|
|
static void printUsage(const char* name, bool devhelp) {
|
|
printf("FoundationDB " FDB_VT_PACKAGE_NAME " (v" FDB_VT_VERSION ")\n");
|
|
printf("Usage: %s -p ADDRESS [OPTIONS]\n\n", name);
|
|
printOptionUsage("-p ADDRESS, --public-address ADDRESS",
|
|
" Public address, specified as `IP_ADDRESS:PORT' or `auto:PORT'.");
|
|
printOptionUsage("-l ADDRESS, --listen-address ADDRESS",
|
|
" Listen address, specified as `IP_ADDRESS:PORT' (defaults to"
|
|
" public address).");
|
|
printOptionUsage("-C CONNFILE, --cluster-file CONNFILE",
|
|
" The path of a file containing the connection string for the"
|
|
" FoundationDB cluster. The default is first the value of the"
|
|
" FDB_CLUSTER_FILE environment variable, then `./fdb.cluster',"
|
|
" then `" +
|
|
platform::getDefaultClusterFilePath() + "'.");
|
|
printOptionUsage("--seed-cluster-file SEEDCONNFILE",
|
|
" The path of a seed cluster file which will be used to connect"
|
|
" if the -C cluster file does not exist. If the server connects"
|
|
" successfully using the seed file, then it copies the file to"
|
|
" the -C file location.");
|
|
printOptionUsage("--seed-connection-string SEEDCONNSTRING",
|
|
" The path of a seed connection string which will be used to connect"
|
|
" if the -C cluster file does not exist. If the server connects"
|
|
" successfully using the seed string, then it copies the string to"
|
|
" the -C file location.");
|
|
#ifdef __linux__
|
|
printOptionUsage("--data-filesystem PATH",
|
|
" Turns on validation that all data files are written to a drive"
|
|
" mounted at the specified PATH. This checks that the device at PATH"
|
|
" is currently mounted and that any data files get written to the"
|
|
" same device.");
|
|
#endif
|
|
printOptionUsage("-d PATH, --datadir PATH",
|
|
" Store data files in the given folder (must be unique for each"
|
|
" fdbserver instance on a given machine).");
|
|
printOptionUsage("-L PATH, --logdir PATH", " Store log files in the given folder (default is `.').");
|
|
printOptionUsage("--logsize SIZE",
|
|
"Roll over to a new log file after the current log file"
|
|
" exceeds SIZE bytes. The default value is 10MiB.");
|
|
printOptionUsage("--maxlogs SIZE, --maxlogssize SIZE",
|
|
" Delete the oldest log file when the total size of all log"
|
|
" files exceeds SIZE bytes. If set to 0, old log files will not"
|
|
" be deleted. The default value is 100MiB.");
|
|
printOptionUsage("--loggroup LOG_GROUP",
|
|
" Sets the LogGroup field with the specified value for all"
|
|
" events in the trace output (defaults to `default').");
|
|
printOptionUsage("--trace-format FORMAT",
|
|
" Select the format of the log files. xml (the default) and json"
|
|
" are supported.");
|
|
printOptionUsage("--tracer TRACER",
|
|
" Select a tracer for transaction tracing. Currently disabled"
|
|
" (the default) and log_file are supported.");
|
|
printOptionUsage("-i ID, --machine-id ID",
|
|
" Machine and zone identifier key (up to 16 hex characters)."
|
|
" Defaults to a random value shared by all fdbserver processes"
|
|
" on this machine.");
|
|
printOptionUsage("-a ID, --datacenter-id ID", " Data center identifier key (up to 16 hex characters).");
|
|
printOptionUsage("--locality-LOCALITYKEY LOCALITYVALUE",
|
|
" Define a locality key. LOCALITYKEY is case-insensitive though"
|
|
" LOCALITYVALUE is not.");
|
|
printOptionUsage("-m SIZE, --memory SIZE",
|
|
" Resident memory limit. The default value is 8GiB. When specified"
|
|
" without a unit, MiB is assumed.");
|
|
printOptionUsage("--memory-vsize SIZE",
|
|
" Virtual memory limit. The default value is unlimited. When specified"
|
|
" without a unit, MiB is assumed.");
|
|
printOptionUsage("-M SIZE, --storage-memory SIZE",
|
|
" Maximum amount of memory used for storage. The default"
|
|
" value is 1GiB. When specified without a unit, MB is"
|
|
" assumed.");
|
|
printOptionUsage("--cache-memory SIZE",
|
|
" The amount of memory to use for caching disk pages."
|
|
" The default value is 2GiB. When specified without a unit,"
|
|
" MiB is assumed.");
|
|
printOptionUsage("-c CLASS, --class CLASS",
|
|
" Machine class (valid options are storage, transaction,"
|
|
" resolution, grv_proxy, commit_proxy, master, test, unset, stateless, log, router,"
|
|
" and cluster_controller).");
|
|
printOptionUsage("--profiler-",
|
|
"Set an actor profiler option. Supported options are:\n"
|
|
" collector -- None or FluentD (FluentD requires collector_endpoint to be set)\n"
|
|
" collector_endpoint -- IP:PORT of the fluentd server\n"
|
|
" collector_protocol -- UDP or TCP (default is UDP)");
|
|
#ifndef TLS_DISABLED
|
|
printf(TLS_HELP);
|
|
#endif
|
|
printOptionUsage("-v, --version", "Print version information and exit.");
|
|
printOptionUsage("-h, -?, --help", "Display this help and exit.");
|
|
if (devhelp) {
|
|
printf(" --build-flags Print build information and exit.\n");
|
|
printOptionUsage("-r ROLE, --role ROLE",
|
|
" Server role (valid options are fdbd, test, multitest,"
|
|
" simulation, networktestclient, networktestserver, restore"
|
|
" consistencycheck, kvfileintegritycheck, kvfilegeneratesums, kvfiledump, unittests)."
|
|
" The default is `fdbd'.");
|
|
#ifdef _WIN32
|
|
printOptionUsage("-n, --newconsole", " Create a new console.");
|
|
printOptionUsage("-q, --no-dialog", " Disable error dialog on crash.");
|
|
printOptionUsage("--parentpid PID", " Specify a process after whose termination to exit.");
|
|
#endif
|
|
printOptionUsage("-f TESTFILE, --testfile",
|
|
" Testfile to run, defaults to `tests/default.txt'. If role is `unittests', specifies which "
|
|
"unit tests to run as a search prefix.");
|
|
printOptionUsage("-R, --restarting", " Restart a previous simulation that was cleanly shut down.");
|
|
printOptionUsage("-s SEED, --seed SEED", " Random seed.");
|
|
printOptionUsage("-k KEY, --key KEY", "Target key for search role.");
|
|
printOptionUsage("--kvfile FILE",
|
|
"Input file (SQLite database file) for use by the 'kvfilegeneratesums', "
|
|
"'kvfileintegritycheck' and 'kvfiledump' roles.");
|
|
printOptionUsage("-b [on,off], --buggify [on,off]", " Sets Buggify system state, defaults to `off'.");
|
|
printOptionUsage("-fi [on,off], --fault-injection [on,off]", " Sets fault injection, defaults to `on'.");
|
|
printOptionUsage("--crash", "Crash on serious errors instead of continuing.");
|
|
printOptionUsage("-N NETWORKIMPL, --network NETWORKIMPL",
|
|
" Select network implementation, `net2' (default),"
|
|
" `net2-threadpool'.");
|
|
printOptionUsage("--unbufferedout", " Do not buffer stdout and stderr.");
|
|
printOptionUsage("--bufferedout", " Buffer stdout and stderr.");
|
|
printOptionUsage("--traceclock CLOCKIMPL",
|
|
" Select clock source for trace files, `now' (default) or"
|
|
" `realtime'.");
|
|
printOptionUsage("--num-testers NUM",
|
|
" A multitester will wait for NUM testers before starting"
|
|
" (defaults to 1).");
|
|
printOptionUsage("--test-PARAMNAME PARAMVALUE",
|
|
" Set a UnitTest named parameter to the given value. Names are case sensitive.");
|
|
#ifdef __linux__
|
|
printOptionUsage("--rsssize SIZE",
|
|
" Turns on automatic heap profiling when RSS memory size exceeds"
|
|
" the given threshold. fdbserver needs to be compiled with"
|
|
" USE_GPERFTOOLS flag in order to use this feature.");
|
|
#endif
|
|
printOptionUsage("--testservers ADDRESSES",
|
|
" The addresses of networktestservers"
|
|
" specified as ADDRESS:PORT,ADDRESS:PORT...");
|
|
printOptionUsage("--testonservers", " Testers are recruited on servers.");
|
|
printOptionUsage("--metrics-cluster CONNFILE",
|
|
" The cluster file designating where this process will"
|
|
" store its metric data. By default metrics will be stored"
|
|
" in the same database the process is participating in.");
|
|
printOptionUsage("--metrics-prefix PREFIX",
|
|
" The prefix where this process will store its metric data."
|
|
" Must be specified if using a different database for metrics.");
|
|
printOptionUsage("--knob-KNOBNAME KNOBVALUE", " Changes a database knob. KNOBNAME should be lowercase.");
|
|
printOptionUsage("--io-trust-seconds SECONDS",
|
|
" Sets the time in seconds that a read or write operation is allowed to take"
|
|
" before timing out with an error. If an operation times out, all future"
|
|
" operations on that file will fail with an error as well. Only has an effect"
|
|
" when using AsyncFileKAIO in Linux.");
|
|
printOptionUsage("--io-trust-warn-only",
|
|
" Instead of failing when an I/O operation exceeds io_trust_seconds, just"
|
|
" log a warning to the trace log. Has no effect if io_trust_seconds is unspecified.");
|
|
printf("\n"
|
|
"The 'kvfiledump' role dump all key-values from kvfile to stdout in binary format:\n"
|
|
"{key length}{key binary}{value length}{value binary}, length is 4 bytes int\n"
|
|
"(little endianness). This role takes 3 environment variables as parameters:\n"
|
|
" - FDB_DUMP_STARTKEY: start key for the dump, default is empty\n"
|
|
" - FDB_DUMP_ENDKEY: end key for the dump, default is \"\\xff\\xff\"\n"
|
|
" - FDB_DUMP_DEBUG: print key-values to stderr in escaped format\n");
|
|
} else {
|
|
printOptionUsage("--dev-help", "Display developer-specific help and exit.");
|
|
}
|
|
|
|
printf("\n"
|
|
"SIZE parameters may use one of the multiplicative suffixes B=1, KB=10^3,\n"
|
|
"KiB=2^10, MB=10^6, MiB=2^20, GB=10^9, GiB=2^30, TB=10^12, or TiB=2^40.\n");
|
|
}
|
|
|
|
extern bool g_crashOnError;
|
|
|
|
#if defined(ALLOC_INSTRUMENTATION) || defined(ALLOC_INSTRUMENTATION_STDOUT)
|
|
void* operator new(std::size_t size) {
|
|
void* p = malloc(size);
|
|
if (!p)
|
|
throw std::bad_alloc();
|
|
recordAllocation(p, size);
|
|
return p;
|
|
}
|
|
void operator delete(void* ptr) throw() {
|
|
recordDeallocation(ptr);
|
|
free(ptr);
|
|
}
|
|
|
|
// scalar, nothrow new and it matching delete
|
|
void* operator new(std::size_t size, const std::nothrow_t&) throw() {
|
|
void* p = malloc(size);
|
|
recordAllocation(p, size);
|
|
return p;
|
|
}
|
|
void operator delete(void* ptr, const std::nothrow_t&) throw() {
|
|
recordDeallocation(ptr);
|
|
free(ptr);
|
|
}
|
|
|
|
// array throwing new and matching delete[]
|
|
void* operator new[](std::size_t size) {
|
|
void* p = malloc(size);
|
|
if (!p)
|
|
throw std::bad_alloc();
|
|
recordAllocation(p, size);
|
|
return p;
|
|
}
|
|
void operator delete[](void* ptr) throw() {
|
|
recordDeallocation(ptr);
|
|
free(ptr);
|
|
}
|
|
|
|
// array, nothrow new and matching delete[]
|
|
void* operator new[](std::size_t size, const std::nothrow_t&) throw() {
|
|
void* p = malloc(size);
|
|
recordAllocation(p, size);
|
|
return p;
|
|
}
|
|
void operator delete[](void* ptr, const std::nothrow_t&) throw() {
|
|
recordDeallocation(ptr);
|
|
free(ptr);
|
|
}
|
|
#endif
|
|
|
|
Optional<bool> checkBuggifyOverride(const char* testFile) {
|
|
std::ifstream ifs;
|
|
ifs.open(testFile, std::ifstream::in);
|
|
if (!ifs.good())
|
|
return 0;
|
|
|
|
std::string cline;
|
|
|
|
while (ifs.good()) {
|
|
getline(ifs, cline);
|
|
std::string line = removeWhitespace(std::string(cline));
|
|
if (!line.size() || line.find(';') == 0)
|
|
continue;
|
|
|
|
size_t found = line.find('=');
|
|
if (found == std::string::npos)
|
|
// hmmm, not good
|
|
continue;
|
|
std::string attrib = removeWhitespace(line.substr(0, found));
|
|
std::string value = removeWhitespace(line.substr(found + 1));
|
|
|
|
if (attrib == "buggify") {
|
|
// Testspec uses `on` or `off` (without quotes).
|
|
// TOML uses literal `true` and `false`.
|
|
if (!strcmp(value.c_str(), "on") || !strcmp(value.c_str(), "true")) {
|
|
ifs.close();
|
|
return true;
|
|
} else if (!strcmp(value.c_str(), "off") || !strcmp(value.c_str(), "false")) {
|
|
ifs.close();
|
|
return false;
|
|
} else {
|
|
fprintf(stderr, "ERROR: Unknown buggify override state `%s'\n", value.c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
ifs.close();
|
|
return Optional<bool>();
|
|
}
|
|
|
|
// Takes a vector of public and listen address strings given via command line, and returns vector of NetworkAddress
|
|
// objects.
|
|
std::pair<NetworkAddressList, NetworkAddressList> buildNetworkAddresses(
|
|
IClusterConnectionRecord& connectionRecord,
|
|
const std::vector<std::string>& publicAddressStrs,
|
|
std::vector<std::string>& listenAddressStrs) {
|
|
if (listenAddressStrs.size() > 0 && publicAddressStrs.size() != listenAddressStrs.size()) {
|
|
fprintf(stderr,
|
|
"ERROR: Listen addresses (if provided) should be equal to the number of public addresses in order.\n");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
listenAddressStrs.resize(publicAddressStrs.size(), "public");
|
|
|
|
if (publicAddressStrs.size() > 2) {
|
|
fprintf(stderr, "ERROR: maximum 2 public/listen addresses are allowed\n");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
NetworkAddressList publicNetworkAddresses;
|
|
NetworkAddressList listenNetworkAddresses;
|
|
|
|
connectionRecord.resolveHostnamesBlocking();
|
|
auto& coordinators = connectionRecord.getConnectionString().coordinators();
|
|
ASSERT(coordinators.size() > 0);
|
|
|
|
for (int ii = 0; ii < publicAddressStrs.size(); ++ii) {
|
|
const std::string& publicAddressStr = publicAddressStrs[ii];
|
|
bool autoPublicAddress = StringRef(publicAddressStr).startsWith(LiteralStringRef("auto:"));
|
|
NetworkAddress currentPublicAddress;
|
|
if (autoPublicAddress) {
|
|
try {
|
|
const NetworkAddress& parsedAddress = NetworkAddress::parse("0.0.0.0:" + publicAddressStr.substr(5));
|
|
const IPAddress publicIP = determinePublicIPAutomatically(connectionRecord.getConnectionString());
|
|
currentPublicAddress = NetworkAddress(publicIP, parsedAddress.port, true, parsedAddress.isTLS());
|
|
} catch (Error& e) {
|
|
fprintf(stderr,
|
|
"ERROR: could not determine public address automatically from `%s': %s\n",
|
|
publicAddressStr.c_str(),
|
|
e.what());
|
|
throw;
|
|
}
|
|
} else {
|
|
try {
|
|
currentPublicAddress = NetworkAddress::parse(publicAddressStr);
|
|
} catch (Error&) {
|
|
fprintf(stderr,
|
|
"ERROR: Could not parse network address `%s' (specify as IP_ADDRESS:PORT)\n",
|
|
publicAddressStr.c_str());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
if (ii == 0) {
|
|
publicNetworkAddresses.address = currentPublicAddress;
|
|
} else {
|
|
publicNetworkAddresses.secondaryAddress = currentPublicAddress;
|
|
}
|
|
|
|
if (!currentPublicAddress.isValid()) {
|
|
fprintf(stderr, "ERROR: %s is not a valid IP address\n", currentPublicAddress.toString().c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
const std::string& listenAddressStr = listenAddressStrs[ii];
|
|
NetworkAddress currentListenAddress;
|
|
if (listenAddressStr == "public") {
|
|
currentListenAddress = currentPublicAddress;
|
|
} else {
|
|
try {
|
|
currentListenAddress = NetworkAddress::parse(listenAddressStr);
|
|
} catch (Error&) {
|
|
fprintf(stderr,
|
|
"ERROR: Could not parse network address `%s' (specify as IP_ADDRESS:PORT)\n",
|
|
listenAddressStr.c_str());
|
|
throw;
|
|
}
|
|
|
|
if (currentListenAddress.isTLS() != currentPublicAddress.isTLS()) {
|
|
fprintf(stderr,
|
|
"ERROR: TLS state of listen address: %s is not equal to the TLS state of public address: %s.\n",
|
|
listenAddressStr.c_str(),
|
|
publicAddressStr.c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
}
|
|
|
|
if (ii == 0) {
|
|
listenNetworkAddresses.address = currentListenAddress;
|
|
} else {
|
|
listenNetworkAddresses.secondaryAddress = currentListenAddress;
|
|
}
|
|
|
|
bool hasSameCoord = std::all_of(coordinators.begin(), coordinators.end(), [&](const NetworkAddress& address) {
|
|
if (address.ip == currentPublicAddress.ip && address.port == currentPublicAddress.port) {
|
|
return address.isTLS() == currentPublicAddress.isTLS();
|
|
}
|
|
return true;
|
|
});
|
|
if (!hasSameCoord) {
|
|
fprintf(stderr,
|
|
"ERROR: TLS state of public address %s does not match in coordinator list.\n",
|
|
publicAddressStr.c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
}
|
|
|
|
if (publicNetworkAddresses.secondaryAddress.present() &&
|
|
publicNetworkAddresses.address.isTLS() == publicNetworkAddresses.secondaryAddress.get().isTLS()) {
|
|
fprintf(stderr, "ERROR: only one public address of each TLS state is allowed.\n");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
return std::make_pair(publicNetworkAddresses, listenNetworkAddresses);
|
|
}
|
|
|
|
// moves files from 'dirSrc' to 'dirToMove' if their name contains 'role'
|
|
void restoreRoleFilesHelper(std::string dirSrc, std::string dirToMove, std::string role) {
|
|
std::vector<std::string> returnFiles = platform::listFiles(dirSrc, "");
|
|
for (const auto& fileEntry : returnFiles) {
|
|
if (fileEntry != "fdb.cluster" && fileEntry.find(role) != std::string::npos) {
|
|
// rename files
|
|
TraceEvent("RenamingSnapFile")
|
|
.detail("Oldname", dirSrc + "/" + fileEntry)
|
|
.detail("Newname", dirToMove + "/" + fileEntry);
|
|
renameFile(dirSrc + "/" + fileEntry, dirToMove + "/" + fileEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
enum class ServerRole {
|
|
ConsistencyCheck,
|
|
CreateTemplateDatabase,
|
|
DSLTest,
|
|
FDBD,
|
|
KVFileGenerateIOLogChecksums,
|
|
KVFileIntegrityCheck,
|
|
KVFileDump,
|
|
MultiTester,
|
|
NetworkTestClient,
|
|
NetworkTestServer,
|
|
Restore,
|
|
SearchMutations,
|
|
Simulation,
|
|
SkipListTest,
|
|
Test,
|
|
VersionedMapTest,
|
|
UnitTests,
|
|
FlowProcess
|
|
};
|
|
struct CLIOptions {
|
|
std::string commandLine;
|
|
std::string fileSystemPath, dataFolder, connFile, seedConnFile, seedConnString, logFolder = ".", metricsConnFile,
|
|
metricsPrefix;
|
|
std::string logGroup = "default";
|
|
uint64_t rollsize = TRACE_DEFAULT_ROLL_SIZE;
|
|
uint64_t maxLogsSize = TRACE_DEFAULT_MAX_LOGS_SIZE;
|
|
bool maxLogsSizeSet = false;
|
|
int maxLogs = 0;
|
|
bool maxLogsSet = false;
|
|
|
|
ServerRole role = ServerRole::FDBD;
|
|
uint32_t randomSeed = platform::getRandomSeed();
|
|
|
|
const char* testFile = "tests/default.txt";
|
|
std::string kvFile;
|
|
std::string testServersStr;
|
|
std::string whitelistBinPaths;
|
|
|
|
std::vector<std::string> publicAddressStrs, listenAddressStrs;
|
|
NetworkAddressList publicAddresses, listenAddresses;
|
|
|
|
const char* targetKey = nullptr;
|
|
uint64_t memLimit =
|
|
8LL << 30; // Nice to maintain the same default value for memLimit and SERVER_KNOBS->SERVER_MEM_LIMIT and
|
|
// SERVER_KNOBS->COMMIT_BATCHES_MEM_BYTES_HARD_LIMIT
|
|
uint64_t virtualMemLimit = 0; // unlimited
|
|
uint64_t storageMemLimit = 1LL << 30;
|
|
bool buggifyEnabled = false, faultInjectionEnabled = true, restarting = false;
|
|
Optional<Standalone<StringRef>> zoneId;
|
|
Optional<Standalone<StringRef>> dcId;
|
|
ProcessClass processClass = ProcessClass(ProcessClass::UnsetClass, ProcessClass::CommandLineSource);
|
|
bool useNet2 = true;
|
|
bool useThreadPool = false;
|
|
std::vector<std::pair<std::string, std::string>> knobs;
|
|
std::map<std::string, std::string> manualKnobOverrides;
|
|
LocalityData localities;
|
|
int minTesterCount = 1;
|
|
bool testOnServers = false;
|
|
|
|
TLSConfig tlsConfig = TLSConfig(TLSEndpointType::SERVER);
|
|
double fileIoTimeout = 0.0;
|
|
bool fileIoWarnOnly = false;
|
|
uint64_t rsssize = -1;
|
|
std::vector<std::string> blobCredentials; // used for fast restore workers & backup workers
|
|
const char* blobCredsFromENV = nullptr;
|
|
|
|
std::string configPath;
|
|
ConfigDBType configDBType{ ConfigDBType::DISABLED };
|
|
|
|
Reference<IClusterConnectionRecord> connectionFile;
|
|
Standalone<StringRef> machineId;
|
|
UnitTestParameters testParams;
|
|
|
|
std::map<std::string, std::string> profilerConfig;
|
|
std::string flowProcessName;
|
|
Endpoint flowProcessEndpoint;
|
|
bool printSimTime = false;
|
|
|
|
static CLIOptions parseArgs(int argc, char* argv[]) {
|
|
CLIOptions opts;
|
|
opts.parseArgsInternal(argc, argv);
|
|
return opts;
|
|
}
|
|
|
|
// Determine publicAddresses and listenAddresses by calling buildNetworkAddresses().
|
|
void buildNetwork(const char* name) {
|
|
try {
|
|
if (!publicAddressStrs.empty()) {
|
|
std::tie(publicAddresses, listenAddresses) =
|
|
buildNetworkAddresses(*connectionFile, publicAddressStrs, listenAddressStrs);
|
|
}
|
|
} catch (Error&) {
|
|
printHelpTeaser(name);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
if (role == ServerRole::ConsistencyCheck) {
|
|
if (!publicAddressStrs.empty()) {
|
|
fprintf(stderr, "ERROR: Public address cannot be specified for consistency check processes\n");
|
|
printHelpTeaser(name);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
auto publicIP = determinePublicIPAutomatically(connectionFile->getConnectionString());
|
|
publicAddresses.address = NetworkAddress(publicIP, ::getpid());
|
|
}
|
|
}
|
|
|
|
private:
|
|
CLIOptions() = default;
|
|
|
|
void parseArgsInternal(int argc, char* argv[]) {
|
|
for (int a = 0; a < argc; a++) {
|
|
if (a)
|
|
commandLine += ' ';
|
|
commandLine += argv[a];
|
|
}
|
|
|
|
CSimpleOpt args(argc, argv, g_rgOptions, SO_O_EXACT | SO_O_HYPHEN_TO_UNDERSCORE);
|
|
|
|
if (argc == 1) {
|
|
printUsage(argv[0], false);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
while (args.Next()) {
|
|
if (args.LastError() == SO_ARG_INVALID_DATA) {
|
|
fprintf(stderr, "ERROR: invalid argument to option `%s'\n", args.OptionText());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
if (args.LastError() == SO_ARG_INVALID) {
|
|
fprintf(stderr, "ERROR: argument given for option `%s'\n", args.OptionText());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
if (args.LastError() == SO_ARG_MISSING) {
|
|
fprintf(stderr, "ERROR: missing argument for option `%s'\n", args.OptionText());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
if (args.LastError() == SO_OPT_INVALID) {
|
|
fprintf(stderr, "ERROR: unknown option: `%s'\n", args.OptionText());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
if (args.LastError() != SO_SUCCESS) {
|
|
fprintf(stderr, "ERROR: error parsing options\n");
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
const char* sRole;
|
|
Optional<uint64_t> ti;
|
|
std::string argStr;
|
|
std::vector<std::string> tmpStrings;
|
|
|
|
switch (args.OptionId()) {
|
|
case OPT_HELP:
|
|
printUsage(argv[0], false);
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
break;
|
|
case OPT_DEVHELP:
|
|
printUsage(argv[0], true);
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
break;
|
|
case OPT_KNOB: {
|
|
Optional<std::string> knobName = extractPrefixedArgument("--knob", args.OptionSyntax());
|
|
if (!knobName.present()) {
|
|
fprintf(stderr, "ERROR: unable to parse knob option '%s'\n", args.OptionSyntax());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
knobs.emplace_back(knobName.get(), args.OptionArg());
|
|
manualKnobOverrides[knobName.get()] = args.OptionArg();
|
|
break;
|
|
}
|
|
case OPT_PROFILER: {
|
|
Optional<std::string> profilerArg = extractPrefixedArgument("--profiler", args.OptionSyntax());
|
|
if (!profilerArg.present()) {
|
|
fprintf(stderr, "ERROR: unable to parse profiler option '%s'\n", args.OptionSyntax());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
profilerConfig.emplace(profilerArg.get(), args.OptionArg());
|
|
break;
|
|
};
|
|
case OPT_UNITTESTPARAM: {
|
|
Optional<std::string> testArg = extractPrefixedArgument("--test", args.OptionSyntax());
|
|
if (!testArg.present()) {
|
|
fprintf(stderr, "ERROR: unable to parse unit test option '%s'\n", args.OptionSyntax());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
testParams.set(testArg.get(), args.OptionArg());
|
|
break;
|
|
}
|
|
case OPT_LOCALITY: {
|
|
Optional<std::string> localityKey = extractPrefixedArgument("--locality", args.OptionSyntax());
|
|
if (!localityKey.present()) {
|
|
fprintf(stderr, "ERROR: unable to parse locality key '%s'\n", args.OptionSyntax());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
Standalone<StringRef> key = StringRef(localityKey.get());
|
|
std::transform(key.begin(), key.end(), mutateString(key), ::tolower);
|
|
localities.set(key, Standalone<StringRef>(std::string(args.OptionArg())));
|
|
break;
|
|
}
|
|
case OPT_VERSION:
|
|
printVersion();
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
break;
|
|
case OPT_BUILD_FLAGS:
|
|
printBuildInformation();
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
case OPT_NOBUFSTDOUT:
|
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
|
setvbuf(stderr, nullptr, _IONBF, 0);
|
|
break;
|
|
case OPT_BUFSTDOUTERR:
|
|
setvbuf(stdout, nullptr, _IOFBF, BUFSIZ);
|
|
setvbuf(stderr, nullptr, _IOFBF, BUFSIZ);
|
|
break;
|
|
case OPT_ROLE:
|
|
sRole = args.OptionArg();
|
|
if (!strcmp(sRole, "fdbd"))
|
|
role = ServerRole::FDBD;
|
|
else if (!strcmp(sRole, "simulation"))
|
|
role = ServerRole::Simulation;
|
|
else if (!strcmp(sRole, "test"))
|
|
role = ServerRole::Test;
|
|
else if (!strcmp(sRole, "multitest"))
|
|
role = ServerRole::MultiTester;
|
|
else if (!strcmp(sRole, "skiplisttest"))
|
|
role = ServerRole::SkipListTest;
|
|
else if (!strcmp(sRole, "search"))
|
|
role = ServerRole::SearchMutations;
|
|
else if (!strcmp(sRole, "dsltest"))
|
|
role = ServerRole::DSLTest;
|
|
else if (!strcmp(sRole, "versionedmaptest"))
|
|
role = ServerRole::VersionedMapTest;
|
|
else if (!strcmp(sRole, "createtemplatedb"))
|
|
role = ServerRole::CreateTemplateDatabase;
|
|
else if (!strcmp(sRole, "networktestclient"))
|
|
role = ServerRole::NetworkTestClient;
|
|
else if (!strcmp(sRole, "networktestserver"))
|
|
role = ServerRole::NetworkTestServer;
|
|
else if (!strcmp(sRole, "restore"))
|
|
role = ServerRole::Restore;
|
|
else if (!strcmp(sRole, "kvfileintegritycheck"))
|
|
role = ServerRole::KVFileIntegrityCheck;
|
|
else if (!strcmp(sRole, "kvfilegeneratesums"))
|
|
role = ServerRole::KVFileGenerateIOLogChecksums;
|
|
else if (!strcmp(sRole, "kvfiledump"))
|
|
role = ServerRole::KVFileDump;
|
|
else if (!strcmp(sRole, "consistencycheck"))
|
|
role = ServerRole::ConsistencyCheck;
|
|
else if (!strcmp(sRole, "unittests"))
|
|
role = ServerRole::UnitTests;
|
|
else if (!strcmp(sRole, "flowprocess"))
|
|
role = ServerRole::FlowProcess;
|
|
else {
|
|
fprintf(stderr, "ERROR: Unknown role `%s'\n", sRole);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
case OPT_PUBLICADDR:
|
|
argStr = args.OptionArg();
|
|
boost::split(tmpStrings, argStr, [](char c) { return c == ','; });
|
|
publicAddressStrs.insert(publicAddressStrs.end(), tmpStrings.begin(), tmpStrings.end());
|
|
break;
|
|
case OPT_LISTEN:
|
|
argStr = args.OptionArg();
|
|
boost::split(tmpStrings, argStr, [](char c) { return c == ','; });
|
|
listenAddressStrs.insert(listenAddressStrs.end(), tmpStrings.begin(), tmpStrings.end());
|
|
break;
|
|
case OPT_CONNFILE:
|
|
connFile = args.OptionArg();
|
|
break;
|
|
case OPT_LOGGROUP:
|
|
logGroup = args.OptionArg();
|
|
break;
|
|
case OPT_SEEDCONNFILE:
|
|
seedConnFile = args.OptionArg();
|
|
break;
|
|
case OPT_SEEDCONNSTRING:
|
|
seedConnString = args.OptionArg();
|
|
break;
|
|
#ifdef __linux__
|
|
case OPT_FILESYSTEM: {
|
|
fileSystemPath = args.OptionArg();
|
|
break;
|
|
}
|
|
case OPT_PROFILER_RSS_SIZE: {
|
|
const char* a = args.OptionArg();
|
|
char* end;
|
|
rsssize = strtoull(a, &end, 10);
|
|
if (*end) {
|
|
fprintf(stderr, "ERROR: Unrecognized memory size `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case OPT_DATAFOLDER:
|
|
dataFolder = args.OptionArg();
|
|
break;
|
|
case OPT_LOGFOLDER:
|
|
logFolder = args.OptionArg();
|
|
break;
|
|
case OPT_NETWORKIMPL: {
|
|
const char* a = args.OptionArg();
|
|
if (!strcmp(a, "net2"))
|
|
useNet2 = true;
|
|
else if (!strcmp(a, "net2-threadpool")) {
|
|
useNet2 = true;
|
|
useThreadPool = true;
|
|
} else {
|
|
fprintf(stderr, "ERROR: Unknown network implementation `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_TRACECLOCK: {
|
|
const char* a = args.OptionArg();
|
|
if (!strcmp(a, "realtime"))
|
|
g_trace_clock.store(TRACE_CLOCK_REALTIME);
|
|
else if (!strcmp(a, "now"))
|
|
g_trace_clock.store(TRACE_CLOCK_NOW);
|
|
else {
|
|
fprintf(stderr, "ERROR: Unknown clock source `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_NUMTESTERS: {
|
|
const char* a = args.OptionArg();
|
|
if (!sscanf(a, "%d", &minTesterCount)) {
|
|
fprintf(stderr, "ERROR: Could not parse numtesters `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_ROLLSIZE: {
|
|
const char* a = args.OptionArg();
|
|
ti = parse_with_suffix(a);
|
|
if (!ti.present()) {
|
|
fprintf(stderr, "ERROR: Could not parse logsize `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
rollsize = ti.get();
|
|
break;
|
|
}
|
|
case OPT_MAXLOGSSIZE: {
|
|
const char* a = args.OptionArg();
|
|
ti = parse_with_suffix(a);
|
|
if (!ti.present()) {
|
|
fprintf(stderr, "ERROR: Could not parse maxlogssize `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
maxLogsSize = ti.get();
|
|
maxLogsSizeSet = true;
|
|
break;
|
|
}
|
|
case OPT_MAXLOGS: {
|
|
const char* a = args.OptionArg();
|
|
char* end;
|
|
maxLogs = strtoull(a, &end, 10);
|
|
if (*end) {
|
|
fprintf(stderr, "ERROR: Unrecognized maximum number of logs `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
maxLogsSet = true;
|
|
break;
|
|
}
|
|
#ifdef _WIN32
|
|
case OPT_PARENTPID: {
|
|
auto pid_str = args.OptionArg();
|
|
int parent_pid = atoi(pid_str);
|
|
auto pHandle = OpenProcess(SYNCHRONIZE, FALSE, parent_pid);
|
|
if (!pHandle) {
|
|
TraceEvent("ParentProcessOpenError").GetLastError();
|
|
fprintf(stderr, "Could not open parent process at pid %d (error %d)", parent_pid, GetLastError());
|
|
throw platform_error();
|
|
}
|
|
startThread(&parentWatcher, pHandle, 0, "fdb-parentwatch");
|
|
break;
|
|
}
|
|
case OPT_NEWCONSOLE:
|
|
FreeConsole();
|
|
AllocConsole();
|
|
freopen("CONIN$", "rb", stdin);
|
|
freopen("CONOUT$", "wb", stdout);
|
|
freopen("CONOUT$", "wb", stderr);
|
|
break;
|
|
case OPT_NOBOX:
|
|
SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
|
|
break;
|
|
#else
|
|
case OPT_PARENTPID: {
|
|
auto pid_str = args.OptionArg();
|
|
int* parent_pid = new (int);
|
|
*parent_pid = atoi(pid_str);
|
|
startThread(&parentWatcher, parent_pid, 0, "fdb-parentwatch");
|
|
break;
|
|
}
|
|
#endif
|
|
case OPT_TRACER: {
|
|
std::string arg = args.OptionArg();
|
|
std::string tracer;
|
|
std::transform(arg.begin(), arg.end(), std::back_inserter(tracer), [](char c) { return tolower(c); });
|
|
if (tracer == "none" || tracer == "disabled") {
|
|
openTracer(TracerType::DISABLED);
|
|
} else if (tracer == "logfile" || tracer == "file" || tracer == "log_file") {
|
|
openTracer(TracerType::LOG_FILE);
|
|
} else if (tracer == "network_lossy") {
|
|
openTracer(TracerType::NETWORK_LOSSY);
|
|
} else {
|
|
fprintf(stderr, "ERROR: Unknown or unsupported tracer: `%s'", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_TESTFILE:
|
|
testFile = args.OptionArg();
|
|
break;
|
|
case OPT_KVFILE:
|
|
kvFile = args.OptionArg();
|
|
break;
|
|
case OPT_RESTARTING:
|
|
restarting = true;
|
|
break;
|
|
case OPT_RANDOMSEED: {
|
|
char* end;
|
|
randomSeed = (uint32_t)strtoul(args.OptionArg(), &end, 0);
|
|
if (*end) {
|
|
fprintf(stderr, "ERROR: Could not parse random seed `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_MACHINEID: {
|
|
zoneId = std::string(args.OptionArg());
|
|
break;
|
|
}
|
|
case OPT_DCID: {
|
|
dcId = std::string(args.OptionArg());
|
|
break;
|
|
}
|
|
case OPT_MACHINE_CLASS:
|
|
sRole = args.OptionArg();
|
|
processClass = ProcessClass(sRole, ProcessClass::CommandLineSource);
|
|
if (processClass == ProcessClass::InvalidClass) {
|
|
fprintf(stderr, "ERROR: Unknown machine class `%s'\n", sRole);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
case OPT_KEY:
|
|
targetKey = args.OptionArg();
|
|
break;
|
|
case OPT_MEMLIMIT:
|
|
ti = parse_with_suffix(args.OptionArg(), "MiB");
|
|
if (!ti.present()) {
|
|
fprintf(stderr, "ERROR: Could not parse memory limit from `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
memLimit = ti.get();
|
|
break;
|
|
case OPT_VMEMLIMIT:
|
|
ti = parse_with_suffix(args.OptionArg(), "MiB");
|
|
if (!ti.present()) {
|
|
fprintf(stderr, "ERROR: Could not parse virtual memory limit from `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
virtualMemLimit = ti.get();
|
|
break;
|
|
case OPT_STORAGEMEMLIMIT:
|
|
ti = parse_with_suffix(args.OptionArg(), "MB");
|
|
if (!ti.present()) {
|
|
fprintf(stderr, "ERROR: Could not parse storage memory limit from `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
storageMemLimit = ti.get();
|
|
break;
|
|
case OPT_CACHEMEMLIMIT:
|
|
ti = parse_with_suffix(args.OptionArg(), "MiB");
|
|
if (!ti.present()) {
|
|
fprintf(stderr, "ERROR: Could not parse cache memory limit from `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
// SOMEDAY: ideally we'd have some better way to express that a knob should be elevated to formal
|
|
// parameter
|
|
knobs.emplace_back(
|
|
"page_cache_4k",
|
|
format("%lld", ti.get() / 4096 * 4096)); // The cache holds 4K pages, so we can truncate this to the
|
|
// next smaller multiple of 4K.
|
|
break;
|
|
case OPT_BUGGIFY:
|
|
if (!strcmp(args.OptionArg(), "on"))
|
|
buggifyEnabled = true;
|
|
else if (!strcmp(args.OptionArg(), "off"))
|
|
buggifyEnabled = false;
|
|
else {
|
|
fprintf(stderr, "ERROR: Unknown buggify state `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
case OPT_FAULT_INJECTION:
|
|
if (!strcmp(args.OptionArg(), "on"))
|
|
faultInjectionEnabled = true;
|
|
else if (!strcmp(args.OptionArg(), "off"))
|
|
faultInjectionEnabled = false;
|
|
else {
|
|
fprintf(stderr, "ERROR: Unknown fault injection state `%s'\n", args.OptionArg());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
case OPT_CRASHONERROR:
|
|
g_crashOnError = true;
|
|
break;
|
|
case OPT_TESTSERVERS:
|
|
testServersStr = args.OptionArg();
|
|
break;
|
|
case OPT_TEST_ON_SERVERS:
|
|
testOnServers = true;
|
|
break;
|
|
case OPT_METRICSCONNFILE:
|
|
metricsConnFile = args.OptionArg();
|
|
break;
|
|
case OPT_METRICSPREFIX:
|
|
metricsPrefix = args.OptionArg();
|
|
break;
|
|
case OPT_IO_TRUST_SECONDS: {
|
|
const char* a = args.OptionArg();
|
|
if (!sscanf(a, "%lf", &fileIoTimeout)) {
|
|
fprintf(stderr, "ERROR: Could not parse io_trust_seconds `%s'\n", a);
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_IO_TRUST_WARN_ONLY:
|
|
fileIoWarnOnly = true;
|
|
break;
|
|
case OPT_TRACE_FORMAT:
|
|
if (!selectTraceFormatter(args.OptionArg())) {
|
|
fprintf(stderr, "WARNING: Unrecognized trace format `%s'\n", args.OptionArg());
|
|
}
|
|
break;
|
|
case OPT_WHITELIST_BINPATH:
|
|
whitelistBinPaths = args.OptionArg();
|
|
break;
|
|
case OPT_BLOB_CREDENTIAL_FILE:
|
|
// Add blob credential following backup agent example
|
|
blobCredentials.push_back(args.OptionArg());
|
|
printf("blob credential file:%s\n", blobCredentials.back().c_str());
|
|
|
|
blobCredsFromENV = getenv("FDB_BLOB_CREDENTIALS");
|
|
if (blobCredsFromENV != nullptr) {
|
|
fprintf(stderr, "[WARNING] Set blob credetial via env variable is not tested yet\n");
|
|
TraceEvent(SevError, "FastRestoreGetBlobCredentialFile")
|
|
.detail("Reason", "Set blob credetial via env variable is not tested yet");
|
|
StringRef t((uint8_t*)blobCredsFromENV, strlen(blobCredsFromENV));
|
|
do {
|
|
StringRef file = t.eat(":");
|
|
if (file.size() != 0) {
|
|
blobCredentials.push_back(file.toString());
|
|
}
|
|
} while (t.size() != 0);
|
|
}
|
|
break;
|
|
case OPT_CONFIG_PATH:
|
|
configPath = args.OptionArg();
|
|
break;
|
|
case OPT_USE_TEST_CONFIG_DB:
|
|
configDBType = ConfigDBType::SIMPLE;
|
|
break;
|
|
case OPT_FLOW_PROCESS_NAME:
|
|
flowProcessName = args.OptionArg();
|
|
std::cout << flowProcessName << std::endl;
|
|
break;
|
|
case OPT_FLOW_PROCESS_ENDPOINT: {
|
|
std::vector<std::string> strings;
|
|
std::cout << args.OptionArg() << std::endl;
|
|
boost::split(strings, args.OptionArg(), [](char c) { return c == ','; });
|
|
for (auto& str : strings) {
|
|
std::cout << str << " ";
|
|
}
|
|
std::cout << "\n";
|
|
if (strings.size() != 3) {
|
|
std::cerr << "Invalid argument, expected 3 elements in --process-endpoint got " << strings.size()
|
|
<< std::endl;
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
try {
|
|
auto addr = NetworkAddress::parse(strings[0]);
|
|
uint64_t fst = std::stoul(strings[1]);
|
|
uint64_t snd = std::stoul(strings[2]);
|
|
UID token(fst, snd);
|
|
NetworkAddressList l;
|
|
l.address = addr;
|
|
flowProcessEndpoint = Endpoint(l, token);
|
|
std::cout << "flowProcessEndpoint: " << flowProcessEndpoint.getPrimaryAddress().toString()
|
|
<< ", token: " << flowProcessEndpoint.token.toString() << "\n";
|
|
} catch (Error& e) {
|
|
std::cerr << "Could not parse network address " << strings[0] << std::endl;
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
} catch (std::exception& e) {
|
|
std::cerr << "Could not parse token " << strings[1] << "," << strings[2] << std::endl;
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
case OPT_PRINT_SIMTIME:
|
|
printSimTime = true;
|
|
break;
|
|
|
|
#ifndef TLS_DISABLED
|
|
case TLSConfig::OPT_TLS_PLUGIN:
|
|
args.OptionArg();
|
|
break;
|
|
case TLSConfig::OPT_TLS_CERTIFICATES:
|
|
tlsConfig.setCertificatePath(args.OptionArg());
|
|
break;
|
|
case TLSConfig::OPT_TLS_PASSWORD:
|
|
tlsConfig.setPassword(args.OptionArg());
|
|
break;
|
|
case TLSConfig::OPT_TLS_CA_FILE:
|
|
tlsConfig.setCAPath(args.OptionArg());
|
|
break;
|
|
case TLSConfig::OPT_TLS_KEY:
|
|
tlsConfig.setKeyPath(args.OptionArg());
|
|
break;
|
|
case TLSConfig::OPT_TLS_VERIFY_PEERS:
|
|
tlsConfig.addVerifyPeers(args.OptionArg());
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
try {
|
|
ProfilerConfig::instance().reset(profilerConfig);
|
|
} catch (ConfigError& e) {
|
|
printf("Error seting up profiler: %s", e.description.c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
if (seedConnString.length() && seedConnFile.length()) {
|
|
fprintf(
|
|
stderr, "%s\n", "--seed-cluster-file and --seed-connection-string may not both be specified at once.");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
bool seedSpecified = seedConnFile.length() || seedConnString.length();
|
|
|
|
if (seedSpecified && !connFile.length()) {
|
|
fprintf(stderr,
|
|
"%s\n",
|
|
"If -seed-cluster-file or --seed-connection-string is specified, -C must be specified as well.");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
if (metricsConnFile == connFile)
|
|
metricsConnFile = "";
|
|
|
|
if (metricsConnFile != "" && metricsPrefix == "") {
|
|
fprintf(stderr, "If a metrics cluster file is specified, a metrics prefix is required.\n");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
bool autoPublicAddress =
|
|
std::any_of(publicAddressStrs.begin(), publicAddressStrs.end(), [](const std::string& addr) {
|
|
return StringRef(addr).startsWith(LiteralStringRef("auto:"));
|
|
});
|
|
if ((role != ServerRole::Simulation && role != ServerRole::CreateTemplateDatabase &&
|
|
role != ServerRole::KVFileIntegrityCheck && role != ServerRole::KVFileGenerateIOLogChecksums &&
|
|
role != ServerRole::KVFileDump && role != ServerRole::UnitTests) ||
|
|
autoPublicAddress) {
|
|
|
|
if (seedSpecified && !fileExists(connFile)) {
|
|
std::string connectionString = seedConnString.length() ? seedConnString : "";
|
|
ClusterConnectionString ccs;
|
|
if (seedConnFile.length()) {
|
|
try {
|
|
connectionString = readFileBytes(seedConnFile, MAX_CLUSTER_FILE_BYTES);
|
|
} catch (Error& e) {
|
|
fprintf(stderr,
|
|
"%s\n",
|
|
ClusterConnectionFile::getErrorString(std::make_pair(seedConnFile, false), e).c_str());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
try {
|
|
ccs = ClusterConnectionString(connectionString);
|
|
} catch (Error& e) {
|
|
fprintf(stderr, "%s\n", ClusterConnectionString::getErrorString(connectionString, e).c_str());
|
|
throw;
|
|
}
|
|
connectionFile = makeReference<ClusterConnectionFile>(connFile, ccs);
|
|
} else {
|
|
std::pair<std::string, bool> resolvedClusterFile;
|
|
try {
|
|
resolvedClusterFile = ClusterConnectionFile::lookupClusterFileName(connFile);
|
|
connectionFile = makeReference<ClusterConnectionFile>(resolvedClusterFile.first);
|
|
} catch (Error& e) {
|
|
fprintf(stderr, "%s\n", ClusterConnectionFile::getErrorString(resolvedClusterFile, e).c_str());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// failmon?
|
|
}
|
|
|
|
if (role == ServerRole::Simulation) {
|
|
Optional<bool> buggifyOverride = checkBuggifyOverride(testFile);
|
|
if (buggifyOverride.present())
|
|
buggifyEnabled = buggifyOverride.get();
|
|
}
|
|
|
|
if (role == ServerRole::SearchMutations && !targetKey) {
|
|
fprintf(stderr, "ERROR: please specify a target key\n");
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
if (role == ServerRole::NetworkTestClient && !testServersStr.size()) {
|
|
fprintf(stderr, "ERROR: please specify --testservers\n");
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
// Interpret legacy "maxLogs" option in the most sensible and unsurprising way we can while eliminating its code
|
|
// path
|
|
if (maxLogsSet) {
|
|
if (maxLogsSizeSet) {
|
|
// This is the case where both options are set and we must deconflict.
|
|
auto maxLogsAsSize = maxLogs * rollsize;
|
|
|
|
// If either was unlimited, then the safe option here is to take the larger one.
|
|
// This means that is one of the two options specified a limited amount of logging
|
|
// then the option that specified "unlimited" will be ignored.
|
|
if (maxLogsSize == 0 || maxLogs == 0)
|
|
maxLogsSize = std::max(maxLogsSize, maxLogsAsSize);
|
|
else
|
|
maxLogsSize = std::min(maxLogsSize, maxLogs * rollsize);
|
|
} else {
|
|
maxLogsSize = maxLogs * rollsize;
|
|
}
|
|
}
|
|
if (!zoneId.present() &&
|
|
!(localities.isPresent(LocalityData::keyZoneId) && localities.isPresent(LocalityData::keyMachineId))) {
|
|
machineId = getSharedMemoryMachineId().toString();
|
|
}
|
|
if (!localities.isPresent(LocalityData::keyZoneId))
|
|
localities.set(LocalityData::keyZoneId, zoneId.present() ? zoneId : machineId);
|
|
|
|
if (!localities.isPresent(LocalityData::keyMachineId))
|
|
localities.set(LocalityData::keyMachineId, zoneId.present() ? zoneId : machineId);
|
|
|
|
if (!localities.isPresent(LocalityData::keyDcId) && dcId.present())
|
|
localities.set(LocalityData::keyDcId, dcId);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
// TODO: Remove later, this is just to force the statics to be initialized
|
|
// otherwise the unit test won't run
|
|
#ifdef ENABLE_SAMPLING
|
|
ActorLineageSet _;
|
|
#endif
|
|
try {
|
|
platformInit();
|
|
|
|
#ifdef ALLOC_INSTRUMENTATION
|
|
g_extra_memory = new uint8_t[1000000];
|
|
#endif
|
|
registerCrashHandler();
|
|
|
|
// Set default of line buffering standard out and error
|
|
setvbuf(stdout, nullptr, _IOLBF, BUFSIZ);
|
|
setvbuf(stderr, nullptr, _IOLBF, BUFSIZ);
|
|
|
|
// Enables profiling on this thread (but does not start it)
|
|
registerThreadForProfiling();
|
|
|
|
#ifdef _WIN32
|
|
// Windows needs a gentle nudge to format floats correctly
|
|
//_set_output_format(_TWO_DIGIT_EXPONENT);
|
|
#endif
|
|
|
|
auto opts = CLIOptions::parseArgs(argc, argv);
|
|
const auto role = opts.role;
|
|
|
|
#ifdef _WIN32
|
|
// For now, ignore all tests for Windows
|
|
if (role == ServerRole::Simulation || role == ServerRole::UnitTests || role == ServerRole::Test) {
|
|
printf("Windows tests are not supported yet\n");
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
if (role == ServerRole::Simulation)
|
|
printf("Random seed is %u...\n", opts.randomSeed);
|
|
|
|
if (opts.zoneId.present())
|
|
printf("ZoneId set to %s, dcId to %s\n", printable(opts.zoneId).c_str(), printable(opts.dcId).c_str());
|
|
|
|
setThreadLocalDeterministicRandomSeed(opts.randomSeed);
|
|
|
|
enableBuggify(opts.buggifyEnabled, BuggifyType::General);
|
|
enableFaultInjection(opts.faultInjectionEnabled);
|
|
|
|
IKnobCollection::setGlobalKnobCollection(IKnobCollection::Type::SERVER,
|
|
Randomize::True,
|
|
role == ServerRole::Simulation ? IsSimulated::True
|
|
: IsSimulated::False);
|
|
auto& g_knobs = IKnobCollection::getMutableGlobalKnobCollection();
|
|
g_knobs.setKnob("log_directory", KnobValue::create(opts.logFolder));
|
|
g_knobs.setKnob("conn_file", KnobValue::create(opts.connFile));
|
|
if (role != ServerRole::Simulation && opts.memLimit > 0) {
|
|
g_knobs.setKnob("commit_batches_mem_bytes_hard_limit",
|
|
KnobValue::create(static_cast<int64_t>(opts.memLimit)));
|
|
}
|
|
|
|
IKnobCollection::setupKnobs(opts.knobs);
|
|
g_knobs.setKnob("server_mem_limit", KnobValue::create(static_cast<int64_t>(opts.memLimit)));
|
|
// Reinitialize knobs in order to update knobs that are dependent on explicitly set knobs
|
|
g_knobs.initialize(Randomize::True, role == ServerRole::Simulation ? IsSimulated::True : IsSimulated::False);
|
|
|
|
// evictionPolicyStringToEnum will throw an exception if the string is not recognized as a valid
|
|
EvictablePageCache::evictionPolicyStringToEnum(FLOW_KNOBS->CACHE_EVICTION_POLICY);
|
|
|
|
if (opts.memLimit > 0 && opts.virtualMemLimit > 0 && opts.memLimit > opts.virtualMemLimit) {
|
|
fprintf(stderr, "ERROR : --memory-vsize has to be no less than --memory");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
if (opts.memLimit > 0 && opts.memLimit <= FLOW_KNOBS->PAGE_CACHE_4K) {
|
|
fprintf(stderr, "ERROR: --memory has to be larger than --cache-memory\n");
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
if (role == ServerRole::SkipListTest) {
|
|
skipListTest();
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
}
|
|
|
|
if (role == ServerRole::DSLTest) {
|
|
dsltest();
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
}
|
|
|
|
if (role == ServerRole::VersionedMapTest) {
|
|
versionedMapTest();
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
}
|
|
|
|
// Initialize the thread pool
|
|
CoroThreadPool::init();
|
|
// Ordinarily, this is done when the network is run. However, network thread should be set before TraceEvents
|
|
// are logged. This thread will eventually run the network, so call it now.
|
|
TraceEvent::setNetworkThread();
|
|
|
|
std::vector<Future<Void>> listenErrors;
|
|
|
|
if (role == ServerRole::Simulation || role == ServerRole::CreateTemplateDatabase) {
|
|
// startOldSimulator();
|
|
opts.buildNetwork(argv[0]);
|
|
startNewSimulator(opts.printSimTime);
|
|
openTraceFile(NetworkAddress(), opts.rollsize, opts.maxLogsSize, opts.logFolder, "trace", opts.logGroup);
|
|
openTracer(TracerType(deterministicRandom()->randomInt(static_cast<int>(TracerType::DISABLED),
|
|
static_cast<int>(TracerType::SIM_END))));
|
|
} else {
|
|
g_network = newNet2(opts.tlsConfig, opts.useThreadPool, true);
|
|
g_network->addStopCallback(Net2FileSystem::stop);
|
|
FlowTransport::createInstance(false, 1, WLTOKEN_RESERVED_COUNT);
|
|
opts.buildNetwork(argv[0]);
|
|
|
|
const bool expectsPublicAddress = (role == ServerRole::FDBD || role == ServerRole::NetworkTestServer ||
|
|
role == ServerRole::Restore || role == ServerRole::FlowProcess);
|
|
if (opts.publicAddressStrs.empty()) {
|
|
if (expectsPublicAddress) {
|
|
fprintf(stderr, "ERROR: The -p or --public-address option is required\n");
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
}
|
|
|
|
openTraceFile(
|
|
opts.publicAddresses.address, opts.rollsize, opts.maxLogsSize, opts.logFolder, "trace", opts.logGroup);
|
|
g_network->initTLS();
|
|
|
|
if (expectsPublicAddress) {
|
|
for (int ii = 0; ii < (opts.publicAddresses.secondaryAddress.present() ? 2 : 1); ++ii) {
|
|
const NetworkAddress& publicAddress =
|
|
ii == 0 ? opts.publicAddresses.address : opts.publicAddresses.secondaryAddress.get();
|
|
const NetworkAddress& listenAddress =
|
|
ii == 0 ? opts.listenAddresses.address : opts.listenAddresses.secondaryAddress.get();
|
|
try {
|
|
const Future<Void>& errorF = FlowTransport::transport().bind(publicAddress, listenAddress);
|
|
listenErrors.push_back(errorF);
|
|
if (errorF.isReady())
|
|
errorF.get();
|
|
} catch (Error& e) {
|
|
TraceEvent("BindError").error(e);
|
|
fprintf(stderr,
|
|
"Error initializing networking with public address %s and listen address %s (%s)\n",
|
|
publicAddress.toString().c_str(),
|
|
listenAddress.toString().c_str(),
|
|
e.what());
|
|
printHelpTeaser(argv[0]);
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use a negative ioTimeout to indicate warn-only
|
|
Net2FileSystem::newFileSystem(opts.fileIoWarnOnly ? -opts.fileIoTimeout : opts.fileIoTimeout,
|
|
opts.fileSystemPath);
|
|
g_network->initMetrics();
|
|
FlowTransport::transport().initMetrics();
|
|
initTraceEventMetrics();
|
|
}
|
|
|
|
double start = timer(), startNow = now();
|
|
|
|
std::string cwd = "<unknown>";
|
|
try {
|
|
cwd = platform::getWorkingDirectory();
|
|
} catch (Error& e) {
|
|
// Allow for platform error by rethrowing all _other_ errors
|
|
if (e.code() != error_code_platform_error)
|
|
throw;
|
|
}
|
|
|
|
TraceEvent("ProgramStart")
|
|
.setMaxEventLength(12000)
|
|
.detail("RandomSeed", opts.randomSeed)
|
|
.detail("SourceVersion", getSourceVersion())
|
|
.detail("Version", FDB_VT_VERSION)
|
|
.detail("PackageName", FDB_VT_PACKAGE_NAME)
|
|
.detail("FileSystem", opts.fileSystemPath)
|
|
.detail("DataFolder", opts.dataFolder)
|
|
.detail("WorkingDirectory", cwd)
|
|
.detail("ClusterFile", opts.connectionFile ? opts.connectionFile->toString() : "")
|
|
.detail("ConnectionString",
|
|
opts.connectionFile ? opts.connectionFile->getConnectionString().toString() : "")
|
|
.detailf("ActualTime", "%lld", DEBUG_DETERMINISM ? 0 : time(nullptr))
|
|
.setMaxFieldLength(10000)
|
|
.detail("CommandLine", opts.commandLine)
|
|
.setMaxFieldLength(0)
|
|
.detail("BuggifyEnabled", opts.buggifyEnabled)
|
|
.detail("FaultInjectionEnabled", opts.faultInjectionEnabled)
|
|
.detail("MemoryLimit", opts.memLimit)
|
|
.detail("VirtualMemoryLimit", opts.virtualMemLimit)
|
|
.trackLatest("ProgramStart");
|
|
|
|
Error::init();
|
|
std::set_new_handler(&platform::outOfMemory);
|
|
Future<Void> memoryUsageMonitor = startMemoryUsageMonitor(opts.memLimit);
|
|
setMemoryQuota(opts.virtualMemLimit);
|
|
|
|
Future<Optional<Void>> f;
|
|
|
|
if (role == ServerRole::Simulation) {
|
|
TraceEvent("Simulation").detail("TestFile", opts.testFile);
|
|
|
|
auto histogramReportActor = histogramReport();
|
|
|
|
CLIENT_KNOBS->trace();
|
|
FLOW_KNOBS->trace();
|
|
SERVER_KNOBS->trace();
|
|
|
|
auto dataFolder = opts.dataFolder.size() ? opts.dataFolder : "simfdb";
|
|
std::vector<std::string> directories = platform::listDirectories(dataFolder);
|
|
const std::set<std::string> allowedDirectories = { ".", "..", "backups", "unittests" };
|
|
|
|
for (const auto& dir : directories) {
|
|
if (dir.size() != 32 && allowedDirectories.count(dir) == 0 && dir.find("snap") == std::string::npos) {
|
|
|
|
TraceEvent(SevError, "IncompatibleDirectoryFound")
|
|
.detail("DataFolder", dataFolder)
|
|
.detail("SuspiciousFile", dir);
|
|
|
|
fprintf(stderr,
|
|
"ERROR: Data folder `%s' had non fdb file `%s'; please use clean, fdb-only folder\n",
|
|
dataFolder.c_str(),
|
|
dir.c_str());
|
|
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
}
|
|
std::vector<std::string> files = platform::listFiles(dataFolder);
|
|
if ((files.size() > 1 || (files.size() == 1 && files[0] != "restartInfo.ini")) && !opts.restarting) {
|
|
TraceEvent(SevError, "IncompatibleFileFound").detail("DataFolder", dataFolder);
|
|
fprintf(stderr,
|
|
"ERROR: Data folder `%s' is non-empty; please use clean, fdb-only folder\n",
|
|
dataFolder.c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
} else if (files.empty() && opts.restarting) {
|
|
TraceEvent(SevWarnAlways, "FileNotFound").detail("DataFolder", dataFolder);
|
|
printf("ERROR: Data folder `%s' is empty, but restarting option selected. Run Phase 1 test first\n",
|
|
dataFolder.c_str());
|
|
flushAndExit(FDB_EXIT_ERROR);
|
|
}
|
|
|
|
int isRestoring = 0;
|
|
if (!opts.restarting) {
|
|
platform::eraseDirectoryRecursive(dataFolder);
|
|
platform::createDirectory(dataFolder);
|
|
} else {
|
|
CSimpleIni ini;
|
|
ini.SetUnicode();
|
|
std::string absDataFolder = abspath(dataFolder);
|
|
ini.LoadFile(joinPath(absDataFolder, "restartInfo.ini").c_str());
|
|
int backupFailed = true;
|
|
const char* isRestoringStr = ini.GetValue("RESTORE", "isRestoring", nullptr);
|
|
if (isRestoringStr) {
|
|
isRestoring = atoi(isRestoringStr);
|
|
const char* backupFailedStr = ini.GetValue("RESTORE", "BackupFailed", nullptr);
|
|
if (isRestoring && backupFailedStr) {
|
|
backupFailed = atoi(backupFailedStr);
|
|
}
|
|
}
|
|
if (isRestoring && !backupFailed) {
|
|
std::vector<std::string> returnList;
|
|
std::string ext = "";
|
|
returnList = platform::listDirectories(absDataFolder);
|
|
std::string snapStr = ini.GetValue("RESTORE", "RestoreSnapUID");
|
|
|
|
TraceEvent("RestoringDataFolder").detail("DataFolder", absDataFolder);
|
|
TraceEvent("RestoreSnapUID").detail("UID", snapStr);
|
|
|
|
// delete all files (except fdb.cluster) in non-snap directories
|
|
for (const auto& dirEntry : returnList) {
|
|
if (dirEntry == "." || dirEntry == "..") {
|
|
continue;
|
|
}
|
|
if (dirEntry.find(snapStr) != std::string::npos) {
|
|
continue;
|
|
}
|
|
|
|
std::string childf = absDataFolder + "/" + dirEntry;
|
|
std::vector<std::string> returnFiles = platform::listFiles(childf, ext);
|
|
for (const auto& fileEntry : returnFiles) {
|
|
if (fileEntry != "fdb.cluster" && fileEntry != "fitness") {
|
|
TraceEvent("DeletingNonSnapfiles").detail("FileBeingDeleted", childf + "/" + fileEntry);
|
|
deleteFile(childf + "/" + fileEntry);
|
|
}
|
|
}
|
|
}
|
|
// cleanup unwanted and partial directories
|
|
for (const auto& dirEntry : returnList) {
|
|
if (dirEntry == "." || dirEntry == "..") {
|
|
continue;
|
|
}
|
|
std::string dirSrc = absDataFolder + "/" + dirEntry;
|
|
// delete snap directories which are not part of restoreSnapUID
|
|
if (dirEntry.find(snapStr) == std::string::npos) {
|
|
if (dirEntry.find("snap") != std::string::npos) {
|
|
platform::eraseDirectoryRecursive(dirSrc);
|
|
}
|
|
continue;
|
|
}
|
|
// remove empty/partial snap directories
|
|
std::vector<std::string> childrenList = platform::listFiles(dirSrc);
|
|
if (childrenList.size() == 0) {
|
|
TraceEvent("RemovingEmptySnapDirectory").detail("DirBeingDeleted", dirSrc);
|
|
platform::eraseDirectoryRecursive(dirSrc);
|
|
continue;
|
|
}
|
|
}
|
|
// move snapshotted files to appropriate locations
|
|
for (const auto& dirEntry : returnList) {
|
|
if (dirEntry == "." || dirEntry == "..") {
|
|
continue;
|
|
}
|
|
std::string dirSrc = absDataFolder + "/" + dirEntry;
|
|
std::string origDir = dirEntry.substr(0, 32);
|
|
std::string dirToMove = absDataFolder + "/" + origDir;
|
|
if ((dirEntry.find("snap") != std::string::npos) &&
|
|
(dirEntry.find("tlog") != std::string::npos)) {
|
|
// restore tlog files
|
|
restoreRoleFilesHelper(dirSrc, dirToMove, "log");
|
|
} else if ((dirEntry.find("snap") != std::string::npos) &&
|
|
(dirEntry.find("storage") != std::string::npos)) {
|
|
// restore storage files
|
|
restoreRoleFilesHelper(dirSrc, dirToMove, "storage");
|
|
} else if ((dirEntry.find("snap") != std::string::npos) &&
|
|
(dirEntry.find("coord") != std::string::npos)) {
|
|
// restore coordinator files
|
|
restoreRoleFilesHelper(dirSrc, dirToMove, "coordination");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
setupAndRun(dataFolder, opts.testFile, opts.restarting, (isRestoring >= 1), opts.whitelistBinPaths);
|
|
g_simulator.run();
|
|
} else if (role == ServerRole::FDBD) {
|
|
// Update the global blob credential files list so that both fast
|
|
// restore workers and backup workers can access blob storage.
|
|
std::vector<std::string>* pFiles =
|
|
(std::vector<std::string>*)g_network->global(INetwork::enBlobCredentialFiles);
|
|
if (pFiles != nullptr) {
|
|
for (auto& f : opts.blobCredentials) {
|
|
pFiles->push_back(f);
|
|
}
|
|
}
|
|
|
|
// Call fast restore for the class FastRestoreClass. This is a short-cut to run fast restore in circus
|
|
if (opts.processClass == ProcessClass::FastRestoreClass) {
|
|
printf("Run as fast restore worker\n");
|
|
ASSERT(opts.connectionFile);
|
|
auto dataFolder = opts.dataFolder;
|
|
if (!dataFolder.size())
|
|
dataFolder = format("fdb/%d/", opts.publicAddresses.address.port); // SOMEDAY: Better default
|
|
|
|
std::vector<Future<Void>> actors(listenErrors.begin(), listenErrors.end());
|
|
actors.push_back(restoreWorker(opts.connectionFile, opts.localities, dataFolder));
|
|
f = stopAfter(waitForAll(actors));
|
|
printf("Fast restore worker started\n");
|
|
g_network->run();
|
|
printf("g_network->run() done\n");
|
|
} else { // Call fdbd roles in conventional way
|
|
ASSERT(opts.connectionFile);
|
|
|
|
setupRunLoopProfiler();
|
|
|
|
auto dataFolder = opts.dataFolder;
|
|
if (!dataFolder.size())
|
|
dataFolder = format("fdb/%d/", opts.publicAddresses.address.port); // SOMEDAY: Better default
|
|
|
|
std::vector<Future<Void>> actors(listenErrors.begin(), listenErrors.end());
|
|
actors.push_back(fdbd(opts.connectionFile,
|
|
opts.localities,
|
|
opts.processClass,
|
|
dataFolder,
|
|
dataFolder,
|
|
opts.storageMemLimit,
|
|
opts.metricsConnFile,
|
|
opts.metricsPrefix,
|
|
opts.rsssize,
|
|
opts.whitelistBinPaths,
|
|
opts.configPath,
|
|
opts.manualKnobOverrides,
|
|
opts.configDBType));
|
|
actors.push_back(histogramReport());
|
|
// actors.push_back( recurring( []{}, .001 ) ); // for ASIO latency measurement
|
|
|
|
f = stopAfter(waitForAll(actors));
|
|
g_network->run();
|
|
}
|
|
} else if (role == ServerRole::MultiTester) {
|
|
setupRunLoopProfiler();
|
|
f = stopAfter(runTests(opts.connectionFile,
|
|
TEST_TYPE_FROM_FILE,
|
|
opts.testOnServers ? TEST_ON_SERVERS : TEST_ON_TESTERS,
|
|
opts.minTesterCount,
|
|
opts.testFile,
|
|
StringRef(),
|
|
opts.localities));
|
|
g_network->run();
|
|
} else if (role == ServerRole::Test) {
|
|
TraceEvent("NonSimulationTest").detail("TestFile", opts.testFile);
|
|
setupRunLoopProfiler();
|
|
auto m = startSystemMonitor(opts.dataFolder, opts.dcId, opts.zoneId, opts.zoneId);
|
|
f = stopAfter(runTests(
|
|
opts.connectionFile, TEST_TYPE_FROM_FILE, TEST_HERE, 1, opts.testFile, StringRef(), opts.localities));
|
|
g_network->run();
|
|
} else if (role == ServerRole::ConsistencyCheck) {
|
|
setupRunLoopProfiler();
|
|
|
|
auto m = startSystemMonitor(opts.dataFolder, opts.dcId, opts.zoneId, opts.zoneId);
|
|
f = stopAfter(runTests(opts.connectionFile,
|
|
TEST_TYPE_CONSISTENCY_CHECK,
|
|
TEST_HERE,
|
|
1,
|
|
opts.testFile,
|
|
StringRef(),
|
|
opts.localities));
|
|
g_network->run();
|
|
} else if (role == ServerRole::UnitTests) {
|
|
setupRunLoopProfiler();
|
|
auto m = startSystemMonitor(opts.dataFolder, opts.dcId, opts.zoneId, opts.zoneId);
|
|
f = stopAfter(runTests(opts.connectionFile,
|
|
TEST_TYPE_UNIT_TESTS,
|
|
TEST_HERE,
|
|
1,
|
|
opts.testFile,
|
|
StringRef(),
|
|
opts.localities,
|
|
opts.testParams));
|
|
g_network->run();
|
|
} else if (role == ServerRole::CreateTemplateDatabase) {
|
|
createTemplateDatabase();
|
|
} else if (role == ServerRole::NetworkTestClient) {
|
|
f = stopAfter(networkTestClient(opts.testServersStr));
|
|
g_network->run();
|
|
} else if (role == ServerRole::NetworkTestServer) {
|
|
f = stopAfter(networkTestServer());
|
|
g_network->run();
|
|
} else if (role == ServerRole::Restore) {
|
|
f = stopAfter(restoreWorker(opts.connectionFile, opts.localities, opts.dataFolder));
|
|
g_network->run();
|
|
} else if (role == ServerRole::KVFileIntegrityCheck) {
|
|
f = stopAfter(KVFileCheck(opts.kvFile, true));
|
|
g_network->run();
|
|
} else if (role == ServerRole::KVFileGenerateIOLogChecksums) {
|
|
Optional<Void> result;
|
|
try {
|
|
GenerateIOLogChecksumFile(opts.kvFile);
|
|
result = Void();
|
|
} catch (Error& e) {
|
|
fprintf(stderr, "Fatal Error: %s\n", e.what());
|
|
}
|
|
|
|
f = result;
|
|
} else if (role == ServerRole::FlowProcess) {
|
|
TraceEvent(SevDebug, "StartingFlowProcess").detail("From", "fdbserver");
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
|
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
if (getppid() == 1) /* parent already died before prctl */
|
|
flushAndExit(FDB_EXIT_SUCCESS);
|
|
#endif
|
|
|
|
if (opts.flowProcessName == "KeyValueStoreProcess") {
|
|
ProcessFactory<KeyValueStoreProcess>(opts.flowProcessName.c_str());
|
|
}
|
|
f = stopAfter(runFlowProcess(opts.flowProcessName, opts.flowProcessEndpoint));
|
|
g_network->run();
|
|
} else if (role == ServerRole::KVFileDump) {
|
|
f = stopAfter(KVFileDump(opts.kvFile));
|
|
g_network->run();
|
|
}
|
|
|
|
int rc = FDB_EXIT_SUCCESS;
|
|
if (f.isValid() && f.isReady() && !f.isError() && !f.get().present()) {
|
|
rc = FDB_EXIT_ERROR;
|
|
}
|
|
|
|
int unseed = noUnseed ? 0 : deterministicRandom()->randomInt(0, 100001);
|
|
TraceEvent("ElapsedTime")
|
|
.detail("SimTime", now() - startNow)
|
|
.detail("RealTime", timer() - start)
|
|
.detail("RandomUnseed", unseed);
|
|
|
|
if (role == ServerRole::Simulation) {
|
|
printf("Unseed: %d\n", unseed);
|
|
printf("Elapsed: %f simsec, %f real seconds\n", now() - startNow, timer() - start);
|
|
}
|
|
|
|
// IFailureMonitor::failureMonitor().address_info.clear();
|
|
|
|
// we should have shut down ALL actors associated with this machine; let's list all of the ones still live
|
|
/*{
|
|
auto living = Actor::all;
|
|
printf("%d surviving actors:\n", living.size());
|
|
for(auto a = living.begin(); a != living.end(); ++a)
|
|
printf(" #%lld %s %p\n", (*a)->creationIndex, (*a)->getName(), (*a));
|
|
}
|
|
|
|
{
|
|
auto living = DatabaseContext::all;
|
|
printf("%d surviving DatabaseContexts:\n", living.size());
|
|
for(auto a = living.begin(); a != living.end(); ++a)
|
|
printf(" #%lld %p\n", (*a)->creationIndex, (*a));
|
|
}
|
|
|
|
{
|
|
auto living = TransactionData::all;
|
|
printf("%d surviving TransactionData(s):\n", living.size());
|
|
for(auto a = living.begin(); a != living.end(); ++a)
|
|
printf(" #%lld %p\n", (*a)->creationIndex, (*a));
|
|
}*/
|
|
|
|
/*cout << Actor::allActors.size() << " surviving actors:" << std::endl;
|
|
std::map<std::string,int> actorCount;
|
|
for(int i=0; i<Actor::allActors.size(); i++)
|
|
++actorCount[Actor::allActors[i]->getName()];
|
|
for(auto i = actorCount.rbegin(); !(i == actorCount.rend()); ++i)
|
|
std::cout << " " << i->second << " " << i->first << std::endl;*/
|
|
// std::cout << " " << Actor::allActors[i]->getName() << std::endl;
|
|
|
|
if (role == ServerRole::Simulation) {
|
|
unsigned long sevErrorEventsLogged = TraceEvent::CountEventsLoggedAt(SevError);
|
|
if (sevErrorEventsLogged > 0) {
|
|
printf("%lu SevError events logged\n", sevErrorEventsLogged);
|
|
rc = FDB_EXIT_ERROR;
|
|
}
|
|
}
|
|
|
|
// g_simulator.run();
|
|
|
|
#ifdef ALLOC_INSTRUMENTATION
|
|
{
|
|
std::cout << "Page Counts: " << FastAllocator<16>::pageCount << " " << FastAllocator<32>::pageCount << " "
|
|
<< FastAllocator<64>::pageCount << " " << FastAllocator<128>::pageCount << " "
|
|
<< FastAllocator<256>::pageCount << " " << FastAllocator<512>::pageCount << " "
|
|
<< FastAllocator<1024>::pageCount << " " << FastAllocator<2048>::pageCount << " "
|
|
<< FastAllocator<4096>::pageCount << " " << FastAllocator<8192>::pageCount << " "
|
|
<< FastAllocator<16384>::pageCount << std::endl;
|
|
|
|
std::vector<std::pair<std::string, const char*>> typeNames;
|
|
for (auto i = allocInstr.begin(); i != allocInstr.end(); ++i) {
|
|
std::string s;
|
|
|
|
#ifdef __linux__
|
|
char* demangled = abi::__cxa_demangle(i->first, nullptr, nullptr, nullptr);
|
|
if (demangled) {
|
|
s = demangled;
|
|
if (StringRef(s).startsWith(LiteralStringRef("(anonymous namespace)::")))
|
|
s = s.substr(LiteralStringRef("(anonymous namespace)::").size());
|
|
free(demangled);
|
|
} else
|
|
s = i->first;
|
|
#else
|
|
s = i->first;
|
|
if (StringRef(s).startsWith(LiteralStringRef("class `anonymous namespace'::")))
|
|
s = s.substr(LiteralStringRef("class `anonymous namespace'::").size());
|
|
else if (StringRef(s).startsWith(LiteralStringRef("class ")))
|
|
s = s.substr(LiteralStringRef("class ").size());
|
|
else if (StringRef(s).startsWith(LiteralStringRef("struct ")))
|
|
s = s.substr(LiteralStringRef("struct ").size());
|
|
#endif
|
|
|
|
typeNames.emplace_back(s, i->first);
|
|
}
|
|
std::sort(typeNames.begin(), typeNames.end());
|
|
for (int i = 0; i < typeNames.size(); i++) {
|
|
const char* n = typeNames[i].second;
|
|
auto& f = allocInstr[n];
|
|
printf("%+d\t%+d\t%d\t%d\t%s\n",
|
|
f.allocCount,
|
|
-f.deallocCount,
|
|
f.allocCount - f.deallocCount,
|
|
f.maxAllocated,
|
|
typeNames[i].first.c_str());
|
|
}
|
|
|
|
// We're about to exit and clean up data structures, this will wreak havoc on allocation recording
|
|
memSample_entered = true;
|
|
}
|
|
#endif
|
|
// printf("\n%d tests passed; %d tests failed\n", passCount, failCount);
|
|
flushAndExit(rc);
|
|
} catch (Error& e) {
|
|
fprintf(stderr, "Error: %s\n", e.what());
|
|
TraceEvent(SevError, "MainError").error(e);
|
|
// printf("\n%d tests passed; %d tests failed\n", passCount, failCount);
|
|
flushAndExit(FDB_EXIT_MAIN_ERROR);
|
|
} catch (boost::system::system_error& e) {
|
|
ASSERT_WE_THINK(false); // boost errors shouldn't leak
|
|
fprintf(stderr, "boost::system::system_error: %s (%d)", e.what(), e.code().value());
|
|
TraceEvent(SevError, "MainError").error(unknown_error()).detail("RootException", e.what());
|
|
// printf("\n%d tests passed; %d tests failed\n", passCount, failCount);
|
|
flushAndExit(FDB_EXIT_MAIN_EXCEPTION);
|
|
} catch (std::exception& e) {
|
|
fprintf(stderr, "std::exception: %s\n", e.what());
|
|
TraceEvent(SevError, "MainError").error(unknown_error()).detail("RootException", e.what());
|
|
// printf("\n%d tests passed; %d tests failed\n", passCount, failCount);
|
|
flushAndExit(FDB_EXIT_MAIN_EXCEPTION);
|
|
}
|
|
|
|
static_assert(LBLocalityData<StorageServerInterface>::Present, "Storage server interface should be load balanced");
|
|
static_assert(LBLocalityData<CommitProxyInterface>::Present, "Commit proxy interface should be load balanced");
|
|
static_assert(LBLocalityData<GrvProxyInterface>::Present, "GRV proxy interface should be load balanced");
|
|
static_assert(LBLocalityData<TLogInterface>::Present, "TLog interface should be load balanced");
|
|
static_assert(!LBLocalityData<MasterInterface>::Present, "Master interface should not be load balanced");
|
|
}
|