From c1964e817459a575a64609229a473d57d08aa4d0 Mon Sep 17 00:00:00 2001 From: Benety Goh Date: Thu, 9 Mar 2017 14:50:59 -0500 Subject: SERVER-28204 added RollbackTest fixture --- src/mongo/db/repl/SConscript | 19 +++- src/mongo/db/repl/rollback_test_fixture.cpp | 115 ++++++++++++++++++++ src/mongo/db/repl/rollback_test_fixture.h | 118 +++++++++++++++++++++ src/mongo/db/repl/rs_rollback_test.cpp | 58 +--------- src/mongo/db/service_context_d_test_fixture.cpp | 5 + src/mongo/db/service_context_d_test_fixture.h | 12 ++- src/mongo/executor/task_executor_test_fixture.cpp | 5 + src/mongo/executor/task_executor_test_fixture.h | 11 +- .../thread_pool_task_executor_test_fixture.cpp | 7 +- .../thread_pool_task_executor_test_fixture.h | 16 +++ 10 files changed, 308 insertions(+), 58 deletions(-) create mode 100644 src/mongo/db/repl/rollback_test_fixture.cpp create mode 100644 src/mongo/db/repl/rollback_test_fixture.h diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 54ae8e436c0..8bf2a589003 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -300,6 +300,22 @@ env.Library( ], ) +env.Library( + target='rollback_test_fixture', + source=[ + 'rollback_test_fixture.cpp', + ], + LIBDEPS=[ + 'optime', + 'repl_settings', + 'replmocks', + #'serveronly', # CYCLE + '$BUILD_DIR/mongo/db/service_context', + '$BUILD_DIR/mongo/db/service_context_d_test_fixture', + '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', + ], +) + env.CppUnitTest( target='rs_rollback_test', source=[ @@ -308,9 +324,8 @@ env.CppUnitTest( LIBDEPS=[ 'oplog_interface_local', 'oplog_interface_mock', - 'replmocks', + 'rollback_test_fixture', 'rs_rollback', - '$BUILD_DIR/mongo/db/service_context_d_test_fixture', ], ) diff --git a/src/mongo/db/repl/rollback_test_fixture.cpp b/src/mongo/db/repl/rollback_test_fixture.cpp new file mode 100644 index 00000000000..764a84a281d --- /dev/null +++ b/src/mongo/db/repl/rollback_test_fixture.cpp @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2017 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 . + * + * 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/repl/rollback_test_fixture.h" + +#include + +#include "mongo/db/client.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/replication_coordinator.h" +#include "mongo/db/repl/replication_coordinator_mock.h" + +namespace mongo { +namespace repl { + +namespace { + +/** + * Creates ReplSettings for ReplicationCoordinatorRollbackMock. + */ +ReplSettings createReplSettings() { + ReplSettings settings; + settings.setOplogSizeBytes(5 * 1024 * 1024); + settings.setReplSetString("mySet/node1:12345"); + return settings; +} + +/** + * Creates ThreadPoolMock::Options that initializes a Client for every thread created in the pool. + */ +executor::ThreadPoolMock::Options createThreadPoolOptions() { + executor::ThreadPoolMock::Options options; + options.onCreateThread = []() { Client::initThread("RollbackTest"); }; + return options; +} + +} // namespace + +RollbackTest::RollbackTest() : _threadPoolExecutorTest(createThreadPoolOptions()) {} + +void RollbackTest::setUp() { + _serviceContextMongoDTest.setUp(); + _threadPoolExecutorTest.setUp(); + _opCtx = cc().makeOperationContext(); + auto serviceContext = _serviceContextMongoDTest.getServiceContext(); + _coordinator = new ReplicationCoordinatorRollbackMock(serviceContext); + ReplicationCoordinator::set(serviceContext, + std::unique_ptr(_coordinator)); + setOplogCollectionName(); + _storageInterface.setAppliedThrough(_opCtx.get(), OpTime{}); + _storageInterface.setMinValid(_opCtx.get(), OpTime{}); + + _threadPoolExecutorTest.launchExecutorThread(); +} + +void RollbackTest::tearDown() { + _coordinator = nullptr; + _opCtx.reset(); + _threadPoolExecutorTest.tearDown(); + + // We cannot unset the global replication coordinator because ServiceContextMongoD::tearDown() + // calls dropAllDatabasesExceptLocal() which requires the replication coordinator to clear all + // snapshots. + _serviceContextMongoDTest.tearDown(); + + // ServiceContextMongoD::tearDown() does not destroy service context so it is okay + // to access the service context after tearDown(). + auto serviceContext = _serviceContextMongoDTest.getServiceContext(); + ReplicationCoordinator::set(serviceContext, {}); +} + +RollbackTest::ReplicationCoordinatorRollbackMock::ReplicationCoordinatorRollbackMock( + ServiceContext* service) + : ReplicationCoordinatorMock(service, createReplSettings()) {} + +void RollbackTest::ReplicationCoordinatorRollbackMock::resetLastOpTimesFromOplog( + OperationContext* opCtx) {} + +bool RollbackTest::ReplicationCoordinatorRollbackMock::setFollowerMode( + const MemberState& newState) { + if (newState == _failSetFollowerModeOnThisMemberState) { + return false; + } + return ReplicationCoordinatorMock::setFollowerMode(newState); +} + +} // namespace repl +} // namespace mongo diff --git a/src/mongo/db/repl/rollback_test_fixture.h b/src/mongo/db/repl/rollback_test_fixture.h new file mode 100644 index 00000000000..ece8cd682d2 --- /dev/null +++ b/src/mongo/db/repl/rollback_test_fixture.h @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2017 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 . + * + * 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 "mongo/db/repl/replication_coordinator_mock.h" +#include "mongo/db/repl/storage_interface_mock.h" +#include "mongo/db/service_context.h" +#include "mongo/db/service_context_d_test_fixture.h" +#include "mongo/executor/thread_pool_task_executor_test_fixture.h" + +namespace mongo { +namespace repl { + +/** + * Test fixture for both 3.4 and 3.6 rollback unit tests. + * The fixture makes available to tests: + * - an "ephemeralForTest" storage engine for checking results of the rollback algorithm at the + * storage layer. The storage engine is initialized as part of the ServiceContextForMongoD test + * fixture. + * - a task executor for simulating remote command responses from the sync source. The + * ThreadPoolExecutorTest is used to initialize the task executor. + */ +class RollbackTest : public unittest::Test { +public: + /** + * Initializes executor::ThreadPoolExecutorTest so that each thread is initialized with a + * Client. + */ + RollbackTest(); + + /** + * Initializes the service context and task executor. + */ + void setUp() override; + + /** + * Destroys the service context and task executor. + * + * Note on overriding tearDown() in tests: + * Tests should explicitly shut down and join the task executor (by invoking + * TaskExecutorTest::shutdownExecutorThread() and TaskExecutorTest::joinExecutorThread() + * respectively) before calling RollbackTest::tearDown(). + * This cancels outstanding tasks and remote command requests scheduled using the task + * executor. + */ + void tearDown() override; + +protected: + // Test fixture used to manage the service context and global storage engine. + ServiceContextMongoDTest _serviceContextMongoDTest; + + // Test fixture used to manage the task executor. + executor::ThreadPoolExecutorTest _threadPoolExecutorTest; + + // OperationContext provided to test cases for storage layer operations. + ServiceContext::UniqueOperationContext _opCtx; + + // ReplicationCoordinator mock implementation for rollback tests. + // Owned by service context. + class ReplicationCoordinatorRollbackMock; + ReplicationCoordinatorRollbackMock* _coordinator = nullptr; + + // StorageInterface used to access minValid. + StorageInterfaceMock _storageInterface; +}; + +/** + * ReplicationCoordinator mock implementation for rollback tests. + */ +class RollbackTest::ReplicationCoordinatorRollbackMock : public ReplicationCoordinatorMock { +public: + ReplicationCoordinatorRollbackMock(ServiceContext* service); + + /** + * Base class implementation triggers an invariant. This function is overridden to be a no-op + * for rollback tests. + */ + void resetLastOpTimesFromOplog(OperationContext* opCtx) override; + + /** + * Returns false (does not forward call to ReplicationCoordinatorMock::setFollowerMode()) + * if new state requested is '_failSetFollowerModeOnThisMemberState'. + * Otherwise, calls ReplicationCoordinatorMock::setFollowerMode(). + */ + bool setFollowerMode(const MemberState& newState) override; + + // Override this to make setFollowerMode() fail when called with this state. + MemberState _failSetFollowerModeOnThisMemberState = MemberState::RS_UNKNOWN; +}; + +} // namespace repl +} // namespace mongo diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp index 90843d9a445..81e50bc27be 100644 --- a/src/mongo/db/repl/rs_rollback_test.cpp +++ b/src/mongo/db/repl/rs_rollback_test.cpp @@ -30,12 +30,10 @@ #include "mongo/platform/basic.h" -#include +#include #include #include "mongo/db/catalog/collection.h" -#include "mongo/db/catalog/database.h" -#include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog/drop_indexes.h" #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/catalog/index_create.h" @@ -49,14 +47,10 @@ #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/oplog_interface.h" #include "mongo/db/repl/oplog_interface_mock.h" -#include "mongo/db/repl/replication_coordinator_global.h" -#include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/rollback_source.h" +#include "mongo/db/repl/rollback_test_fixture.h" #include "mongo/db/repl/rs_rollback.h" -#include "mongo/db/repl/storage_interface.h" -#include "mongo/db/repl/storage_interface_mock.h" #include "mongo/db/s/shard_identity_rollback_notifier.h" -#include "mongo/db/service_context_d_test_fixture.h" #include "mongo/stdx/memory.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" @@ -71,28 +65,6 @@ const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2; const OplogInterfaceMock::Operations kEmptyMockOperations; -ReplSettings createReplSettings() { - ReplSettings settings; - settings.setOplogSizeBytes(5 * 1024 * 1024); - settings.setReplSetString("mySet/node1:12345"); - return settings; -} - -class ReplicationCoordinatorRollbackMock : public ReplicationCoordinatorMock { -public: - ReplicationCoordinatorRollbackMock(ServiceContext* service) - : ReplicationCoordinatorMock(service, createReplSettings()) {} - void resetLastOpTimesFromOplog(OperationContext* opCtx) override {} - bool setFollowerMode(const MemberState& newState) override { - if (newState == _failSetFollowerModeOnThisMemberState) { - return false; - } - return ReplicationCoordinatorMock::setFollowerMode(newState); - } - MemberState _failSetFollowerModeOnThisMemberState = MemberState::RS_UNKNOWN; -}; - - class RollbackSourceMock : public RollbackSource { public: RollbackSourceMock(std::unique_ptr oplog); @@ -137,38 +109,18 @@ StatusWith RollbackSourceMock::getCollectionInfo(const NamespaceString& return BSON("name" << nss.ns() << "options" << BSONObj()); } -class RSRollbackTest : public ServiceContextMongoDTest { -protected: - ServiceContext::UniqueOperationContext _opCtx; - - // Owned by service context - ReplicationCoordinatorRollbackMock* _coordinator; - - repl::StorageInterfaceMock _storageInterface; - +class RSRollbackTest : public RollbackTest { private: void setUp() override; void tearDown() override; }; void RSRollbackTest::setUp() { - ServiceContextMongoDTest::setUp(); - _opCtx = cc().makeOperationContext(); - _coordinator = new ReplicationCoordinatorRollbackMock(_opCtx->getServiceContext()); - - auto serviceContext = getServiceContext(); - ReplicationCoordinator::set(serviceContext, - std::unique_ptr(_coordinator)); - - setOplogCollectionName(); - _storageInterface.setAppliedThrough(_opCtx.get(), OpTime{}); - _storageInterface.setMinValid(_opCtx.get(), OpTime{}); + RollbackTest::setUp(); } void RSRollbackTest::tearDown() { - _opCtx.reset(); - ServiceContextMongoDTest::tearDown(); - setGlobalReplicationCoordinator(nullptr); + RollbackTest::tearDown(); } OplogInterfaceMock::Operation makeNoopOplogEntryAndRecordId(Seconds seconds) { diff --git a/src/mongo/db/service_context_d_test_fixture.cpp b/src/mongo/db/service_context_d_test_fixture.cpp index efa54fdd1a4..be62c952d22 100644 --- a/src/mongo/db/service_context_d_test_fixture.cpp +++ b/src/mongo/db/service_context_d_test_fixture.cpp @@ -46,6 +46,7 @@ #include "mongo/db/time_proof_service.h" #include "mongo/stdx/memory.h" #include "mongo/unittest/temp_dir.h" +#include "mongo/util/assert_util.h" #include "mongo/util/scopeguard.h" namespace mongo { @@ -84,6 +85,10 @@ ServiceContext* ServiceContextMongoDTest::getServiceContext() { return getGlobalServiceContext(); } +void ServiceContextMongoDTest::_doTest() { + MONGO_UNREACHABLE; +} + void ServiceContextMongoDTest::_dropAllDBs(OperationContext* opCtx) { dropAllDatabasesExceptLocal(opCtx); diff --git a/src/mongo/db/service_context_d_test_fixture.h b/src/mongo/db/service_context_d_test_fixture.h index 2442a10132a..402a1837724 100644 --- a/src/mongo/db/service_context_d_test_fixture.h +++ b/src/mongo/db/service_context_d_test_fixture.h @@ -39,7 +39,7 @@ class OperationContext; * Test fixture class for tests that use either the "ephemeralForTest" or "devnull" storage engines. */ class ServiceContextMongoDTest : public unittest::Test { -protected: +public: /** * Initializes global storage engine. */ @@ -57,6 +57,16 @@ protected: ServiceContext* getServiceContext(); private: + /** + * Unused implementation of test function. This allows us to instantiate + * ServiceContextMongoDTest on its own without the need to inherit from it in a test. + * This supports using ServiceContextMongoDTest inside another test fixture and works around the + * limitation that tests cannot inherit from multiple test fixtures. + * + * It is an error to call this implementation of _doTest() directly. + */ + void _doTest() override; + /** * Drops all databases. Call this before global ReplicationCoordinator is destroyed -- it is * used to drop the databases. diff --git a/src/mongo/executor/task_executor_test_fixture.cpp b/src/mongo/executor/task_executor_test_fixture.cpp index bc99e1538fa..a97c3559e7e 100644 --- a/src/mongo/executor/task_executor_test_fixture.cpp +++ b/src/mongo/executor/task_executor_test_fixture.cpp @@ -34,6 +34,7 @@ #include "mongo/executor/network_interface_mock.h" #include "mongo/executor/remote_command_request.h" #include "mongo/stdx/memory.h" +#include "mongo/util/assert_util.h" #include "mongo/util/mongoutils/str.h" namespace mongo { @@ -100,6 +101,10 @@ void TaskExecutorTest::joinExecutorThread() { _executorState = LifecycleState::kShutdownComplete; } +void TaskExecutorTest::_doTest() { + MONGO_UNREACHABLE; +} + void TaskExecutorTest::postExecutorThreadLaunch() {} } // namespace executor diff --git a/src/mongo/executor/task_executor_test_fixture.h b/src/mongo/executor/task_executor_test_fixture.h index d89b6e0062d..e90cbaec413 100644 --- a/src/mongo/executor/task_executor_test_fixture.h +++ b/src/mongo/executor/task_executor_test_fixture.h @@ -59,7 +59,6 @@ public: static RemoteCommandRequest assertRemoteCommandNameEquals(StringData cmdName, const RemoteCommandRequest& request); -protected: virtual ~TaskExecutorTest(); executor::NetworkInterfaceMock* getNet() { @@ -86,6 +85,16 @@ protected: void joinExecutorThread(); private: + /** + * Unused implementation of test function. This allows us to instantiate + * TaskExecutorTest on its own without the need to inherit from it in a test. + * This supports using TaskExecutorTest inside another test fixture and works around the + * limitation that tests cannot inherit from multiple test fixtures. + * + * It is an error to call this implementation of _doTest() directly. + */ + void _doTest() override; + virtual std::unique_ptr makeTaskExecutor( std::unique_ptr net) = 0; diff --git a/src/mongo/executor/thread_pool_task_executor_test_fixture.cpp b/src/mongo/executor/thread_pool_task_executor_test_fixture.cpp index 4973363707d..ebd0ea6dbbe 100644 --- a/src/mongo/executor/thread_pool_task_executor_test_fixture.cpp +++ b/src/mongo/executor/thread_pool_task_executor_test_fixture.cpp @@ -43,8 +43,13 @@ std::unique_ptr makeThreadPoolTestExecutor( stdx::make_unique(netPtr, 1, std::move(options)), std::move(net)); } +ThreadPoolExecutorTest::ThreadPoolExecutorTest() {} + +ThreadPoolExecutorTest::ThreadPoolExecutorTest(ThreadPoolMock::Options options) + : _options(std::move(options)) {} + ThreadPoolMock::Options ThreadPoolExecutorTest::makeThreadPoolMockOptions() const { - return ThreadPoolMock::Options(); + return _options; } std::unique_ptr ThreadPoolExecutorTest::makeTaskExecutor( diff --git a/src/mongo/executor/thread_pool_task_executor_test_fixture.h b/src/mongo/executor/thread_pool_task_executor_test_fixture.h index 31444809758..ebe2d77d23f 100644 --- a/src/mongo/executor/thread_pool_task_executor_test_fixture.h +++ b/src/mongo/executor/thread_pool_task_executor_test_fixture.h @@ -49,10 +49,26 @@ std::unique_ptr makeThreadPoolTestExecutor( * Useful fixture class for tests that use a ThreadPoolTaskExecutor. */ class ThreadPoolExecutorTest : public TaskExecutorTest { +public: + /** + * This default constructor supports the use of this class as a base class for a test fixture. + */ + ThreadPoolExecutorTest(); + + /** + * This constructor supports the use of this class as a member variable to be used alongside + * another test fixture. Accepts a ThreadPoolMock::Options that will be used to override the + * result of makeThreadPoolMockOptions(). + */ + explicit ThreadPoolExecutorTest(ThreadPoolMock::Options options); + private: virtual ThreadPoolMock::Options makeThreadPoolMockOptions() const; std::unique_ptr makeTaskExecutor( std::unique_ptr net) override; + + // Returned by makeThreadPoolMockOptions(). + const ThreadPoolMock::Options _options = {}; }; } // namespace executor -- cgit v1.2.1