diff --git a/fdbcli/StatusCommand.actor.cpp b/fdbcli/StatusCommand.actor.cpp index d0a2fd275f..1ecf28cf7c 100644 --- a/fdbcli/StatusCommand.actor.cpp +++ b/fdbcli/StatusCommand.actor.cpp @@ -1179,7 +1179,14 @@ void printStatus(StatusObjectReader statusObj, return; } -ACTOR Future statusCommandActor(Reference db, std::vector tokens, bool isExecMode) { +// "db" is the handler to the multiversion databse +// localDb is the native Database object +// localDb is rarely needed except the "db" has not establised a connection to the cluster where the operation will +// return Never as we expect status command to always return, we use "localDb" to return the default result +ACTOR Future statusCommandActor(Reference db, + Database localDb, + std::vector tokens, + bool isExecMode) { state StatusClient::StatusLevel level; if (tokens.size() == 1) @@ -1195,15 +1202,21 @@ ACTOR Future statusCommandActor(Reference db, std::vector tr = db->createTransaction(); - state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); - Optional statusValue = wait(safeThreadFutureToFuture(statusValueF)); - if (!statusValue.present()) { - fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); + if (!tr->isValid()) { + StatusObject _s = wait(StatusClient::statusFetcher(localDb)); + s = _s; + } else { + state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); + Optional statusValue = wait(safeThreadFutureToFuture(statusValueF)); + if (!statusValue.present()) { + fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); + } + json_spirit::mValue mv; + json_spirit::read_string(statusValue.get().toString(), mv); + s = StatusObject(mv.get_obj()); } - json_spirit::mValue mv; - json_spirit::read_string(statusValue.get().toString(), mv); - StatusObject s = StatusObject(mv.get_obj()); if (!isExecMode) printf("\n"); diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 5bfb7e9302..213f69cced 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -661,18 +661,27 @@ ACTOR Future timeWarning(double when, const char* msg) { return Void(); } -ACTOR Future checkStatus(Future f, Reference db, bool displayDatabaseAvailable = true) { +ACTOR Future checkStatus(Future f, + Reference db, + Database localDb, + bool displayDatabaseAvailable = true) { wait(f); state Reference tr = db->createTransaction(); - state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); - Optional> statusValue = wait(timeout(safeThreadFutureToFuture(statusValueF), 5.0)); - if (!statusValue.present() || !statusValue.get().present()) { - fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); - return Void(); + state StatusObject s; + if (!tr->isValid()) { + StatusObject _s = wait(StatusClient::statusFetcher(localDb)); + s = _s; + } else { + state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); + Optional statusValue = wait(safeThreadFutureToFuture(statusValueF)); + if (!statusValue.present()) { + fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); + return Void(); + } + json_spirit::mValue mv; + json_spirit::read_string(statusValue.get().toString(), mv); + s = StatusObject(mv.get_obj()); } - json_spirit::mValue mv; - json_spirit::read_string(statusValue.get().get().toString(), mv); - StatusObject s = StatusObject(mv.get_obj()); printf("\n"); printStatus(s, StatusClient::MINIMAL, displayDatabaseAvailable); printf("\n"); @@ -1987,6 +1996,9 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { } // used to catch the first cluster_version_changed error when using external clients + // when using external clients, it will throw cluster_version_changed for the first time establish the connection to + // the cluster. Thus, we catch it by doing a get version request to establish the connection + // The 3.0 timeout is a guard to avoid waiting forever when the cli cannot talk to any coordinators loop { try { getTransaction(db2, tr, options, intrans); @@ -1994,13 +2006,20 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { wait(delay(3.0) || success(safeThreadFutureToFuture(tr->getReadVersion()))); break; } catch (Error& e) { - wait(safeThreadFutureToFuture(tr->onError(e))); + if (e.code() == error_code_cluster_version_changed) { + wait(safeThreadFutureToFuture(tr->onError(e))); + } else { + // unexpected errors + fprintf(stderr, "ERROR: unexpected error %d while initializing the multiversion database\n", e.code()); + tr->reset(); + break; + } } } if (!opt.exec.present()) { if (opt.initialStatusCheck) { - Future checkStatusF = checkStatus(Void(), db2); + Future checkStatusF = checkStatus(Void(), db2, db); wait(makeInterruptable(success(checkStatusF))); } else { printf("\n"); @@ -2038,7 +2057,7 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { linenoise.historyAdd(line); } - warn = checkStatus(timeWarning(5.0, "\nWARNING: Long delay (Ctrl-C to interrupt)\n"), db2); + warn = checkStatus(timeWarning(5.0, "\nWARNING: Long delay (Ctrl-C to interrupt)\n"), db2, db); try { state UID randomID = deterministicRandom()->randomUniqueID(); @@ -2192,7 +2211,7 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { // printStatus(s, level); // if (!opt.exec.present()) // printf("\n"); - bool _result = wait(makeInterruptable(statusCommandActor(db2, tokens, opt.exec.present()))); + bool _result = wait(makeInterruptable(statusCommandActor(db2, db, tokens, opt.exec.present()))); if (!_result) is_error = true; continue; @@ -2301,7 +2320,7 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { fflush(stdout); Optional input = wait(linenoise.read(format("Repeat the above passphrase if you would like to proceed:"))); - warn = checkStatus(timeWarning(5.0, "\nWARNING: Long delay (Ctrl-C to interrupt)\n"), db2); + warn = checkStatus(timeWarning(5.0, "\nWARNING: Long delay (Ctrl-C to interrupt)\n"), db2, db); if (input.present() && input.get() == passPhrase) { UID unlockUID = UID::fromString(tokens[1].toString()); try { diff --git a/fdbcli/fdbcli.actor.h b/fdbcli/fdbcli.actor.h index f7178a68cd..dab31df05f 100644 --- a/fdbcli/fdbcli.actor.h +++ b/fdbcli/fdbcli.actor.h @@ -137,7 +137,10 @@ ACTOR Future setClassCommandActor(Reference db, std::vector snapshotCommandActor(Reference db, std::vector tokens); // status command -ACTOR Future statusCommandActor(Reference db, std::vector tokens, bool isExecMode = false); +ACTOR Future statusCommandActor(Reference db, + Database localDb, + std::vector tokens, + bool isExecMode = false); // suspend command ACTOR Future suspendCommandActor(Reference db, Reference tr, diff --git a/fdbclient/IClientApi.h b/fdbclient/IClientApi.h index 79e45c0842..cf304202bb 100644 --- a/fdbclient/IClientApi.h +++ b/fdbclient/IClientApi.h @@ -92,6 +92,10 @@ public: // used in template functions as returned Future type template using FutureT = ThreadFuture; + // internal use only, return true by default + // Only if it's a MultiVersionTransaction and the underlying transaction handler is null, + // it will return false + virtual bool isValid() { return true; } }; // An interface that represents a connection to a cluster made by a client diff --git a/fdbclient/MultiVersionTransaction.actor.cpp b/fdbclient/MultiVersionTransaction.actor.cpp index f016201f31..6b0b6227e2 100644 --- a/fdbclient/MultiVersionTransaction.actor.cpp +++ b/fdbclient/MultiVersionTransaction.actor.cpp @@ -877,6 +877,11 @@ void MultiVersionTransaction::reset() { updateTransaction(); } +bool MultiVersionTransaction::isValid() { + auto tr = getTransaction(); + return tr.transaction ? true : false; +} + // MultiVersionDatabase MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api, int threadIdx, diff --git a/fdbclient/MultiVersionTransaction.h b/fdbclient/MultiVersionTransaction.h index 274df7dd84..f3d7f5d8ce 100644 --- a/fdbclient/MultiVersionTransaction.h +++ b/fdbclient/MultiVersionTransaction.h @@ -388,6 +388,9 @@ public: void addref() override { ThreadSafeReferenceCounted::addref(); } void delref() override { ThreadSafeReferenceCounted::delref(); } + // return true if the underlying transaction pointer is not empty + bool isValid() override; + private: const Reference db; ThreadSpinLock lock;