diff options
author | Daniel Gottlieb <daniel.gottlieb@mongodb.com> | 2018-09-20 15:00:47 -0400 |
---|---|---|
committer | Daniel Gottlieb <daniel.gottlieb@mongodb.com> | 2018-09-20 15:00:47 -0400 |
commit | 630eabac0591f207b29b6be014257387a9a7a904 (patch) | |
tree | 463bd23d64f3dec795b2fcf769d88a8fbe10ef50 /src/mongo/db/storage | |
parent | 4ec12c35a07a8c0f3a30692aec413a71fdab30de (diff) | |
download | mongo-630eabac0591f207b29b6be014257387a9a7a904.tar.gz |
SERVER-37192: Move $backupCursor to enterprise.
Diffstat (limited to 'src/mongo/db/storage')
-rw-r--r-- | src/mongo/db/storage/SConscript | 45 | ||||
-rw-r--r-- | src/mongo/db/storage/backup_cursor_hooks.cpp | 87 | ||||
-rw-r--r-- | src/mongo/db/storage/backup_cursor_hooks.h | 69 | ||||
-rw-r--r-- | src/mongo/db/storage/backup_cursor_service.cpp | 212 | ||||
-rw-r--r-- | src/mongo/db/storage/backup_cursor_service.h | 122 | ||||
-rw-r--r-- | src/mongo/db/storage/backup_cursor_service_test.cpp | 157 | ||||
-rw-r--r-- | src/mongo/db/storage/backup_cursor_state.h | 45 |
7 files changed, 215 insertions, 522 deletions
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 |