summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gottlieb <daniel.gottlieb@mongodb.com>2018-09-20 15:00:47 -0400
committerDaniel Gottlieb <daniel.gottlieb@mongodb.com>2018-09-20 15:00:47 -0400
commit630eabac0591f207b29b6be014257387a9a7a904 (patch)
tree463bd23d64f3dec795b2fcf769d88a8fbe10ef50
parent4ec12c35a07a8c0f3a30692aec413a71fdab30de (diff)
downloadmongo-630eabac0591f207b29b6be014257387a9a7a904.tar.gz
SERVER-37192: Move $backupCursor to enterprise.
-rw-r--r--buildscripts/resmokeconfig/suites/no_passthrough.yml1
-rw-r--r--jstests/auth/lib/commands_lib.js12
-rw-r--r--jstests/noPassthrough/aggregation_backup_cursor.js125
-rw-r--r--jstests/noPassthrough/backup_restore_backup_cursor.js29
-rw-r--r--src/mongo/SConscript2
-rw-r--r--src/mongo/db/commands/SConscript2
-rw-r--r--src/mongo/db/commands/fsync.cpp24
-rw-r--r--src/mongo/db/db.cpp4
-rw-r--r--src/mongo/db/pipeline/SConscript7
-rw-r--r--src/mongo/db/pipeline/document_source_backup_cursor.cpp110
-rw-r--r--src/mongo/db/pipeline/document_source_backup_cursor.h117
-rw-r--r--src/mongo/db/pipeline/mongo_process_interface.h8
-rw-r--r--src/mongo/db/pipeline/mongod_process_interface.cpp27
-rw-r--r--src/mongo/db/pipeline/mongod_process_interface.h2
-rw-r--r--src/mongo/db/pipeline/mongos_process_interface.h8
-rw-r--r--src/mongo/db/pipeline/stub_mongo_process_interface.h8
-rw-r--r--src/mongo/db/storage/SConscript45
-rw-r--r--src/mongo/db/storage/backup_cursor_hooks.cpp87
-rw-r--r--src/mongo/db/storage/backup_cursor_hooks.h69
-rw-r--r--src/mongo/db/storage/backup_cursor_service.cpp212
-rw-r--r--src/mongo/db/storage/backup_cursor_service.h122
-rw-r--r--src/mongo/db/storage/backup_cursor_service_test.cpp157
-rw-r--r--src/mongo/db/storage/backup_cursor_state.h45
23 files changed, 269 insertions, 954 deletions
diff --git a/buildscripts/resmokeconfig/suites/no_passthrough.yml b/buildscripts/resmokeconfig/suites/no_passthrough.yml
index 71d6af0b5de..a4441473a44 100644
--- a/buildscripts/resmokeconfig/suites/no_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/no_passthrough.yml
@@ -3,6 +3,7 @@ test_kind: js_test
selector:
roots:
- jstests/noPassthrough/*.js
+ - src/mongo/db/modules/*/jstests/hot_backups/*.js
# Self-tests for the Concurrency testing framework are run as part of this test suite.
- jstests/concurrency/*.js
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js
index 0a485990027..4f402dad1cd 100644
--- a/jstests/auth/lib/commands_lib.js
+++ b/jstests/auth/lib/commands_lib.js
@@ -84,6 +84,11 @@ authorization failure.
Set of options to be passed to your 'command' function. Can be used to send different versions of
the command depending on the testcase being run.
+10) skipTest
+
+Add "skipTest: <function>" to not run the test for more complex reasons. The function is passed
+one argument, the connection object.
+
*/
// constants
@@ -5945,6 +5950,10 @@ var authCommandsLib = {
testname: "aggregate_$backupCursor",
command: {aggregate: 1, cursor: {}, pipeline: [{$backupCursor: {}}]},
skipSharded: true,
+ // Only enterprise knows of this aggregation stage.
+ skipTest:
+ (conn) =>
+ !conn.getDB("admin").runCommand({buildInfo: 1}).modules.includes("enterprise"),
testcases: [{
runOnDb: adminDbName,
roles: roles_hostManager,
@@ -5988,6 +5997,9 @@ var authCommandsLib = {
runOneTest: function(conn, t, impls) {
jsTest.log("Running test: " + t.testname);
+ if (t.skipTest && t.skipTest(conn)) {
+ return [];
+ }
// some tests shouldn't run in a sharded environment
if (t.skipSharded && this.isMongos(conn)) {
return [];
diff --git a/jstests/noPassthrough/aggregation_backup_cursor.js b/jstests/noPassthrough/aggregation_backup_cursor.js
deleted file mode 100644
index 0e6e7551b09..00000000000
--- a/jstests/noPassthrough/aggregation_backup_cursor.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Test the basic operation of a `$backupCursor` aggregation stage.
- *
- * @tags: [requires_persistence, requires_wiredtiger]
- */
-(function() {
- "use strict";
-
- let conn = MongoRunner.runMongod();
- let db = conn.getDB("test");
-
- let backupCursor = db.aggregate([{$backupCursor: {}}]);
- // There should be about 14 files in total, but being precise would be unnecessarily fragile.
- assert.gt(backupCursor.itcount(), 6);
- assert(!backupCursor.isExhausted());
- backupCursor.close();
-
- // Open a backup cursor. Use a small batch size to ensure a getMore retrieves additional
- // results.
- let response = assert.commandWorked(
- db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {batchSize: 2}}));
- assert.eq("test.$cmd.aggregate", response.cursor.ns);
- assert.eq(2, response.cursor.firstBatch.length);
- let cursorId = response.cursor.id;
-
- response =
- assert.commandWorked(db.runCommand({getMore: cursorId, collection: "$cmd.aggregate"}));
- // Sanity check the results.
- assert.neq(0, response.cursor.id);
- assert.gt(response.cursor.nextBatch.length, 4);
-
- // The $backupCursor is a tailable cursor. Even though we've exhausted the results, running a
- // getMore should succeed.
- response =
- assert.commandWorked(db.runCommand({getMore: cursorId, collection: "$cmd.aggregate"}));
- assert.neq(0, response.cursor.id);
- assert.eq(0, response.cursor.nextBatch.length);
-
- // Because the backup cursor is still open, trying to open a second cursor should fail.
- assert.commandFailed(
- db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {}}));
-
- // Kill the backup cursor.
- response =
- assert.commandWorked(db.runCommand({killCursors: "$cmd.aggregate", cursors: [cursorId]}));
- assert.eq(1, response.cursorsKilled.length);
- assert.eq(cursorId, response.cursorsKilled[0]);
-
- // Open another backup cursor with a batch size of 0. The underlying backup cursor should be
- // created.
- response = assert.commandWorked(
- db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {batchSize: 0}}));
- assert.neq(0, response.cursor.id);
- assert.eq(0, response.cursor.firstBatch.length);
-
- // Attempt to open a second backup cursor to demonstrate the original underlying cursor was
- // opened.
- assert.commandFailed(
- db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {}}));
-
- // Close the cursor to reset state.
- response = assert.commandWorked(
- db.runCommand({killCursors: "$cmd.aggregate", cursors: [response.cursor.id]}));
- assert.eq(1, response.cursorsKilled.length);
-
- // Set a failpoint which will generate a uassert after the backup cursor is open.
- assert.commandWorked(
- db.adminCommand({configureFailPoint: "backupCursorErrorAfterOpen", mode: "alwaysOn"}));
- assert.commandFailed(
- db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {}}));
- assert.commandWorked(
- db.adminCommand({configureFailPoint: "backupCursorErrorAfterOpen", mode: "off"}));
-
- // Demonstrate query cursor timeouts will kill backup cursors, closing the underlying resources.
- assert.commandWorked(
- db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {}}));
- assert.commandWorked(db.adminCommand({setParameter: 1, cursorTimeoutMillis: 1}));
- assert.soon(() => {
- return db.runCommand({aggregate: 1, pipeline: [{$backupCursor: {}}], cursor: {}})['ok'] ==
- 1;
- });
-
- MongoRunner.stopMongod(conn);
-
- if (jsTest.options().noJournal) {
- print("This test is being run with nojournal. Skipping ReplicaSet part.");
- return;
- }
-
- // Run a replica set to verify the contents of the `metadata` document.
- let rst = new ReplSetTest({name: "aggBackupCursor", nodes: 1});
- rst.startSet();
- rst.initiate();
- db = rst.getPrimary().getDB("test");
-
- backupCursor = db.aggregate([{$backupCursor: {}}]);
- // The metadata document should be returned first.
- let metadataDocEnvelope = backupCursor.next();
- assert(metadataDocEnvelope.hasOwnProperty("metadata"));
-
- let metadataDoc = metadataDocEnvelope["metadata"];
- let oplogStart = metadataDoc["oplogStart"];
- let oplogEnd = metadataDoc["oplogEnd"];
- let checkpointTimestamp = metadataDoc["checkpointTimestamp"];
-
- // When replication is run, there will always be an oplog with a start/end.
- assert(oplogStart);
- assert(oplogEnd);
- // The first opTime will likely have term -1 (repl initiation).
- assert.gte(oplogStart["t"], -1);
- // The last opTime's term must be a positive value larger than the first.
- assert.gte(oplogEnd["t"], oplogStart["t"]);
- assert.gte(oplogEnd["t"], 1);
- // The timestamp of the last optime must be larger than the first.
- assert.gte(oplogEnd["ts"], oplogStart["ts"]);
-
- // The checkpoint timestamp may or may not exist. If it exists, it must be between the start
- // and end.
- if (checkpointTimestamp != null) {
- assert.gte(checkpointTimestamp, oplogStart["ts"]);
- assert.gte(oplogEnd["ts"], checkpointTimestamp);
- }
-
- rst.stopSet();
-})();
diff --git a/jstests/noPassthrough/backup_restore_backup_cursor.js b/jstests/noPassthrough/backup_restore_backup_cursor.js
deleted file mode 100644
index de9fc8df88b..00000000000
--- a/jstests/noPassthrough/backup_restore_backup_cursor.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Test the backup/restore process:
- * - 3 node replica set
- * - Mongo CRUD client
- * - Mongo FSM client
- * - open a $backupCursor on the Secondary
- * - cp files returned by the $backupCursor
- * - close the $backupCursor
- * - Start mongod as hidden secondary
- * - Wait until new hidden node becomes secondary
- *
- * Some methods for backup used in this test checkpoint the files in the dbpath. This technique will
- * not work for ephemeral storage engines, as they do not store any data in the dbpath.
- * @tags: [requires_persistence, requires_replication]
- */
-
-load("jstests/noPassthrough/libs/backup_restore.js");
-
-(function() {
- "use strict";
-
- if (_isWindows()) {
- return;
- }
-
- // Run the fsyncLock test. Will return before testing for any engine that doesn't
- // support fsyncLock
- new BackupRestoreTest({backup: 'backupCursor'}).run();
-}());
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index b59dde57c0d..4c251b15bd6 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -327,7 +327,7 @@ env.Library(
'db/serveronly',
'db/service_context_d',
'db/startup_warnings_mongod',
- 'db/storage/backup_cursor_service',
+ 'db/storage/backup_cursor_hooks',
'db/system_index',
'db/ttl_d',
'executor/network_interface_factory',
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 1d99c2f9cb0..984e9ce80a2 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -174,7 +174,7 @@ env.Library(
'$BUILD_DIR/mongo/db/commands',
'$BUILD_DIR/mongo/db/concurrency/write_conflict_exception',
'$BUILD_DIR/mongo/db/curop',
- '$BUILD_DIR/mongo/db/storage/backup_cursor_service',
+ '$BUILD_DIR/mongo/db/storage/backup_cursor_hooks',
'fsync_locked',
]
)
diff --git a/src/mongo/db/commands/fsync.cpp b/src/mongo/db/commands/fsync.cpp
index 69a904578a8..7f68ba2cecd 100644
--- a/src/mongo/db/commands/fsync.cpp
+++ b/src/mongo/db/commands/fsync.cpp
@@ -48,7 +48,7 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/db.h"
#include "mongo/db/service_context.h"
-#include "mongo/db/storage/backup_cursor_service.h"
+#include "mongo/db/storage/backup_cursor_hooks.h"
#include "mongo/db/storage/storage_engine.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/util/assert_util.h"
@@ -355,14 +355,22 @@ void FSyncLockThread::run() {
}
bool successfulFsyncLock = false;
- auto backupCursorService = BackupCursorService::get(opCtx.getServiceContext());
+ auto backupCursorHooks = BackupCursorHooks::get(opCtx.getServiceContext());
try {
writeConflictRetry(&opCtx,
"beginBackup",
"global",
- [&opCtx, backupCursorService, &successfulFsyncLock] {
- backupCursorService->fsyncLock(&opCtx);
- successfulFsyncLock = true;
+ [&opCtx, backupCursorHooks, &successfulFsyncLock, storageEngine] {
+ if (backupCursorHooks->enabled()) {
+ backupCursorHooks->fsyncLock(&opCtx);
+ successfulFsyncLock = true;
+ } else {
+ // Have the uassert be caught by the DBException
+ // block. Maintain "allowFsyncFailure" compatibility in
+ // community.
+ uassertStatusOK(storageEngine->beginBackup(&opCtx));
+ successfulFsyncLock = true;
+ }
});
} catch (const DBException& e) {
if (_allowFsyncFailure) {
@@ -385,7 +393,11 @@ void FSyncLockThread::run() {
}
if (successfulFsyncLock) {
- backupCursorService->fsyncUnlock(&opCtx);
+ if (backupCursorHooks->enabled()) {
+ backupCursorHooks->fsyncUnlock(&opCtx);
+ } else {
+ storageEngine->endBackup(&opCtx);
+ }
}
} catch (const std::exception& e) {
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 06ce7d6787b..f92ee6409e9 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -123,7 +123,7 @@
#include "mongo/db/session_killer.h"
#include "mongo/db/startup_warnings_mongod.h"
#include "mongo/db/stats/counters.h"
-#include "mongo/db/storage/backup_cursor_service.h"
+#include "mongo/db/storage/backup_cursor_hooks.h"
#include "mongo/db/storage/encryption_hooks.h"
#include "mongo/db/storage/storage_engine.h"
#include "mongo/db/storage/storage_engine_init.h"
@@ -532,7 +532,7 @@ ExitCode _initAndListen(int listenPort) {
auto storageEngine = serviceContext->getStorageEngine();
invariant(storageEngine);
- BackupCursorService::set(serviceContext, stdx::make_unique<BackupCursorService>(storageEngine));
+ BackupCursorHooks::initialize(serviceContext, storageEngine);
if (!storageGlobalParams.readOnly) {
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index ec31d2ff975..86bce5e7e06 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -283,10 +283,12 @@ env.Library(
'$BUILD_DIR/mongo/db/ops/write_ops_exec',
'$BUILD_DIR/mongo/db/query_exec',
'$BUILD_DIR/mongo/db/stats/top',
- '$BUILD_DIR/mongo/db/storage/backup_cursor_service',
'$BUILD_DIR/mongo/s/sharding_api',
'mongo_process_common',
- ]
+ ],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/storage/backup_cursor_hooks',
+ ],
)
env.Library(
@@ -308,7 +310,6 @@ pipelineeEnv.Library(
source=[
'document_source.cpp',
'document_source_add_fields.cpp',
- 'document_source_backup_cursor.cpp',
'document_source_bucket.cpp',
'document_source_bucket_auto.cpp',
'document_source_change_stream.cpp',
diff --git a/src/mongo/db/pipeline/document_source_backup_cursor.cpp b/src/mongo/db/pipeline/document_source_backup_cursor.cpp
deleted file mode 100644
index cb1ce4264d5..00000000000
--- a/src/mongo/db/pipeline/document_source_backup_cursor.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * Copyright (C) 2018 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects
- * for all of the code used other than as permitted herein. If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. If you do not
- * wish to do so, delete this exception statement from your version. If you
- * delete this exception statement from all source files in the program,
- * then also delete it in the license file.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/pipeline/document_source_backup_cursor.h"
-
-#include <vector>
-
-#include "mongo/bson/bsonmisc.h"
-#include "mongo/util/log.h"
-
-namespace mongo {
-
-REGISTER_DOCUMENT_SOURCE(backupCursor,
- DocumentSourceBackupCursor::LiteParsed::parse,
- DocumentSourceBackupCursor::createFromBson);
-
-const char* DocumentSourceBackupCursor::kStageName = "$backupCursor";
-
-DocumentSourceBackupCursor::DocumentSourceBackupCursor(
- const boost::intrusive_ptr<ExpressionContext>& pExpCtx)
- : DocumentSource(pExpCtx),
- _backupCursorState(pExpCtx->mongoProcessInterface->openBackupCursor(pExpCtx->opCtx)) {}
-
-
-DocumentSourceBackupCursor::~DocumentSourceBackupCursor() {
- try {
- pExpCtx->mongoProcessInterface->closeBackupCursor(pExpCtx->opCtx,
- _backupCursorState.cursorId);
- } catch (DBException& exc) {
- severe() << exc.toStatus("Error closing a backup cursor.");
- fassertFailed(50909);
- }
-}
-
-DocumentSource::GetNextResult DocumentSourceBackupCursor::getNext() {
- pExpCtx->checkForInterrupt();
-
- if (_backupCursorState.preamble) {
- Document doc = _backupCursorState.preamble.get();
- _backupCursorState.preamble = boost::none;
-
- return std::move(doc);
- }
-
- if (!_backupCursorState.filenames.empty()) {
- Document doc = {{"filename", _backupCursorState.filenames.back()}};
- _backupCursorState.filenames.pop_back();
-
- return std::move(doc);
- }
-
- return GetNextResult::makeEOF();
-}
-
-boost::intrusive_ptr<DocumentSource> DocumentSourceBackupCursor::createFromBson(
- BSONElement spec, const boost::intrusive_ptr<ExpressionContext>& pExpCtx) {
- // The anticipated usage of a backup cursor: open the backup cursor, consume the results, copy
- // data off disk, close the backup cursor. The backup cursor must be successfully closed for
- // the data copied to be valid. Hence, the caller needs a way to keep the cursor open after
- // consuming the results, as well as the ability to send "heartbeats" to prevent the client
- // cursor manager from timing out the backup cursor. A backup cursor does consume resources;
- // in the event the calling process crashes, the cursors should eventually be timed out.
- pExpCtx->tailableMode = TailableModeEnum::kTailable;
-
- uassert(
- ErrorCodes::FailedToParse,
- str::stream() << kStageName << " value must be an object. Found: " << typeName(spec.type()),
- spec.type() == BSONType::Object);
-
- uassert(ErrorCodes::CannotBackup,
- str::stream() << kStageName << " cannot be executed against a MongoS.",
- !pExpCtx->inMongos && !pExpCtx->fromMongos && !pExpCtx->needsMerge);
-
- return new DocumentSourceBackupCursor(pExpCtx);
-}
-
-Value DocumentSourceBackupCursor::serialize(
- boost::optional<ExplainOptions::Verbosity> explain) const {
- return Value(BSON(kStageName << 1));
-}
-} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_backup_cursor.h b/src/mongo/db/pipeline/document_source_backup_cursor.h
deleted file mode 100644
index 5295c9159d8..00000000000
--- a/src/mongo/db/pipeline/document_source_backup_cursor.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * Copyright (C) 2018 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects
- * for all of the code used other than as permitted herein. If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. If you do not
- * wish to do so, delete this exception statement from your version. If you
- * delete this exception statement from all source files in the program,
- * then also delete it in the license file.
- */
-
-#pragma once
-
-#include <vector>
-
-#include "mongo/bson/bsonobj.h"
-#include "mongo/db/auth/action_set.h"
-#include "mongo/db/pipeline/document_source.h"
-#include "mongo/db/repl/read_concern_args.h"
-#include "mongo/db/storage/backup_cursor_service.h"
-
-namespace mongo {
-
-/**
- * Represents the `$backupCursor` aggregation stage. The lifetime of this object maps to storage
- * engine calls on `beginNonBlockingBackup` and `endNonBlockingBackup`. The DocumentSource will
- * return filenames in the running `dbpath` that an application can copy and optionally some
- * metadata information.
- */
-class DocumentSourceBackupCursor final : public DocumentSource {
-public:
- static const char* kStageName;
-
- class LiteParsed final : public LiteParsedDocumentSource {
- public:
- static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
- const BSONElement& spec) {
- return stdx::make_unique<LiteParsed>();
- }
-
- stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
- return stdx::unordered_set<NamespaceString>();
- }
-
- PrivilegeVector requiredPrivileges(bool isMongos) const final {
- return {Privilege(ResourcePattern::forClusterResource(), ActionSet{ActionType::fsync})};
- }
-
- bool isInitialSource() const final {
- return true;
- }
-
- bool allowedToForwardFromMongos() const final {
- return false;
- }
-
- void assertSupportsReadConcern(const repl::ReadConcernArgs& readConcern) const {
- uassert(ErrorCodes::InvalidOptions,
- str::stream() << "Aggregation stage " << kStageName
- << " requires read concern local but found "
- << readConcern.toString(),
- readConcern.getLevel() == repl::ReadConcernLevel::kLocalReadConcern);
- }
- };
-
- virtual ~DocumentSourceBackupCursor();
-
- GetNextResult getNext() final;
-
- const char* getSourceName() const final {
- return kStageName;
- }
-
- Value serialize(boost::optional<ExplainOptions::Verbosity> explain = boost::none) const final;
-
- StageConstraints constraints(Pipeline::SplitState pipeState) const final {
- StageConstraints constraints(StreamType::kStreaming,
- PositionRequirement::kFirst,
- // This stage `uasserts` on a MongoS; the
- // `HostTypeRequirement` field has no effect.
- HostTypeRequirement::kAnyShard,
- DiskUseRequirement::kNoDiskUse,
- FacetRequirement::kNotAllowed,
- TransactionRequirement::kNotAllowed);
-
- constraints.isIndependentOfAnyCollection = true;
- constraints.requiresInputDocSource = false;
- return constraints;
- }
-
- static boost::intrusive_ptr<DocumentSource> createFromBson(
- BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& pExpCtx);
-
-private:
- DocumentSourceBackupCursor(const boost::intrusive_ptr<ExpressionContext>& pExpCtx);
-
- BackupCursorState _backupCursorState;
-};
-
-} // namespace mongo
diff --git a/src/mongo/db/pipeline/mongo_process_interface.h b/src/mongo/db/pipeline/mongo_process_interface.h
index 890f5565f87..974fd2807d4 100644
--- a/src/mongo/db/pipeline/mongo_process_interface.h
+++ b/src/mongo/db/pipeline/mongo_process_interface.h
@@ -45,7 +45,7 @@
#include "mongo/db/pipeline/lite_parsed_document_source.h"
#include "mongo/db/pipeline/value.h"
#include "mongo/db/query/explain_options.h"
-#include "mongo/db/storage/backup_cursor_service.h"
+#include "mongo/db/storage/backup_cursor_state.h"
namespace mongo {
@@ -231,12 +231,8 @@ public:
CurrentOpUserMode userMode) const = 0;
/**
- * The following methods forward to the BackupCursorService decorating the ServiceContext.
+ * The following methods forward to the BackupCursorHooks decorating the ServiceContext.
*/
- virtual void fsyncLock(OperationContext* opCtx) = 0;
-
- virtual void fsyncUnlock(OperationContext* opCtx) = 0;
-
virtual BackupCursorState openBackupCursor(OperationContext* opCtx) = 0;
virtual void closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId) = 0;
diff --git a/src/mongo/db/pipeline/mongod_process_interface.cpp b/src/mongo/db/pipeline/mongod_process_interface.cpp
index 719ee010b04..0141726f22e 100644
--- a/src/mongo/db/pipeline/mongod_process_interface.cpp
+++ b/src/mongo/db/pipeline/mongod_process_interface.cpp
@@ -49,6 +49,7 @@
#include "mongo/db/session_catalog.h"
#include "mongo/db/stats/fill_locker_info.h"
#include "mongo/db/stats/storage_stats.h"
+#include "mongo/db/storage/backup_cursor_hooks.h"
#include "mongo/db/transaction_participant.h"
#include "mongo/s/catalog_cache.h"
#include "mongo/s/grid.h"
@@ -437,24 +438,22 @@ boost::optional<Document> MongoDInterface::lookupSingleDocument(
return lookedUpDocument;
}
-void MongoDInterface::fsyncLock(OperationContext* opCtx) {
- auto backupCursorService = BackupCursorService::get(opCtx->getServiceContext());
- backupCursorService->fsyncLock(opCtx);
-}
-
-void MongoDInterface::fsyncUnlock(OperationContext* opCtx) {
- auto backupCursorService = BackupCursorService::get(opCtx->getServiceContext());
- backupCursorService->fsyncUnlock(opCtx);
-}
-
BackupCursorState MongoDInterface::openBackupCursor(OperationContext* opCtx) {
- auto backupCursorService = BackupCursorService::get(opCtx->getServiceContext());
- return backupCursorService->openBackupCursor(opCtx);
+ auto backupCursorHooks = BackupCursorHooks::get(opCtx->getServiceContext());
+ if (backupCursorHooks->enabled()) {
+ return backupCursorHooks->openBackupCursor(opCtx);
+ } else {
+ uasserted(50956, "Backup cursors are an enterprise only feature.");
+ }
}
void MongoDInterface::closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId) {
- auto backupCursorService = BackupCursorService::get(opCtx->getServiceContext());
- backupCursorService->closeBackupCursor(opCtx, cursorId);
+ auto backupCursorHooks = BackupCursorHooks::get(opCtx->getServiceContext());
+ if (backupCursorHooks->enabled()) {
+ backupCursorHooks->closeBackupCursor(opCtx, cursorId);
+ } else {
+ uasserted(50955, "Backup cursors are an enterprise only feature.");
+ }
}
std::vector<BSONObj> MongoDInterface::getMatchingPlanCacheEntryStats(
diff --git a/src/mongo/db/pipeline/mongod_process_interface.h b/src/mongo/db/pipeline/mongod_process_interface.h
index 5667c8a5158..cb9614f2172 100644
--- a/src/mongo/db/pipeline/mongod_process_interface.h
+++ b/src/mongo/db/pipeline/mongod_process_interface.h
@@ -93,8 +93,6 @@ public:
boost::optional<BSONObj> readConcern) final;
std::vector<GenericCursor> getIdleCursors(const boost::intrusive_ptr<ExpressionContext>& expCtx,
CurrentOpUserMode userMode) const final;
- void fsyncLock(OperationContext* opCtx) final;
- void fsyncUnlock(OperationContext* opCtx) final;
BackupCursorState openBackupCursor(OperationContext* opCtx) final;
void closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId) final;
diff --git a/src/mongo/db/pipeline/mongos_process_interface.h b/src/mongo/db/pipeline/mongos_process_interface.h
index a9092028cd5..1fed694e47a 100644
--- a/src/mongo/db/pipeline/mongos_process_interface.h
+++ b/src/mongo/db/pipeline/mongos_process_interface.h
@@ -136,14 +136,6 @@ public:
* The following methods only make sense for data-bearing nodes and should never be called on
* a mongos.
*/
- void fsyncLock(OperationContext* opCtx) final {
- MONGO_UNREACHABLE;
- }
-
- void fsyncUnlock(OperationContext* opCtx) final {
- MONGO_UNREACHABLE;
- }
-
BackupCursorState openBackupCursor(OperationContext* opCtx) final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/pipeline/stub_mongo_process_interface.h b/src/mongo/db/pipeline/stub_mongo_process_interface.h
index a3df25fc49b..1d1f9a94562 100644
--- a/src/mongo/db/pipeline/stub_mongo_process_interface.h
+++ b/src/mongo/db/pipeline/stub_mongo_process_interface.h
@@ -154,14 +154,6 @@ public:
MONGO_UNREACHABLE;
}
- void fsyncLock(OperationContext* opCtx) final {
- MONGO_UNREACHABLE;
- }
-
- void fsyncUnlock(OperationContext* opCtx) final {
- MONGO_UNREACHABLE;
- }
-
BackupCursorState openBackupCursor(OperationContext* opCtx) final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index 50c884dd6e6..366a429e58d 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -95,6 +95,20 @@ env.Library(
)
env.Library(
+ target='backup_cursor_hooks',
+ source=[
+ 'backup_cursor_hooks.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/service_context',
+ ],
+ LIBDEPS_DEPENDENTS=[
+ '$BUILD_DIR/mongo/mongodmain',
+ ],
+)
+
+env.Library(
target='test_harness_helper',
source=[
'test_harness_helper.cpp',
@@ -291,27 +305,6 @@ env.CppUnitTest(
],
)
-
-env.Library(
- target='backup_cursor_service',
- source=[
- 'backup_cursor_service.cpp',
- ],
- LIBDEPS=[
- '$BUILD_DIR/mongo/base',
- ],
- LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/db/db_raii',
- '$BUILD_DIR/mongo/db/dbhelpers',
- '$BUILD_DIR/mongo/db/namespace_string',
- '$BUILD_DIR/mongo/db/pipeline/document_value',
- '$BUILD_DIR/mongo/db/repl/oplog_entry',
- '$BUILD_DIR/mongo/db/repl/optime',
- 'encryption_hooks',
- 'storage_options',
- ],
-)
-
env.CppUnitTest(
target= 'storage_engine_metadata_test',
source = 'storage_engine_metadata_test.cpp',
@@ -337,13 +330,3 @@ env.Benchmark(
'$BUILD_DIR/mongo/base',
],
)
-
-env.CppUnitTest(
- target='backup_cursor_service_test',
- source='backup_cursor_service_test.cpp',
- LIBDEPS=[
- '$BUILD_DIR/mongo/db/auth/authmocks',
- '$BUILD_DIR/mongo/db/service_context_d_test_fixture',
- 'backup_cursor_service',
- ],
-)
diff --git a/src/mongo/db/storage/backup_cursor_hooks.cpp b/src/mongo/db/storage/backup_cursor_hooks.cpp
new file mode 100644
index 00000000000..d4c4a863fb2
--- /dev/null
+++ b/src/mongo/db/storage/backup_cursor_hooks.cpp
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/storage/backup_cursor_hooks.h"
+
+#include "mongo/base/init.h"
+#include "mongo/db/service_context.h"
+
+namespace mongo {
+
+namespace {
+BackupCursorHooks::InitializerFunction initializer = [](StorageEngine* storageEngine) {
+ return stdx::make_unique<BackupCursorHooks>();
+};
+
+struct BackupCursorHooksHolder {
+ std::unique_ptr<BackupCursorHooks> ptr = std::make_unique<BackupCursorHooks>();
+};
+
+const auto getBackupCursorHooks = ServiceContext::declareDecoration<BackupCursorHooksHolder>();
+} // namespace
+
+void BackupCursorHooks::registerInitializer(InitializerFunction func) {
+ initializer = func;
+}
+
+void BackupCursorHooks::initialize(ServiceContext* service, StorageEngine* storageEngine) {
+ getBackupCursorHooks(service).ptr = initializer(storageEngine);
+}
+
+BackupCursorHooks* BackupCursorHooks::get(ServiceContext* service) {
+ return getBackupCursorHooks(service).ptr.get();
+}
+
+BackupCursorHooks::~BackupCursorHooks() {}
+
+bool BackupCursorHooks::enabled() const {
+ return false;
+}
+
+/**
+ * The following methods cannot be called when BackupCursorHooks is not enabled.
+ */
+void BackupCursorHooks::fsyncLock(OperationContext* opCtx) {
+ MONGO_UNREACHABLE;
+}
+
+void BackupCursorHooks::fsyncUnlock(OperationContext* opCtx) {
+ MONGO_UNREACHABLE;
+}
+
+BackupCursorState BackupCursorHooks::openBackupCursor(OperationContext* opCtx) {
+ MONGO_UNREACHABLE;
+}
+
+void BackupCursorHooks::closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId) {
+ MONGO_UNREACHABLE;
+}
+} // namespace mongo
diff --git a/src/mongo/db/storage/backup_cursor_hooks.h b/src/mongo/db/storage/backup_cursor_hooks.h
new file mode 100644
index 00000000000..7344e5d391e
--- /dev/null
+++ b/src/mongo/db/storage/backup_cursor_hooks.h
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "mongo/db/storage/backup_cursor_state.h"
+
+namespace mongo {
+class OperationContext;
+class ServiceContext;
+class StorageEngine;
+
+class BackupCursorHooks {
+public:
+ using InitializerFunction = std::function<std::unique_ptr<BackupCursorHooks>(StorageEngine*)>;
+
+ static void registerInitializer(InitializerFunction func);
+
+ static void initialize(ServiceContext* service, StorageEngine* storageEngine);
+
+ static BackupCursorHooks* get(ServiceContext* service);
+
+ virtual ~BackupCursorHooks();
+
+ /**
+ * Returns true if the backup cursor hooks are enabled. If this returns false, none of the
+ * other methods on this class may be called.
+ */
+ virtual bool enabled() const;
+
+ virtual void fsyncLock(OperationContext* opCtx);
+
+ virtual void fsyncUnlock(OperationContext* opCtx);
+
+ virtual BackupCursorState openBackupCursor(OperationContext* opCtx);
+
+ virtual void closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId);
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/storage/backup_cursor_service.cpp b/src/mongo/db/storage/backup_cursor_service.cpp
deleted file mode 100644
index e83dc22cfc9..00000000000
--- a/src/mongo/db/storage/backup_cursor_service.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * Copyright (C) 2018 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/storage/backup_cursor_service.h"
-
-#include "mongo/db/db_raii.h"
-#include "mongo/db/dbhelpers.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/repl/oplog_entry.h"
-#include "mongo/db/service_context.h"
-#include "mongo/db/storage/encryption_hooks.h"
-#include "mongo/db/storage/storage_engine.h"
-#include "mongo/db/storage/storage_options.h"
-#include "mongo/util/fail_point.h"
-#include "mongo/util/log.h"
-#include "mongo/util/scopeguard.h"
-
-namespace mongo {
-namespace {
-
-MONGO_FAIL_POINT_DEFINE(backupCursorErrorAfterOpen);
-
-const auto getBackupCursorService =
- ServiceContext::declareDecoration<std::unique_ptr<BackupCursorService>>();
-} // namespace
-
-BackupCursorService* BackupCursorService::get(ServiceContext* service) {
- return getBackupCursorService(service).get();
-}
-
-void BackupCursorService::set(ServiceContext* service,
- std::unique_ptr<BackupCursorService> backupCursorService) {
- auto& decoration = getBackupCursorService(service);
- decoration = std::move(backupCursorService);
-}
-
-void BackupCursorService::fsyncLock(OperationContext* opCtx) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- uassert(50885, "The node is already fsyncLocked.", _state != kFsyncLocked);
- uassert(50884,
- "The existing backup cursor must be closed before fsyncLock can succeed.",
- _state != kBackupCursorOpened);
- uassertStatusOK(_storageEngine->beginBackup(opCtx));
- _state = kFsyncLocked;
-}
-
-void BackupCursorService::fsyncUnlock(OperationContext* opCtx) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- uassert(50888, "The node is not fsyncLocked.", _state == kFsyncLocked);
- _storageEngine->endBackup(opCtx);
- _state = kInactive;
-}
-
-BackupCursorState BackupCursorService::openBackupCursor(OperationContext* opCtx) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- uassert(50887, "The node is currently fsyncLocked.", _state != kFsyncLocked);
- uassert(50886,
- "The existing backup cursor must be closed before $backupCursor can succeed.",
- _state != kBackupCursorOpened);
-
- // Replica sets must also return the opTime's of the earliest and latest oplog entry. The
- // range represented by the oplog start/end values must exist in the backup copy, but are not
- // expected to be exact.
- repl::OpTime oplogStart;
- repl::OpTime oplogEnd;
-
- // If the oplog exists, capture the last oplog entry before opening the backup cursor. This
- // value will be checked again after the cursor is established to guarantee it still exists
- // (and was not truncated before the backup cursor was established.
- {
- AutoGetCollectionForRead coll(opCtx, NamespaceString::kRsOplogNamespace);
- if (coll.getCollection()) {
- BSONObj lastEntry;
- uassert(50914,
- str::stream() << "No oplog records were found.",
- Helpers::getLast(
- opCtx, NamespaceString::kRsOplogNamespace.ns().c_str(), lastEntry));
- auto oplogEntry = fassertNoTrace(50913, repl::OplogEntry::parse(lastEntry));
- oplogEnd = oplogEntry.getOpTime();
- }
- }
-
- // Capture the checkpointTimestamp before and after opening a cursor. If it hasn't moved, the
- // checkpointTimestamp is known to be exact. If it has moved, uassert and have the user retry.
- boost::optional<Timestamp> checkpointTimestamp;
- if (_storageEngine->supportsRecoverToStableTimestamp()) {
- checkpointTimestamp = _storageEngine->getLastStableRecoveryTimestamp();
- };
-
- auto filesToBackup = uassertStatusOK(_storageEngine->beginNonBlockingBackup(opCtx));
- _state = kBackupCursorOpened;
- _openCursor = ++_cursorIdGenerator;
- log() << "Opened backup cursor. ID: " << _openCursor.get();
-
- // A backup cursor is open. Any exception code path must leave the BackupCursorService in an
- // inactive state.
- auto closeCursorGuard =
- MakeGuard([this, opCtx, &lk] { _closeBackupCursor(opCtx, _openCursor.get(), lk); });
-
- uassert(50919,
- "Failpoint hit after opening the backup cursor.",
- !MONGO_FAIL_POINT(backupCursorErrorAfterOpen));
-
- // Ensure the checkpointTimestamp hasn't moved. A subtle case to catch is the first stable
- // checkpoint coming out of initial sync racing with opening the backup cursor.
- if (checkpointTimestamp && _storageEngine->supportsRecoverToStableTimestamp()) {
- auto requeriedCheckpointTimestamp = _storageEngine->getLastStableRecoveryTimestamp();
- if (!requeriedCheckpointTimestamp ||
- requeriedCheckpointTimestamp.get() < checkpointTimestamp.get()) {
- severe() << "The last stable recovery timestamp went backwards. Original: "
- << checkpointTimestamp.get() << " Found: " << requeriedCheckpointTimestamp;
- fassertFailed(50916);
- }
-
- uassert(50915,
- str::stream() << "A checkpoint took place while opening a backup cursor.",
- checkpointTimestamp == requeriedCheckpointTimestamp);
- };
-
- // If the oplog exists, capture the first oplog entry after opening the backup cursor. Ensure
- // it is before the `oplogEnd` value.
- if (!oplogEnd.isNull()) {
- BSONObj firstEntry;
- uassert(50912,
- str::stream() << "No oplog records were found.",
- Helpers::getSingleton(
- opCtx, NamespaceString::kRsOplogNamespace.ns().c_str(), firstEntry));
- auto oplogEntry = fassertNoTrace(50918, repl::OplogEntry::parse(firstEntry));
- oplogStart = oplogEntry.getOpTime();
- uassert(50917,
- str::stream() << "Oplog rolled over while establishing the backup cursor.",
- oplogStart < oplogEnd);
- }
-
- auto encHooks = EncryptionHooks::get(opCtx->getServiceContext());
- if (encHooks->enabled()) {
- auto eseFiles = uassertStatusOK(encHooks->beginNonBlockingBackup());
- filesToBackup.insert(filesToBackup.end(), eseFiles.begin(), eseFiles.end());
- }
-
- BSONObjBuilder builder;
- builder << "dbpath" << storageGlobalParams.dbpath;
- if (!oplogStart.isNull()) {
- builder << "oplogStart" << oplogStart.toBSON();
- builder << "oplogEnd" << oplogEnd.toBSON();
- }
-
- // Notably during initial sync, a node may have an oplog without a stable checkpoint.
- if (checkpointTimestamp) {
- builder << "checkpointTimestamp" << checkpointTimestamp.get();
- }
-
- Document preamble{{"metadata", builder.obj()}};
-
- closeCursorGuard.Dismiss();
- return {_openCursor.get(), preamble, filesToBackup};
-}
-
-void BackupCursorService::closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- _closeBackupCursor(opCtx, cursorId, lk);
-}
-
-void BackupCursorService::_closeBackupCursor(OperationContext* opCtx,
- std::uint64_t cursorId,
- WithLock) {
- uassert(50880, "There is no backup cursor to close.", _state == kBackupCursorOpened);
- uassert(50879,
- str::stream() << "Can only close the running backup cursor. To close: " << cursorId
- << " Running: "
- << _openCursor.get(),
- cursorId == _openCursor.get());
- _storageEngine->endNonBlockingBackup(opCtx);
- auto encHooks = EncryptionHooks::get(opCtx->getServiceContext());
- if (encHooks->enabled()) {
- fassert(50934, encHooks->endNonBlockingBackup());
- }
- log() << "Closed backup cursor. ID: " << cursorId;
- _state = kInactive;
- _openCursor = boost::none;
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/storage/backup_cursor_service.h b/src/mongo/db/storage/backup_cursor_service.h
deleted file mode 100644
index aa6e4a562cf..00000000000
--- a/src/mongo/db/storage/backup_cursor_service.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * Copyright (C) 2018 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include <boost/optional.hpp>
-#include <memory>
-#include <vector>
-
-#include "mongo/base/disallow_copying.h"
-#include "mongo/db/pipeline/document.h"
-#include "mongo/stdx/mutex.h"
-#include "mongo/util/concurrency/with_lock.h"
-
-namespace mongo {
-
-class OperationContext;
-class ServiceContext;
-class StorageEngine;
-
-struct BackupCursorState {
- std::uint64_t cursorId;
- boost::optional<Document> preamble;
- std::vector<std::string> filenames;
-};
-
-/**
- * MongoDB exposes two separate client APIs for locking down files on disk for a file-system based
- * backup that require only one pass of the data to complete. First is using the `fsync` command
- * with `lock: true`. The second is using the `$backupCursor` aggregation stage. It does not make
- * sense for both techniques to be concurrently engaged. This class will manage access to these
- * resources such that their lifetimes do not overlap. More specifically, an `fsyncLock` mode can
- * only be freed by `fsyncUnlock` and `openBackupCursor` by `closeBackupCursor`.
- *
- * Failure to comply is expected to be due to user error and this class is best situated to detect
- * such errors. For this reason, callers should assume all method calls can only fail with
- * uassert exceptions.
- */
-class BackupCursorService {
- MONGO_DISALLOW_COPYING(BackupCursorService);
-
-public:
- static BackupCursorService* get(ServiceContext* service);
- static void set(ServiceContext* service,
- std::unique_ptr<BackupCursorService> backupCursorService);
-
- BackupCursorService(StorageEngine* storageEngine) : _storageEngine(storageEngine) {}
-
- /**
- * This method will uassert if `_state` is not `kInactive`. Otherwise, the method forwards to
- * `StorageEngine::beginBackup`.
- */
- void fsyncLock(OperationContext* opCtx);
-
- /**
- * This method will uassert if `_state` is not `kFsyncLocked`. Otherwise, the method forwards
- * to `StorageEngine::endBackup`.
- */
- void fsyncUnlock(OperationContext* opCtx);
-
- /**
- * This method will uassert if `_state` is not `kInactive`. Otherwise, the method forwards to
- * `StorageEngine::beginNonBlockingBackup`.
- *
- * Returns a BackupCursorState. The structure's `cursorId` must be passed to
- * `closeBackupCursor` to successfully exit this backup mode. `filenames` is a list of files
- * relative to the server's `dbpath` that must be copied by the application to complete a
- * backup.
- */
- BackupCursorState openBackupCursor(OperationContext* opCtx);
-
- /**
- * This method will uassert if `_state` is not `kBackupCursorOpened`, or the `cursorId` input
- * is not the active backup cursor open known to `_openCursor`.
- */
- void closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId);
-
-private:
- void _closeBackupCursor(OperationContext* opCtx, std::uint64_t cursorId, WithLock);
-
- StorageEngine* _storageEngine;
-
- enum State { kInactive, kFsyncLocked, kBackupCursorOpened };
-
- // This mutex serializes all access into this class.
- stdx::mutex _mutex;
- State _state = kInactive;
- // When state is `kBackupCursorOpened`, _openCursor contains the cursorId of the active backup
- // cursor. Otherwise it is boost::none.
- boost::optional<std::uint64_t> _openCursor = boost::none;
- // _cursorIdGenerator is incremented and returned each time a new backup cursor is
- // opened. While `fsyncLock`ing a system does open a WT backup cursor, the terminology keeps
- // the two separate here.
- std::uint64_t _cursorIdGenerator = 0;
-};
-
-} // namespace mongo
diff --git a/src/mongo/db/storage/backup_cursor_service_test.cpp b/src/mongo/db/storage/backup_cursor_service_test.cpp
deleted file mode 100644
index 9f6a1958fa7..00000000000
--- a/src/mongo/db/storage/backup_cursor_service_test.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/**
- * Copyright (C) 2018 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/storage/backup_cursor_service.h"
-
-#include "mongo/db/client.h"
-#include "mongo/db/service_context.h"
-#include "mongo/db/service_context_d_test_fixture.h"
-#include "mongo/db/storage/devnull/devnull_kv_engine.h"
-#include "mongo/db/storage/kv/kv_storage_engine.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-namespace {
-
-class BackupCursorServiceTest : public ServiceContextMongoDTest {
-public:
- BackupCursorServiceTest()
- : ServiceContextMongoDTest("devnull"),
- _opCtx(cc().makeOperationContext()),
- _storageEngine(getServiceContext()->getStorageEngine()),
- _backupCursorService(stdx::make_unique<BackupCursorService>(_storageEngine)) {}
-
-protected:
- ServiceContext::UniqueOperationContext _opCtx;
- StorageEngine* _storageEngine;
- std::unique_ptr<BackupCursorService> _backupCursorService;
-};
-
-TEST_F(BackupCursorServiceTest, TestTypicalFsyncLifetime) {
- _backupCursorService->fsyncLock(_opCtx.get());
- _backupCursorService->fsyncUnlock(_opCtx.get());
-
- _backupCursorService->fsyncLock(_opCtx.get());
- _backupCursorService->fsyncUnlock(_opCtx.get());
-}
-
-TEST_F(BackupCursorServiceTest, TestDoubleLock) {
- _backupCursorService->fsyncLock(_opCtx.get());
- ASSERT_THROWS_WHAT(_backupCursorService->fsyncLock(_opCtx.get()),
- DBException,
- "The node is already fsyncLocked.");
- _backupCursorService->fsyncUnlock(_opCtx.get());
-}
-
-TEST_F(BackupCursorServiceTest, TestDoubleUnlock) {
- ASSERT_THROWS_WHAT(_backupCursorService->fsyncUnlock(_opCtx.get()),
- DBException,
- "The node is not fsyncLocked.");
-
- _backupCursorService->fsyncLock(_opCtx.get());
- _backupCursorService->fsyncUnlock(_opCtx.get());
- ASSERT_THROWS_WHAT(_backupCursorService->fsyncUnlock(_opCtx.get()),
- DBException,
- "The node is not fsyncLocked.");
-}
-
-TEST_F(BackupCursorServiceTest, TestTypicalCursorLifetime) {
- auto backupCursorState = _backupCursorService->openBackupCursor(_opCtx.get());
- ASSERT_EQUALS(1u, backupCursorState.cursorId);
- ASSERT_EQUALS(1u, backupCursorState.filenames.size());
- ASSERT_EQUALS("filename.wt", backupCursorState.filenames[0]);
-
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId);
-
- backupCursorState = _backupCursorService->openBackupCursor(_opCtx.get());
- ASSERT_EQUALS(2u, backupCursorState.cursorId);
- ASSERT_EQUALS(1u, backupCursorState.filenames.size());
- ASSERT_EQUALS("filename.wt", backupCursorState.filenames[0]);
-
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId);
-}
-
-TEST_F(BackupCursorServiceTest, TestDoubleOpenCursor) {
- auto backupCursorState = _backupCursorService->openBackupCursor(_opCtx.get());
- ASSERT_THROWS_WHAT(
- _backupCursorService->openBackupCursor(_opCtx.get()),
- DBException,
- "The existing backup cursor must be closed before $backupCursor can succeed.");
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId);
-}
-
-TEST_F(BackupCursorServiceTest, TestDoubleCloseCursor) {
- ASSERT_THROWS_WHAT(_backupCursorService->closeBackupCursor(_opCtx.get(), 10),
- DBException,
- "There is no backup cursor to close.");
-
- auto backupCursorState = _backupCursorService->openBackupCursor(_opCtx.get());
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId);
- ASSERT_THROWS_WHAT(
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId),
- DBException,
- "There is no backup cursor to close.");
-}
-
-TEST_F(BackupCursorServiceTest, TestCloseWrongCursor) {
- auto backupCursorState = _backupCursorService->openBackupCursor(_opCtx.get());
-
- ASSERT_THROWS_WITH_CHECK(
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId + 1),
- DBException,
- [](const DBException& exc) {
- ASSERT_STRING_CONTAINS(exc.what(), "Can only close the running backup cursor.");
- });
-
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId);
-}
-
-TEST_F(BackupCursorServiceTest, TestMixingFsyncAndCursors) {
- _backupCursorService->fsyncLock(_opCtx.get());
- ASSERT_THROWS_WHAT(_backupCursorService->openBackupCursor(_opCtx.get()),
- DBException,
- "The node is currently fsyncLocked.");
- ASSERT_THROWS_WHAT(_backupCursorService->closeBackupCursor(_opCtx.get(), 1),
- DBException,
- "There is no backup cursor to close.");
- _backupCursorService->fsyncUnlock(_opCtx.get());
-
- auto backupCursorState = _backupCursorService->openBackupCursor(_opCtx.get());
- ASSERT_THROWS_WHAT(_backupCursorService->fsyncLock(_opCtx.get()),
- DBException,
- "The existing backup cursor must be closed before fsyncLock can succeed.");
- ASSERT_THROWS_WHAT(_backupCursorService->fsyncUnlock(_opCtx.get()),
- DBException,
- "The node is not fsyncLocked.");
- _backupCursorService->closeBackupCursor(_opCtx.get(), backupCursorState.cursorId);
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/storage/backup_cursor_state.h b/src/mongo/db/storage/backup_cursor_state.h
new file mode 100644
index 00000000000..584d61e800d
--- /dev/null
+++ b/src/mongo/db/storage/backup_cursor_state.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <string>
+#include <vector>
+
+#include "mongo/db/pipeline/document.h"
+
+namespace mongo {
+
+struct BackupCursorState {
+ std::uint64_t cursorId;
+ boost::optional<Document> preamble;
+ std::vector<std::string> filenames;
+};
+
+} // namespace mongo