diff --git a/bindings/c/fdb_c.cpp b/bindings/c/fdb_c.cpp index f632cc0c07..e6065531f1 100644 --- a/bindings/c/fdb_c.cpp +++ b/bindings/c/fdb_c.cpp @@ -398,6 +398,10 @@ extern "C" DLLEXPORT FDBFuture* fdb_database_force_recovery_with_data_loss(FDBDa return (FDBFuture*)(DB(db)->forceRecoveryWithDataLoss(StringRef(dcid, dcid_length)).extractPtr()); } +extern "C" DLLEXPORT FDBFuture* fdb_database_create_snapshot(FDBDatabase* db, uint8_t const* snap_command, int snap_command_length) { + return (FDBFuture*)(DB(db)->createSnapshot(StringRef(snap_command, snap_command_length)).extractPtr()); +} + extern "C" DLLEXPORT void fdb_transaction_destroy( FDBTransaction* tr ) { try { diff --git a/bindings/c/foundationdb/fdb_c.h b/bindings/c/foundationdb/fdb_c.h index a8ee20466b..1b9f432cb6 100644 --- a/bindings/c/foundationdb/fdb_c.h +++ b/bindings/c/foundationdb/fdb_c.h @@ -185,6 +185,9 @@ extern "C" { DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_database_force_recovery_with_data_loss( FDBDatabase* db, uint8_t const* dcid, int dcid_length); + DLLEXPORT WARN_UNUSED_RESULT FDBFuture* + fdb_database_create_snapshot( FDBDatabase* db, uint8_t const* snap_command, int snap_command_length); + DLLEXPORT void fdb_transaction_destroy( FDBTransaction* tr); DLLEXPORT void fdb_transaction_cancel( FDBTransaction* tr); diff --git a/bindings/c/test/unit/fdb_api.cpp b/bindings/c/test/unit/fdb_api.cpp index 1c567efc89..7bf43f3928 100644 --- a/bindings/c/test/unit/fdb_api.cpp +++ b/bindings/c/test/unit/fdb_api.cpp @@ -102,6 +102,10 @@ EmptyFuture Database::force_recovery_with_data_loss(FDBDatabase *db, const uint8 return EmptyFuture(fdb_database_force_recovery_with_data_loss(db, dcid, dcid_length)); } +EmptyFuture Database::create_snapshot(FDBDatabase *db, const uint8_t *snap_command, int snap_command_length) { + return EmptyFuture(fdb_database_create_snapshot(db, snap_command, snap_command_length)); +} + // Transaction Transaction::Transaction(FDBDatabase* db) { diff --git a/bindings/c/test/unit/fdb_api.hpp b/bindings/c/test/unit/fdb_api.hpp index af58180b17..dc22134e70 100644 --- a/bindings/c/test/unit/fdb_api.hpp +++ b/bindings/c/test/unit/fdb_api.hpp @@ -154,6 +154,7 @@ public: static Int64Future reboot_worker(FDBDatabase* db, const uint8_t* address, int address_length, fdb_bool_t check, int duration); static EmptyFuture force_recovery_with_data_loss(FDBDatabase* db, const uint8_t* dcid, int dcid_length); + static EmptyFuture create_snapshot(FDBDatabase* db, const uint8_t* snap_command, int snap_command_length); }; // Wrapper around FDBTransaction, providing the same set of calls as the C API. diff --git a/bindings/c/test/unit/unit_tests.cpp b/bindings/c/test/unit/unit_tests.cpp index 296f8317e5..959104a891 100644 --- a/bindings/c/test/unit/unit_tests.cpp +++ b/bindings/c/test/unit/unit_tests.cpp @@ -2046,6 +2046,20 @@ TEST_CASE("fdb_database_force_recovery_with_data_loss") { } } +TEST_CASE("fdb_database_create_snapshot") { + std::string snapshot_command = "test"; + while (1) { + fdb::EmptyFuture f = fdb::Database::create_snapshot(db, (const uint8_t*)snapshot_command.c_str(), snapshot_command.length()); + fdb_error_t err = wait_future(f); + if (err == 2505) { // expected error code + break; + } else { + // Otherwise, something went wrong. + CHECK(false); + } + } +} + TEST_CASE("fdb_error_predicate") { CHECK(fdb_error_predicate(FDB_ERROR_PREDICATE_RETRYABLE, 1007)); // transaction_too_old CHECK(fdb_error_predicate(FDB_ERROR_PREDICATE_RETRYABLE, 1020)); // not_committed diff --git a/bindings/flow/fdb_flow.actor.cpp b/bindings/flow/fdb_flow.actor.cpp index 4f59a1c66b..cfc30977d4 100644 --- a/bindings/flow/fdb_flow.actor.cpp +++ b/bindings/flow/fdb_flow.actor.cpp @@ -25,6 +25,7 @@ #include #include "flow/DeterministicRandom.h" +#include "flow/FastRef.h" #include "flow/SystemMonitor.h" #include "flow/TLSConfig.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. @@ -104,6 +105,7 @@ namespace FDB { void setDatabaseOption(FDBDatabaseOption option, Optional value = Optional()) override; Future rebootWorker(const StringRef& address, bool check = false, int duration = 0) override; Future forceRecoveryWithDataLoss(const StringRef& dcid) override; + Future createSnapshot(const StringRef& snap_command) override; private: FDBDatabase* db; @@ -304,6 +306,13 @@ namespace FDB { }); } + Future DatabaseImpl::createSnapshot(const StringRef &snap_command) { + return backToFuture( fdb_database_create_snapshot(db, snap_command.begin(), snap_command.size()), [](Reference f){ + throw_on_error( fdb_future_get_error( f->f)); + return Void(); + }); + } + TransactionImpl::TransactionImpl(FDBDatabase* db) { throw_on_error(fdb_database_create_transaction(db, &tr)); } diff --git a/bindings/flow/fdb_flow.h b/bindings/flow/fdb_flow.h index b4e857f639..22b48148b4 100644 --- a/bindings/flow/fdb_flow.h +++ b/bindings/flow/fdb_flow.h @@ -21,6 +21,7 @@ #ifndef FDB_FLOW_FDB_FLOW_H #define FDB_FLOW_FDB_FLOW_H +#include "flow/Arena.h" #include #define FDB_API_VERSION 700 @@ -126,6 +127,7 @@ namespace FDB { virtual void setDatabaseOption(FDBDatabaseOption option, Optional value = Optional()) = 0; virtual Future rebootWorker(const StringRef& address, bool check = false, int duration = 0) = 0; virtual Future forceRecoveryWithDataLoss(const StringRef& dcid) = 0; + virtual Future createSnapshot(const StringRef& snap_command) = 0; }; class API { diff --git a/fdbclient/DatabaseContext.h b/fdbclient/DatabaseContext.h index ab2debfe4c..81ea289c7d 100644 --- a/fdbclient/DatabaseContext.h +++ b/fdbclient/DatabaseContext.h @@ -211,6 +211,8 @@ public: Future rebootWorker(StringRef address, bool check = false, int duration = 0); // Management API, force the database to recover into DCID, causing the database to lose the most recently committed mutations Future forceRecoveryWithDataLoss(StringRef dcId); + // Management API, create snapshot + Future createSnapshot(StringRef snapshot_command); //private: explicit DatabaseContext( Reference>> connectionFile, Reference> clientDBInfo, diff --git a/fdbclient/IClientApi.h b/fdbclient/IClientApi.h index 0cc52719a9..42b75000cf 100644 --- a/fdbclient/IClientApi.h +++ b/fdbclient/IClientApi.h @@ -90,6 +90,8 @@ public: virtual ThreadFuture rebootWorker(const StringRef& address, bool check, int duration) = 0; // Management API, force the database to recover into DCID, causing the database to lose the most recently committed mutations virtual ThreadFuture forceRecoveryWithDataLoss(const StringRef& dcid) = 0; + // Management API, create snapshot + virtual ThreadFuture createSnapshot(const StringRef& snapshot_command) = 0; }; class IClientApi { diff --git a/fdbclient/MultiVersionTransaction.actor.cpp b/fdbclient/MultiVersionTransaction.actor.cpp index b1eef60004..10518dac36 100644 --- a/fdbclient/MultiVersionTransaction.actor.cpp +++ b/fdbclient/MultiVersionTransaction.actor.cpp @@ -23,6 +23,7 @@ #include "fdbclient/MultiVersionAssignmentVars.h" #include "fdbclient/ThreadSafeTransaction.h" +#include "flow/FastRef.h" #include "flow/network.h" #include "flow/Platform.h" #include "flow/ProtocolVersion.h" @@ -309,6 +310,17 @@ ThreadFuture DLDatabase::forceRecoveryWithDataLoss(const StringRef &dcid) return Void(); }); } + +ThreadFuture DLDatabase::createSnapshot(const StringRef &snapshot_command){ + if(!api->databaseCreateSnapshot) { + return unsupported_operation(); + } + + FdbCApi::FDBFuture *f = api->databaseCreateSnapshot(db, snapshot_command.begin(), snapshot_command.size()); + return toThreadFuture(api, f, [](FdbCApi::FDBFuture *f, FdbCApi *api) { + return Void(); + }); +} // DLApi template @@ -346,6 +358,7 @@ void DLApi::init() { loadClientFunction(&api->databaseDestroy, lib, fdbCPath, "fdb_database_destroy"); loadClientFunction(&api->databaseRebootWorker, lib, fdbCPath, "fdb_database_reboot_worker", headerVersion >= 700); loadClientFunction(&api->databaseForceRecoveryWithDataLoss, lib, fdbCPath, "fdb_database_force_recovery_with_data_loss", headerVersion >= 700); + loadClientFunction(&api->databaseCreateSnapshot, lib, fdbCPath, "fdb_database_create_snapshot", headerVersion >= 700); loadClientFunction(&api->transactionSetOption, lib, fdbCPath, "fdb_transaction_set_option"); loadClientFunction(&api->transactionDestroy, lib, fdbCPath, "fdb_transaction_destroy"); @@ -820,6 +833,11 @@ ThreadFuture MultiVersionDatabase::forceRecoveryWithDataLoss(const StringR return abortableFuture(f, dbState->dbVar->get().onChange); } +ThreadFuture MultiVersionDatabase::createSnapshot(const StringRef &snapshot_command) { + auto f = dbState->db ? dbState->db->createSnapshot(snapshot_command) : ThreadFuture(Never()); + return abortableFuture(f, dbState->dbVar->get().onChange); +} + void MultiVersionDatabase::Connector::connect() { addref(); onMainThreadVoid([this]() { diff --git a/fdbclient/MultiVersionTransaction.h b/fdbclient/MultiVersionTransaction.h index b22e389290..6de1db2108 100644 --- a/fdbclient/MultiVersionTransaction.h +++ b/fdbclient/MultiVersionTransaction.h @@ -68,6 +68,7 @@ struct FdbCApi : public ThreadSafeReferenceCounted { void (*databaseDestroy)(FDBDatabase *database); FDBFuture* (*databaseRebootWorker)(FDBDatabase *database, uint8_t const *address, int addressLength, fdb_bool_t check, int duration); FDBFuture* (*databaseForceRecoveryWithDataLoss)(FDBDatabase *database, uint8_t const *dcid, int dcidLength); + FDBFuture* (*databaseCreateSnapshot)(FDBDatabase *database, uint8_t const *snapshot_commmand, int snapshotCommandLength); //Transaction fdb_error_t (*transactionSetOption)(FDBTransaction *tr, FDBTransactionOptions::Option option, uint8_t const *value, int valueLength); @@ -199,6 +200,7 @@ public: ThreadFuture rebootWorker(const StringRef& address, bool check, int duration) override; ThreadFuture forceRecoveryWithDataLoss(const StringRef& dcid) override; + ThreadFuture createSnapshot(const StringRef& snapshot_command) override; private: const Reference api; @@ -320,6 +322,8 @@ class MultiVersionApi; class MultiVersionDatabase final : public IDatabase, ThreadSafeReferenceCounted { public: + struct DatabaseState; + MultiVersionDatabase(MultiVersionApi *api, std::string clusterFilePath, Reference db, bool openConnectors=true); ~MultiVersionDatabase() override; @@ -331,11 +335,14 @@ public: static Reference debugCreateFromExistingDatabase(Reference db); + const Reference& getDbState() {return dbState;} + ThreadFuture dbAvaliable(); + ThreadFuture rebootWorker(const StringRef& address, bool check, int duration) override; ThreadFuture forceRecoveryWithDataLoss(const StringRef& dcid) override; + ThreadFuture createSnapshot(const StringRef& snapshot_command) override; private: - struct DatabaseState; struct Connector : ThreadCallback, ThreadSafeReferenceCounted { Connector(Reference dbState, Reference client, std::string clusterFilePath) : dbState(dbState), client(client), clusterFilePath(clusterFilePath), connected(false), cancelled(false) {} @@ -361,6 +368,7 @@ private: bool cancelled; }; +public: struct DatabaseState : ThreadSafeReferenceCounted { DatabaseState(); diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 4ad562fb45..0e20cb3ae6 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -4835,3 +4835,17 @@ Future DatabaseContext::rebootWorker(StringRef addr, bool check, int du Future DatabaseContext::forceRecoveryWithDataLoss(StringRef dcId) { return forceRecovery(getConnectionFile(), dcId); } + +ACTOR static Future createSnapshotActor(DatabaseContext* cx, StringRef snapCmd) { + UID snapUID = deterministicRandom()->randomUniqueID(); + try { + wait(mgmtSnapCreate(cx->clone(), snapCmd, snapUID)); + } catch (Error& e) { + throw e; + } + return Void(); +} + +Future DatabaseContext::createSnapshot(StringRef snapshot_command) { + return createSnapshotActor(this, snapshot_command); +} diff --git a/fdbclient/ThreadSafeTransaction.cpp b/fdbclient/ThreadSafeTransaction.cpp index b8efbb254d..d916da3b21 100644 --- a/fdbclient/ThreadSafeTransaction.cpp +++ b/fdbclient/ThreadSafeTransaction.cpp @@ -84,6 +84,14 @@ ThreadFuture ThreadSafeDatabase::forceRecoveryWithDataLoss(const StringRef } ); } +ThreadFuture ThreadSafeDatabase::createSnapshot(const StringRef& snapshot_command) { + DatabaseContext *db = this->db; + Key cmd = snapshot_command; + return onMainThread( [db, cmd]() -> Future { + return db->createSnapshot(cmd); + } ); +} + ThreadSafeDatabase::ThreadSafeDatabase(std::string connFilename, int apiVersion) { ClusterConnectionFile *connFile = new ClusterConnectionFile(ClusterConnectionFile::lookupClusterFileName(connFilename).first); diff --git a/fdbclient/ThreadSafeTransaction.h b/fdbclient/ThreadSafeTransaction.h index a6515fa262..f8fb35bf9e 100644 --- a/fdbclient/ThreadSafeTransaction.h +++ b/fdbclient/ThreadSafeTransaction.h @@ -43,6 +43,7 @@ public: ThreadFuture rebootWorker(const StringRef& address, bool check, int duration) override; ThreadFuture forceRecoveryWithDataLoss(const StringRef& dcid) override; + ThreadFuture createSnapshot(const StringRef& snapshot_command) override; private: friend class ThreadSafeTransaction;