summaryrefslogtreecommitdiff
path: root/src/mongo/db/repl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/repl')
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/idempotency_test_fixture.h2
-rw-r--r--src/mongo/db/repl/mock_repl_coord_server_fixture.cpp9
-rw-r--r--src/mongo/db/repl/mock_repl_coord_server_fixture.h4
-rw-r--r--src/mongo/db/repl/oplog_applier_impl_test_fixture.h9
-rw-r--r--src/mongo/db/repl/primary_only_service_test_fixture.h3
-rw-r--r--src/mongo/db/repl/replication_consistency_markers_impl_test.cpp2
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_heartbeat_v1_test.cpp2
-rw-r--r--src/mongo/db/repl/replication_coordinator_mock.cpp19
-rw-r--r--src/mongo/db/repl/replication_coordinator_mock.h2
-rw-r--r--src/mongo/db/repl/replication_coordinator_test_fixture.cpp4
-rw-r--r--src/mongo/db/repl/replication_coordinator_test_fixture.h2
-rw-r--r--src/mongo/db/repl/replication_recovery_test.cpp3
-rw-r--r--src/mongo/db/repl/rollback_impl_test.cpp3
-rw-r--r--src/mongo/db/repl/rollback_test_fixture.cpp70
-rw-r--r--src/mongo/db/repl/rollback_test_fixture.h39
-rw-r--r--src/mongo/db/repl/rs_rollback_test.cpp2971
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp7
-rw-r--r--src/mongo/db/repl/storage_timestamp_test.cpp7
-rw-r--r--src/mongo/db/repl/tenant_migration_donor_service_test.cpp11
-rw-r--r--src/mongo/db/repl/tenant_migration_recipient_service_test.cpp20
21 files changed, 63 insertions, 3127 deletions
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 2f419fcdf60..07b2e725e95 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -1643,7 +1643,6 @@ if wiredtiger:
'roll_back_local_operations_test.cpp',
'rollback_checker_test.cpp',
'rollback_impl_test.cpp',
- 'rs_rollback_test.cpp',
'scatter_gather_test.cpp',
'speculative_majority_read_info_test.cpp',
'split_horizon_test.cpp',
diff --git a/src/mongo/db/repl/idempotency_test_fixture.h b/src/mongo/db/repl/idempotency_test_fixture.h
index 3c79dc60a0e..93766f8d51e 100644
--- a/src/mongo/db/repl/idempotency_test_fixture.h
+++ b/src/mongo/db/repl/idempotency_test_fixture.h
@@ -89,7 +89,7 @@ StringBuilder& operator<<(StringBuilder& sb, const CollectionState& state);
class IdempotencyTest : public OplogApplierImplTest {
public:
- IdempotencyTest() : OplogApplierImplTest("wiredTiger") {
+ IdempotencyTest() {
globalFailPointRegistry()
.find("doUntimestampedWritesForIdempotencyTests")
->setMode(FailPoint::alwaysOn);
diff --git a/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp b/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp
index bc8704c8622..9a76ffc13b4 100644
--- a/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp
+++ b/src/mongo/db/repl/mock_repl_coord_server_fixture.cpp
@@ -47,6 +47,7 @@
#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/db/storage/snapshot_manager.h"
namespace mongo {
@@ -90,6 +91,14 @@ void MockReplCoordServerFixture::setUp() {
repl::DropPendingCollectionReaper::set(
service,
std::make_unique<repl::DropPendingCollectionReaper>(repl::StorageInterface::get(service)));
+
+ // Set a committed snapshot so that we can perform majority reads.
+ WriteUnitOfWork wuow{_opCtx.get()};
+ if (auto snapshotManager =
+ _opCtx->getServiceContext()->getStorageEngine()->getSnapshotManager()) {
+ snapshotManager->setCommittedSnapshot(repl::getNextOpTime(_opCtx.get()).getTimestamp());
+ }
+ wuow.commit();
}
void MockReplCoordServerFixture::insertOplogEntry(const repl::OplogEntry& entry) {
diff --git a/src/mongo/db/repl/mock_repl_coord_server_fixture.h b/src/mongo/db/repl/mock_repl_coord_server_fixture.h
index 7f52f4a3f21..e0859800f63 100644
--- a/src/mongo/db/repl/mock_repl_coord_server_fixture.h
+++ b/src/mongo/db/repl/mock_repl_coord_server_fixture.h
@@ -57,6 +57,10 @@ public:
OperationContext* opCtx();
+protected:
+ explicit MockReplCoordServerFixture(Options options = {})
+ : ServiceContextMongoDTest(std::move(options)) {}
+
private:
ServiceContext::UniqueOperationContext _opCtx;
repl::StorageInterfaceMock* _storageInterface;
diff --git a/src/mongo/db/repl/oplog_applier_impl_test_fixture.h b/src/mongo/db/repl/oplog_applier_impl_test_fixture.h
index 7013192fba9..e1b188232ae 100644
--- a/src/mongo/db/repl/oplog_applier_impl_test_fixture.h
+++ b/src/mongo/db/repl/oplog_applier_impl_test_fixture.h
@@ -195,12 +195,11 @@ public:
};
class OplogApplierImplTest : public ServiceContextMongoDTest {
-public:
- OplogApplierImplTest(){};
- OplogApplierImplTest(std::string storageEngine)
- : ServiceContextMongoDTest(std::move(storageEngine)){};
-
protected:
+ // TODO (SERVER-65297): Use wiredTiger.
+ explicit OplogApplierImplTest()
+ : ServiceContextMongoDTest(Options{}.engine("ephemeralForTest")) {}
+
void _testApplyOplogEntryOrGroupedInsertsCrudOperation(ErrorCodes::Error expectedError,
const OplogEntry& op,
bool expectedApplyOpCalled);
diff --git a/src/mongo/db/repl/primary_only_service_test_fixture.h b/src/mongo/db/repl/primary_only_service_test_fixture.h
index 906cbd15297..c72a2fe85e1 100644
--- a/src/mongo/db/repl/primary_only_service_test_fixture.h
+++ b/src/mongo/db/repl/primary_only_service_test_fixture.h
@@ -54,6 +54,9 @@ public:
void tearDown() override;
protected:
+ explicit PrimaryOnlyServiceMongoDTest(Options options = {})
+ : ServiceContextMongoDTest(std::move(options)) {}
+
void startup(OperationContext* opCtx);
void shutdown();
diff --git a/src/mongo/db/repl/replication_consistency_markers_impl_test.cpp b/src/mongo/db/repl/replication_consistency_markers_impl_test.cpp
index 465289de440..3d58ef476e4 100644
--- a/src/mongo/db/repl/replication_consistency_markers_impl_test.cpp
+++ b/src/mongo/db/repl/replication_consistency_markers_impl_test.cpp
@@ -203,7 +203,7 @@ TEST_F(ReplicationConsistencyMarkersTest, ReplicationConsistencyMarkers) {
OpTime startOpTime({Seconds(123), 0}, 1LL);
OpTime endOpTime({Seconds(456), 0}, 1LL);
consistencyMarkers.setAppliedThrough(opCtx, startOpTime);
- consistencyMarkers.setMinValid(opCtx, endOpTime);
+ consistencyMarkers.setMinValid(opCtx, endOpTime, true /* alwaysAllowUntimestampedWrite */);
consistencyMarkers.setOplogTruncateAfterPoint(opCtx, endOpTime.getTimestamp());
ASSERT_EQ(consistencyMarkers.getAppliedThrough(opCtx), startOpTime);
diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat_v1_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat_v1_test.cpp
index 6cb6e55609c..d403a7d259f 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat_v1_test.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat_v1_test.cpp
@@ -80,6 +80,8 @@ TEST(ReplSetHeartbeatArgs, AcceptsUnknownField) {
class ReplCoordHBV1Test : public ReplCoordTest {
protected:
+ explicit ReplCoordHBV1Test() : ReplCoordTest(Options{}.useMockClock(true)) {}
+
void assertMemberState(MemberState expected, std::string msg = "");
ReplSetHeartbeatResponse receiveHeartbeatFrom(
const ReplSetConfig& rsConfig,
diff --git a/src/mongo/db/repl/replication_coordinator_mock.cpp b/src/mongo/db/repl/replication_coordinator_mock.cpp
index d396c2c429d..22118c874aa 100644
--- a/src/mongo/db/repl/replication_coordinator_mock.cpp
+++ b/src/mongo/db/repl/replication_coordinator_mock.cpp
@@ -39,6 +39,7 @@
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/db/repl/sync_source_resolver.h"
#include "mongo/db/repl/tenant_migration_decoration.h"
+#include "mongo/db/storage/snapshot_manager.h"
#include "mongo/db/write_concern_options.h"
#include "mongo/util/assert_util.h"
@@ -234,12 +235,23 @@ void ReplicationCoordinatorMock::setMyHeartbeatMessage(const std::string& msg) {
// TODO
}
+void ReplicationCoordinatorMock::_setMyLastAppliedOpTimeAndWallTime(
+ const OpTimeAndWallTime& opTimeAndWallTime) {
+ _myLastAppliedOpTime = opTimeAndWallTime.opTime;
+ _myLastAppliedWallTime = opTimeAndWallTime.wallTime;
+
+ if (auto storageEngine = _service->getStorageEngine()) {
+ if (auto snapshotManager = storageEngine->getSnapshotManager()) {
+ snapshotManager->setCommittedSnapshot(opTimeAndWallTime.opTime.getTimestamp());
+ }
+ }
+}
+
void ReplicationCoordinatorMock::setMyLastAppliedOpTimeAndWallTime(
const OpTimeAndWallTime& opTimeAndWallTime) {
stdx::lock_guard<Mutex> lk(_mutex);
- _myLastAppliedOpTime = opTimeAndWallTime.opTime;
- _myLastAppliedWallTime = opTimeAndWallTime.wallTime;
+ _setMyLastAppliedOpTimeAndWallTime(opTimeAndWallTime);
}
void ReplicationCoordinatorMock::setMyLastDurableOpTimeAndWallTime(
@@ -255,8 +267,7 @@ void ReplicationCoordinatorMock::setMyLastAppliedOpTimeAndWallTimeForward(
stdx::lock_guard<Mutex> lk(_mutex);
if (opTimeAndWallTime.opTime > _myLastAppliedOpTime) {
- _myLastAppliedOpTime = opTimeAndWallTime.opTime;
- _myLastAppliedWallTime = opTimeAndWallTime.wallTime;
+ _setMyLastAppliedOpTimeAndWallTime(opTimeAndWallTime);
}
}
diff --git a/src/mongo/db/repl/replication_coordinator_mock.h b/src/mongo/db/repl/replication_coordinator_mock.h
index a77d5ad4982..8e48a9bd8d7 100644
--- a/src/mongo/db/repl/replication_coordinator_mock.h
+++ b/src/mongo/db/repl/replication_coordinator_mock.h
@@ -420,6 +420,8 @@ public:
virtual WriteConcernTagChanges* getWriteConcernTagChanges() override;
private:
+ void _setMyLastAppliedOpTimeAndWallTime(const OpTimeAndWallTime& opTimeAndWallTime);
+
ServiceContext* const _service;
ReplSettings _settings;
StorageInterface* _storage = nullptr;
diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
index ced8e4499dc..d1bcdcc92ab 100644
--- a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
+++ b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
@@ -81,7 +81,7 @@ BSONObj ReplCoordTest::addProtocolVersion(const BSONObj& configDoc, int protocol
return builder.obj();
}
-ReplCoordTest::ReplCoordTest() {
+ReplCoordTest::ReplCoordTest(Options options) : ServiceContextMongoDTest(std::move(options)) {
_settings.setReplSetString("mySet/node1:12345,node2:54321");
}
@@ -167,8 +167,6 @@ void ReplCoordTest::init() {
replicationProcess,
_storageInterface,
seed);
- service->setFastClockSource(std::make_unique<ClockSourceMock>());
- service->setPreciseClockSource(std::make_unique<ClockSourceMock>());
}
void ReplCoordTest::init(const ReplSettings& settings) {
diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.h b/src/mongo/db/repl/replication_coordinator_test_fixture.h
index 79845ffcfa1..ad1e52a1af3 100644
--- a/src/mongo/db/repl/replication_coordinator_test_fixture.h
+++ b/src/mongo/db/repl/replication_coordinator_test_fixture.h
@@ -92,7 +92,7 @@ public:
}
protected:
- ReplCoordTest();
+ explicit ReplCoordTest(Options options = Options{}.useMockClock(true));
virtual ~ReplCoordTest();
/**
diff --git a/src/mongo/db/repl/replication_recovery_test.cpp b/src/mongo/db/repl/replication_recovery_test.cpp
index 7b32092e747..2107eaff181 100644
--- a/src/mongo/db/repl/replication_recovery_test.cpp
+++ b/src/mongo/db/repl/replication_recovery_test.cpp
@@ -136,6 +136,9 @@ public:
class ReplicationRecoveryTest : public ServiceContextMongoDTest {
protected:
+ // TODO (SERVER-65304): Use wiredTiger.
+ ReplicationRecoveryTest() : ServiceContextMongoDTest(Options{}.engine("ephemeralForTest")) {}
+
OperationContext* getOperationContext() {
return _opCtx.get();
}
diff --git a/src/mongo/db/repl/rollback_impl_test.cpp b/src/mongo/db/repl/rollback_impl_test.cpp
index afab6b658a7..af9e2d85dad 100644
--- a/src/mongo/db/repl/rollback_impl_test.cpp
+++ b/src/mongo/db/repl/rollback_impl_test.cpp
@@ -152,6 +152,9 @@ private:
friend class RollbackImplTest::Listener;
protected:
+ // TODO (SERVER-65305): Use wiredTiger.
+ RollbackImplTest() : RollbackTest(Options{}.engine("ephemeralForTest")) {}
+
/**
* Creates a new mock collection with name 'nss' via the StorageInterface and associates 'uuid'
* with the new collection in the CollectionCatalog. There must not already exist a collection
diff --git a/src/mongo/db/repl/rollback_test_fixture.cpp b/src/mongo/db/repl/rollback_test_fixture.cpp
index b4a064a7bce..eaf3e1152f4 100644
--- a/src/mongo/db/repl/rollback_test_fixture.cpp
+++ b/src/mongo/db/repl/rollback_test_fixture.cpp
@@ -296,75 +296,5 @@ StatusWith<BSONObj> RollbackSourceMock::getCollectionInfoByUUID(const std::strin
const UUID& uuid) const {
return BSON("options" << BSONObj() << "info" << BSON("uuid" << uuid));
}
-
-RollbackResyncsCollectionOptionsTest::RollbackSourceWithCollectionOptions::
- RollbackSourceWithCollectionOptions(std::unique_ptr<OplogInterface> oplog,
- BSONObj collOptionsObj)
- : RollbackSourceMock(std::move(oplog)), collOptionsObj(collOptionsObj) {}
-
-StatusWith<BSONObj>
-RollbackResyncsCollectionOptionsTest::RollbackSourceWithCollectionOptions::getCollectionInfoByUUID(
- const std::string& db, const UUID& uuid) const {
- return BSON("options" << collOptionsObj << "info" << BSON("uuid" << uuid));
-}
-
-void RollbackResyncsCollectionOptionsTest::resyncCollectionOptionsTest(
- CollectionOptions localCollOptions, BSONObj remoteCollOptionsObj) {
- resyncCollectionOptionsTest(localCollOptions,
- remoteCollOptionsObj,
- BSON("collMod"
- << "coll"
- << "validationLevel"
- << "strict"),
- "coll");
-}
-void RollbackResyncsCollectionOptionsTest::resyncCollectionOptionsTest(
- CollectionOptions localCollOptions,
- BSONObj remoteCollOptionsObj,
- BSONObj collModCmd,
- std::string collName) {
- createOplog(_opCtx.get());
-
- auto dbName = "test";
- auto nss = NamespaceString(dbName, collName);
-
- auto coll = _createCollection(_opCtx.get(), nss.toString(), localCollOptions);
-
- auto commonOpUuid = unittest::assertGet(UUID::parse("f005ba11-cafe-bead-f00d-123456789abc"));
- auto commonOpBson = BSON("ts" << Timestamp(1, 1) << "t" << 1LL << "op"
- << "n"
- << "o" << BSONObj() << "ns"
- << "rollback_test.test"
- << "wall" << Date_t() << "ui" << commonOpUuid);
-
- auto commonOperation = std::make_pair(commonOpBson, RecordId(1));
-
- auto collectionModificationOperation =
- makeCommandOp(Timestamp(Seconds(2), 0), coll->uuid(), nss.toString(), collModCmd, 2);
-
- RollbackSourceWithCollectionOptions rollbackSource(
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})),
- remoteCollOptionsObj);
-
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({collectionModificationOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- // Make sure the collection options are correct.
- AutoGetCollectionForReadCommand autoColl(_opCtx.get(), NamespaceString(nss.toString()));
- auto collAfterRollbackOptions = autoColl->getCollectionOptions();
-
- BSONObjBuilder expectedOptionsBob;
- if (localCollOptions.uuid) {
- localCollOptions.uuid.get().appendToBuilder(&expectedOptionsBob, "uuid");
- }
- expectedOptionsBob.appendElements(remoteCollOptionsObj);
-
- ASSERT_BSONOBJ_EQ(expectedOptionsBob.obj(), collAfterRollbackOptions.toBSON());
-}
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/rollback_test_fixture.h b/src/mongo/db/repl/rollback_test_fixture.h
index bae1e9293b4..9bb4103b656 100644
--- a/src/mongo/db/repl/rollback_test_fixture.h
+++ b/src/mongo/db/repl/rollback_test_fixture.h
@@ -57,7 +57,7 @@ namespace repl {
*/
class RollbackTest : public ServiceContextMongoDTest {
public:
- RollbackTest() = default;
+ explicit RollbackTest(Options options = {}) : ServiceContextMongoDTest(std::move(options)) {}
/**
* Initializes the service context and task executor.
@@ -294,42 +294,5 @@ private:
HostAndPort _source;
};
-/**
- * Test fixture to ensure that rollback re-syncs collection options from a sync source and updates
- * the local collection options correctly. A test operates on a single test collection, and is
- * parameterized on two arguments:
- *
- * 'localCollOptions': the collection options that the local test collection is initially created
- * with.
- *
- * 'remoteCollOptionsObj': the collection options object that the sync source will respond with to
- * the rollback node when it fetches collection metadata.
- *
- * If no command is provided, a collMod operation with a 'validationLevel' argument is used to
- * trigger a collection metadata resync, since the rollback of collMod operations does not take into
- * account the actual command object. It simply re-syncs all the collection options.
- */
-class RollbackResyncsCollectionOptionsTest : public RollbackTest {
-
- class RollbackSourceWithCollectionOptions : public RollbackSourceMock {
- public:
- RollbackSourceWithCollectionOptions(std::unique_ptr<OplogInterface> oplog,
- BSONObj collOptionsObj);
-
- StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db,
- const UUID& uuid) const override;
-
- BSONObj collOptionsObj;
- };
-
-public:
- void resyncCollectionOptionsTest(CollectionOptions localCollOptions,
- BSONObj remoteCollOptionsObj,
- BSONObj collModCmd,
- std::string collName);
- void resyncCollectionOptionsTest(CollectionOptions localCollOptions,
- BSONObj remoteCollOptionsObj);
-};
-
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp
deleted file mode 100644
index cdeff56a1d5..00000000000
--- a/src/mongo/db/repl/rs_rollback_test.cpp
+++ /dev/null
@@ -1,2971 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * 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
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 Server Side 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_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-#include "mongo/platform/basic.h"
-
-#include <initializer_list>
-#include <memory>
-#include <utility>
-
-#include "mongo/db/catalog/collection_catalog.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/client.h"
-#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/db_raii.h"
-#include "mongo/db/dbhelpers.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/index_builds_coordinator.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/namespace_string.h"
-#include "mongo/db/op_observer_noop.h"
-#include "mongo/db/op_observer_registry.h"
-#include "mongo/db/read_write_concern_defaults.h"
-#include "mongo/db/repl/drop_pending_collection_reaper.h"
-#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/rollback_source.h"
-#include "mongo/db/repl/rollback_test_fixture.h"
-#include "mongo/db/repl/rs_rollback.h"
-#include "mongo/db/s/shard_identity_rollback_notifier.h"
-#include "mongo/unittest/death_test.h"
-#include "mongo/unittest/unittest.h"
-#include "mongo/util/net/hostandport.h"
-
-namespace mongo {
-namespace {
-
-using namespace mongo::repl;
-using namespace mongo::repl::rollback_internal;
-
-const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2;
-
-class RSRollbackTest : public RollbackTest {};
-
-
-OplogInterfaceMock::Operation makeNoopOplogEntryAndRecordId(Seconds seconds) {
- OpTime ts(Timestamp(seconds, 0), 0);
- return std::make_pair(BSON("ts" << ts.getTimestamp()), RecordId(1));
-}
-
-OplogInterfaceMock::Operation makeDropIndexOplogEntry(const CollectionPtr& collection,
- BSONObj key,
- std::string indexName,
- int time) {
- auto indexSpec =
- BSON("key" << key << "name" << indexName << "v" << static_cast<int>(kIndexVersion));
-
- return std::make_pair(
- BSON("ts" << Timestamp(Seconds(time), 0) << "op"
- << "c"
- << "ui" << collection->uuid() << "ns"
- << "test.$cmd"
- << "o" << BSON("dropIndexes" << collection->ns().coll() << "index" << indexName)
- << "o2" << indexSpec << "wall" << Date_t()),
- RecordId(time));
-}
-
-OplogInterfaceMock::Operation makeStartIndexBuildOplogEntry(const CollectionPtr& collection,
- UUID buildUUID,
- BSONObj spec,
- int time) {
- auto entry = BSON("startIndexBuild" << collection->ns().coll() << "indexBuildUUID" << buildUUID
- << "indexes" << BSON_ARRAY(spec));
-
- return std::make_pair(BSON("ts" << Timestamp(Seconds(time), 0) << "op"
- << "c"
- << "ns"
- << "test.$cmd"
- << "ui" << collection->uuid() << "o" << entry << "wall"
- << Date_t()),
- RecordId(time));
-}
-
-OplogInterfaceMock::Operation makeCommitIndexBuildOplogEntry(const CollectionPtr& collection,
- UUID buildUUID,
- BSONObj spec,
- int time) {
- auto entry = BSON("commitIndexBuild" << collection->ns().coll() << "indexBuildUUID" << buildUUID
- << "indexes" << BSON_ARRAY(spec));
-
- return std::make_pair(BSON("ts" << Timestamp(Seconds(time), 0) << "op"
- << "c"
- << "ns"
- << "test.$cmd"
- << "ui" << collection->uuid() << "o" << entry << "wall"
- << Date_t()),
- RecordId(time));
-}
-
-OplogInterfaceMock::Operation makeAbortIndexBuildOplogEntry(const CollectionPtr& collection,
- UUID buildUUID,
- BSONObj spec,
- int time) {
- Status cause = {ErrorCodes::IndexBuildAborted, "test"};
-
- BSONObjBuilder causeBuilder;
- causeBuilder.appendBool("ok", 0);
- cause.serializeErrorToBSON(&causeBuilder);
- auto entry =
- BSON("abortIndexBuild" << collection->ns().coll() << "indexBuildUUID" << buildUUID
- << "indexes" << BSON_ARRAY(spec) << "cause" << causeBuilder.done());
-
- return std::make_pair(BSON("ts" << Timestamp(Seconds(time), 0) << "op"
- << "c"
- << "ns"
- << "test.$cmd"
- << "ui" << collection->uuid() << "o" << entry << "wall"
- << Date_t()),
- RecordId(time));
-}
-
-OplogInterfaceMock::Operation makeCreateIndexOplogEntry(const CollectionPtr& collection,
- BSONObj key,
- std::string indexName,
- int time) {
- auto indexSpec =
- BSON("createIndexes" << collection->ns().coll() << "v" << static_cast<int>(kIndexVersion)
- << "key" << key << "name" << indexName);
-
- return std::make_pair(BSON("ts" << Timestamp(Seconds(time), 0) << "op"
- << "c"
- << "ns"
- << "test.$cmd"
- << "ui" << collection->uuid() << "o" << indexSpec << "wall"
- << Date_t()),
- RecordId(time));
-}
-
-OplogInterfaceMock::Operation makeRenameCollectionOplogEntry(
- const NamespaceString& renameFrom,
- const NamespaceString& renameTo,
- const UUID collectionUUID,
- const boost::optional<UUID>& dropTarget,
- const bool stayTemp,
- OpTime opTime) {
- BSONObjBuilder cmd;
- cmd.append("renameCollection", renameFrom.ns());
- cmd.append("to", renameTo.ns());
- cmd.append("stayTemp", stayTemp);
-
- BSONObj obj = cmd.obj();
-
- if (dropTarget) {
- obj = obj.addField(BSON("dropTarget" << *dropTarget).firstElement());
- }
- return std::make_pair(BSON("ts" << opTime.getTimestamp() << "t" << opTime.getTerm() << "op"
- << "c"
- << "ui" << collectionUUID << "ns" << renameFrom.ns() << "o"
- << obj << "wall" << Date_t()),
- RecordId(opTime.getTimestamp().getSecs()));
-}
-
-BSONObj makeOp(long long seconds) {
- auto uuid = unittest::assertGet(UUID::parse("f005ba11-cafe-bead-f00d-123456789abc"));
- return BSON("ts" << Timestamp(seconds, seconds) << "t" << seconds << "op"
- << "n"
- << "o" << BSONObj() << "ns"
- << "rs_rollback.test"
- << "ui" << uuid << "wall" << Date_t());
-}
-
-int recordId = 0;
-OplogInterfaceMock::Operation makeOpAndRecordId(long long seconds) {
- return std::make_pair(makeOp(seconds), RecordId(++recordId));
-}
-
-// Create an index on an empty collection. Returns the number of indexes that exist on the
-// collection after the given index is created.
-int _createIndexOnEmptyCollection(OperationContext* opCtx,
- Collection* coll,
- NamespaceString nss,
- BSONObj indexSpec) {
- Lock::DBLock dbLock(opCtx, nss.db(), MODE_X);
- auto indexCatalog = coll->getIndexCatalog();
- WriteUnitOfWork wunit(opCtx);
- ASSERT_OK(indexCatalog->createIndexOnEmptyCollection(opCtx, coll, indexSpec).getStatus());
- wunit.commit();
- return indexCatalog->numIndexesReady(opCtx);
-}
-
-TEST_F(RSRollbackTest, InconsistentMinValid) {
- _replicationProcess->getConsistencyMarkers()->setAppliedThrough(
- _opCtx.get(), OpTime(Timestamp(Seconds(1), 0), 0));
- _replicationProcess->getConsistencyMarkers()->setMinValid(_opCtx.get(),
- OpTime(Timestamp(Seconds(2), 0), 0));
- auto status = syncRollback(_opCtx.get(),
- OplogInterfaceMock(),
- RollbackSourceMock(std::make_unique<OplogInterfaceMock>()),
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
- ASSERT_STRING_CONTAINS(status.reason(), "unable to determine common point");
-}
-
-TEST_F(RSRollbackTest, OplogStartMissing) {
- OpTime ts(Timestamp(Seconds(1), 0), 0);
- auto operation = std::make_pair(BSON("ts" << ts.getTimestamp()), RecordId());
- OplogInterfaceMock::Operations remoteOperations({operation});
- auto remoteOplog = std::make_unique<OplogInterfaceMock>(remoteOperations);
- ASSERT_EQUALS(ErrorCodes::OplogStartMissing,
- syncRollback(_opCtx.get(),
- OplogInterfaceMock(),
- RollbackSourceMock(std::move(remoteOplog)),
- {},
- {},
- _coordinator,
- _replicationProcess.get())
- .code());
-}
-
-TEST_F(RSRollbackTest, NoRemoteOpLog) {
- OpTime ts(Timestamp(Seconds(1), 0), 0);
- auto operation = std::make_pair(BSON("ts" << ts.getTimestamp()), RecordId());
- auto status = syncRollback(_opCtx.get(),
- OplogInterfaceMock({operation}),
- RollbackSourceMock(std::make_unique<OplogInterfaceMock>()),
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
- ASSERT_STRING_CONTAINS(status.reason(), "unable to determine common point");
-}
-
-TEST_F(RSRollbackTest, RemoteGetRollbackIdThrows) {
- OpTime ts(Timestamp(Seconds(1), 0), 0);
- auto operation = std::make_pair(BSON("ts" << ts.getTimestamp()), RecordId());
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)) {}
- int getRollbackId() const override {
- uassert(ErrorCodes::UnknownError, "getRollbackId() failed", false);
- }
- };
- ASSERT_THROWS_CODE(syncRollback(_opCtx.get(),
- OplogInterfaceMock({operation}),
- RollbackSourceLocal(std::make_unique<OplogInterfaceMock>()),
- {},
- {},
- _coordinator,
- _replicationProcess.get()),
- AssertionException,
- ErrorCodes::UnknownError);
-}
-
-TEST_F(RSRollbackTest, RemoteGetRollbackIdDiffersFromRequiredRBID) {
- OpTime ts(Timestamp(Seconds(1), 0), 0);
- auto operation = std::make_pair(BSON("ts" << ts.getTimestamp()), RecordId());
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- using RollbackSourceMock::RollbackSourceMock;
- int getRollbackId() const override {
- return 2;
- }
- };
-
- ASSERT_THROWS_CODE(syncRollback(_opCtx.get(),
- OplogInterfaceMock({operation}),
- RollbackSourceLocal(std::make_unique<OplogInterfaceMock>()),
- {},
- 1,
- _coordinator,
- _replicationProcess.get()),
- AssertionException,
- ErrorCodes::duplicateCodeForTest(40506));
-}
-
-TEST_F(RSRollbackTest, BothOplogsAtCommonPoint) {
- createOplog(_opCtx.get());
- auto operation = makeOpAndRecordId(1);
- ASSERT_OK(
- syncRollback(_opCtx.get(),
- OplogInterfaceMock({operation}),
- RollbackSourceMock(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- operation,
- }))),
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-}
-
-/**
- * Test function to roll back a delete operation.
- * Returns number of records in collection after rolling back delete operation.
- * If collection does not exist after rolling back, returns -1.
- */
-int _testRollbackDelete(OperationContext* opCtx,
- ReplicationCoordinator* coordinator,
- ReplicationProcess* replicationProcess,
- UUID uuid,
- const BSONObj& documentAtSource,
- const bool collectionAtSourceExists = true) {
- auto commonOperation = makeOpAndRecordId(1);
- auto deleteOperation =
- std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "d"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 0) << "wall" << Date_t()),
- RecordId(2));
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(const BSONObj& documentAtSource,
- std::unique_ptr<OplogInterface> oplog,
- const bool collectionAtSourceExists)
- : RollbackSourceMock(std::move(oplog)),
- called(false),
- _documentAtSource(documentAtSource),
- _collectionAtSourceExists(collectionAtSourceExists) {}
- std::pair<BSONObj, NamespaceString> findOneByUUID(const std::string& db,
- UUID uuid,
- const BSONObj& filter) const override {
- called = true;
- if (!_collectionAtSourceExists) {
- uassertStatusOKWithContext(
- Status(ErrorCodes::NamespaceNotFound, "MockNamespaceNotFoundMsg"),
- "find command using UUID failed.");
- }
- return {_documentAtSource, NamespaceString()};
- }
- mutable bool called;
-
- private:
- BSONObj _documentAtSource;
- bool _collectionAtSourceExists;
- };
- RollbackSourceLocal rollbackSource(documentAtSource,
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })),
- collectionAtSourceExists);
- ASSERT_OK(syncRollback(opCtx,
- OplogInterfaceMock({deleteOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- coordinator,
- replicationProcess));
- ASSERT_TRUE(rollbackSource.called);
-
- Lock::DBLock dbLock(opCtx, "test", MODE_S);
- Lock::CollectionLock collLock(opCtx, NamespaceString("test.t"), MODE_S);
- auto databaseHolder = DatabaseHolder::get(opCtx);
- auto db = databaseHolder->getDb(opCtx, TenantDatabaseName(boost::none, "test"));
- ASSERT_TRUE(db);
- auto collection = CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(
- opCtx, NamespaceString("test.t"));
- if (!collection) {
- return -1;
- }
- return collection->getRecordStore()->numRecords(opCtx);
-}
-
-TEST_F(RSRollbackTest, RollbackDeleteNoDocumentAtSourceCollectionDoesNotExist) {
- createOplog(_opCtx.get());
- ASSERT_EQUALS(
- -1,
- _testRollbackDelete(
- _opCtx.get(), _coordinator, _replicationProcess.get(), UUID::gen(), BSONObj()));
-}
-
-TEST_F(RSRollbackTest, RollbackDeleteDocCmdCollectionAtSourceDropped) {
- const bool collectionAtSourceExists = false;
- const NamespaceString nss("test.t");
- createOplog(_opCtx.get());
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_X);
- auto databaseHolder = DatabaseHolder::get(_opCtx.get());
- auto db = databaseHolder->openDb(_opCtx.get(), TenantDatabaseName(boost::none, nss.db()));
- ASSERT_TRUE(db);
- }
- ASSERT_EQUALS(-1,
- _testRollbackDelete(_opCtx.get(),
- _coordinator,
- _replicationProcess.get(),
- UUID::gen(),
- BSONObj(),
- collectionAtSourceExists));
-}
-
-TEST_F(RSRollbackTest, RollbackDeleteNoDocumentAtSourceCollectionExistsNonCapped) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
- _testRollbackDelete(
- _opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), BSONObj());
- ASSERT_EQUALS(
- 0,
- _testRollbackDelete(
- _opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), BSONObj()));
-}
-
-TEST_F(RSRollbackTest, RollbackDeleteNoDocumentAtSourceCollectionExistsCapped) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- options.capped = true;
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
- ASSERT_EQUALS(
- 0,
- _testRollbackDelete(
- _opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), BSONObj()));
-}
-
-TEST_F(RSRollbackTest, RollbackDeleteRestoreDocument) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
- BSONObj doc = BSON("_id" << 0 << "a" << 1);
- _testRollbackDelete(_opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), doc);
- ASSERT_EQUALS(1,
- _testRollbackDelete(
- _opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), doc));
-}
-
-TEST_F(RSRollbackTest, RollbackInsertDocumentWithNoId) {
- createOplog(_opCtx.get());
- auto commonOperation = makeOpAndRecordId(1);
- auto insertDocumentOperation =
- std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "i"
- << "ui" << UUID::gen() << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("a" << 1)),
- RecordId(2));
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)), called(false) {}
- BSONObj findOne(const NamespaceString& nss, const BSONObj& filter) const {
- called = true;
- return BSONObj();
- }
- mutable bool called;
-
- private:
- BSONObj _documentAtSource;
- };
- RollbackSourceLocal rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- startCapturingLogMessages();
- auto status = syncRollback(_opCtx.get(),
- OplogInterfaceMock({insertDocumentOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- stopCapturingLogMessages();
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
- ASSERT_STRING_CONTAINS(status.reason(), "unable to determine common point");
- ASSERT_EQUALS(1, countTextFormatLogLinesContaining("Cannot roll back op with no _id"));
- ASSERT_FALSE(rollbackSource.called);
-}
-
-TEST_F(RSRollbackTest, RollbackCreateIndexCommand) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto collection = _createCollection(_opCtx.get(), nss.toString(), options);
- auto indexSpec =
- BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON("a" << 1) << "name"
- << "a_1");
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), collection, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto commonOperation = makeOpAndRecordId(1);
- auto createIndexOperation = makeCreateIndexOplogEntry(collection, BSON("a" << 1), "a_1", 2);
-
- // Collection pointer will be stale after rollback
- collection = nullptr;
-
- // Repeat index creation operation and confirm that rollback attempts to drop index just once.
- // This can happen when an index is re-created with different options.
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- startCapturingLogMessages();
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({createIndexOperation, createIndexOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- stopCapturingLogMessages();
- ASSERT_EQUALS(1, countTextFormatLogLinesContaining("Dropped index in rollback"));
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), nss)
- ->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-}
-
-TEST_F(RSRollbackTest, RollbackCreateIndexCommandIndexNotInCatalog) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test.t");
- auto collection = _createCollection(_opCtx.get(), nss, options);
- auto indexSpec = BSON("key" << BSON("a" << 1) << "name"
- << "a_1");
- // Skip index creation to trigger warning during rollback.
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = collection->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-
- auto commonOperation = makeOpAndRecordId(1);
- auto createIndexOperation = makeCreateIndexOplogEntry(collection, BSON("a" << 1), "a_1", 2);
-
- // Collection pointer will be stale after rollback
- collection = nullptr;
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- startCapturingLogMessages();
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({createIndexOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- stopCapturingLogMessages();
- ASSERT_EQUALS(1, countTextFormatLogLinesContaining("Rollback failed to drop index"));
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), nss)
- ->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-}
-
-TEST_F(RSRollbackTest, RollbackDropIndexCommandWithOneIndex) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test.t");
- auto collection = _createCollection(_opCtx.get(), nss, options);
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = collection->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-
- auto commonOperation = makeOpAndRecordId(1);
- auto dropIndexOperation = makeDropIndexOplogEntry(collection, BSON("a" << 1), "a_1", 2);
-
- // Collection pointer will be stale after rollback
- collection = nullptr;
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({dropIndexOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), nss)
- ->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(2, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-}
-
-TEST_F(RSRollbackTest, RollbackDropIndexCommandWithMultipleIndexes) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test.t");
- auto collection = _createCollection(_opCtx.get(), nss, options);
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = collection->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-
- auto commonOperation = makeOpAndRecordId(1);
-
- auto dropIndexOperation1 = makeDropIndexOplogEntry(collection, BSON("a" << 1), "a_1", 2);
- auto dropIndexOperation2 = makeDropIndexOplogEntry(collection, BSON("b" << 1), "b_1", 3);
-
- // Collection pointer will be stale after rollback
- collection = nullptr;
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({dropIndexOperation2, dropIndexOperation1, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), nss)
- ->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(3, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-}
-
-TEST_F(RSRollbackTest, RollingBackCreateAndDropOfSameIndexIgnoresBothCommands) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test.t");
- auto collection = _createCollection(_opCtx.get(), nss, options);
-
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_X);
- auto indexCatalog = collection->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- }
-
- auto commonOperation = makeOpAndRecordId(1);
-
- auto createIndexOperation = makeCreateIndexOplogEntry(collection, BSON("a" << 1), "a_1", 2);
-
- auto dropIndexOperation = makeDropIndexOplogEntry(collection, BSON("a" << 1), "a_1", 3);
-
- // Collection pointer will be stale after rollback
- collection = nullptr;
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({dropIndexOperation, createIndexOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), nss)
- ->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
- auto indexDescriptor = indexCatalog->findIndexByName(_opCtx.get(), "a_1", false);
- ASSERT(!indexDescriptor);
- }
-}
-
-TEST_F(RSRollbackTest, RollingBackCreateIndexAndRenameWithLongName) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto collection = _createCollection(_opCtx.get(), nss.toString(), options);
-
- auto longName = std::string(115, 'a');
- auto indexSpec = BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON("b" << 1)
- << "name" << longName);
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), collection, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto commonOperation = makeOpAndRecordId(1);
-
- auto createIndexOperation = makeCreateIndexOplogEntry(collection, BSON("b" << 1), longName, 2);
-
- // A collection rename will fail if it would cause an index name to become more than 128 bytes.
- // The old collection name plus the index name is not too long, but the new collection name
- // plus the index name is too long.
- auto newName = NamespaceString("test", "collcollcollcollcoll");
- auto renameCollectionOperation = makeRenameCollectionOplogEntry(
- newName, nss, collection->uuid(), boost::none, false, OpTime(Timestamp(Seconds(2), 0), 1));
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({createIndexOperation, renameCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- {
- AutoGetCollectionForReadCommand coll(_opCtx.get(), newName);
- auto indexCatalog = coll.getCollection()->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(1, indexCatalog->numIndexesReady(_opCtx.get()));
-
- std::vector<const IndexDescriptor*> indexes;
- indexCatalog->findIndexesByKeyPattern(_opCtx.get(), BSON("b" << 1), false, &indexes);
- ASSERT(indexes.size() == 0);
- }
-}
-
-TEST_F(RSRollbackTest, RollingBackDropAndCreateOfSameIndexNameWithDifferentSpecs) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto collection = _createCollection(_opCtx.get(), nss.toString(), options);
-
- auto indexSpec =
- BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON("b" << 1) << "name"
- << "a_1");
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), collection, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto commonOperation = makeOpAndRecordId(1);
-
- auto dropIndexOperation = makeDropIndexOplogEntry(collection, BSON("a" << 1), "a_1", 2);
-
- auto createIndexOperation = makeCreateIndexOplogEntry(collection, BSON("b" << 1), "a_1", 3);
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- startCapturingLogMessages();
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({createIndexOperation, dropIndexOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- stopCapturingLogMessages();
- {
- Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_S);
- auto indexCatalog = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), nss)
- ->getIndexCatalog();
- ASSERT(indexCatalog);
- ASSERT_EQUALS(2, indexCatalog->numIndexesReady(_opCtx.get()));
- ASSERT_EQUALS(1, countTextFormatLogLinesContaining("Dropped index in rollback"));
- ASSERT_EQUALS(1, countTextFormatLogLinesContaining("Created index in rollback"));
- std::vector<const IndexDescriptor*> indexes;
- indexCatalog->findIndexesByKeyPattern(_opCtx.get(), BSON("a" << 1), false, &indexes);
- ASSERT(indexes.size() == 1);
- ASSERT(indexes[0]->indexName() == "a_1");
-
- std::vector<const IndexDescriptor*> indexes2;
- indexCatalog->findIndexesByKeyPattern(_opCtx.get(), BSON("b" << 1), false, &indexes2);
- ASSERT(indexes2.size() == 0);
- }
-}
-
-TEST_F(RSRollbackTest, RollbackCreateIndexCommandMissingIndexName) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto collection = _createCollection(_opCtx.get(), "test.t", options);
- auto commonOperation = makeOpAndRecordId(1);
- BSONObj command = BSON("createIndexes"
- << "t"
- << "ns"
- << "test.t"
- << "wall" << Date_t() << "v" << static_cast<int>(kIndexVersion) << "key"
- << BSON("a" << 1));
-
- auto createIndexOperation = std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "c"
- << "ns"
- << "test.$cmd"
- << "wall" << Date_t() << "ui"
- << collection->uuid() << "o" << command),
- RecordId(2));
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- startCapturingLogMessages();
- auto status = syncRollback(_opCtx.get(),
- OplogInterfaceMock({createIndexOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- stopCapturingLogMessages();
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
- ASSERT_STRING_CONTAINS(status.reason(), "unable to determine common point");
- ASSERT_EQUALS(1,
- countTextFormatLogLinesContaining(
- "Missing index name in createIndexes operation on rollback"));
-}
-
-// Generators of standard index keys and names given an index 'id'.
-std::string idxKey(std::string id) {
- return "key_" + id;
-};
-std::string idxName(std::string id) {
- return "index_" + id;
-};
-
-// Create an index spec object given the namespace and the index 'id'.
-BSONObj idxSpec(NamespaceString nss, std::string id) {
- return BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON(idxKey(id) << 1) << "name"
- << idxName(id));
-}
-
-// Returns the number of indexes that exist on the given collection.
-int numIndexesOnColl(OperationContext* opCtx, NamespaceString nss, const CollectionPtr& coll) {
- Lock::DBLock dbLock(opCtx, nss.db(), MODE_X);
- auto indexCatalog = coll->getIndexCatalog();
- ASSERT(indexCatalog);
- return indexCatalog->numIndexesReady(opCtx);
-}
-
-int numIndexesInProgress(OperationContext* opCtx, NamespaceString nss, const CollectionPtr& coll) {
- Lock::DBLock dbLock(opCtx, nss.db(), MODE_X);
- auto indexCatalog = coll->getIndexCatalog();
- ASSERT(indexCatalog);
- return indexCatalog->numIndexesInProgress(opCtx);
-}
-
-TEST_F(RSRollbackTest, RollbackDropIndexOnCollectionWithTwoExistingIndexes) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- // Create the necessary indexes. Index 0 is created and dropped in the sequence of ops that will
- // be rolled back, so we only create index 1.
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, idxSpec(nss, "1"));
- ASSERT_EQUALS(2, numIndexes);
-
- auto commonOp = makeOpAndRecordId(1);
-
- // The ops that will be rolled back.
- auto createIndex0Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 2);
- auto createIndex1Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("1") << 1), idxName("1"), 3);
- auto dropIndex0Op = makeDropIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 4);
-
- auto remoteOplog = {commonOp};
- auto localOplog = {dropIndex0Op, createIndex1Op, createIndex0Op, commonOp};
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(
- 1,
- numIndexesOnColl(
- _opCtx.get(),
- nss,
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss)));
-}
-
-TEST_F(RSRollbackTest, RollbackTwoIndexDropsPrecededByTwoIndexCreationsOnSameCollection) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- auto commonOp = makeOpAndRecordId(1);
-
- // The ops that will be rolled back.
- auto createIndex0Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 2);
- auto createIndex1Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("1") << 1), idxName("1"), 3);
- auto dropIndex0Op = makeDropIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 4);
- auto dropIndex1Op = makeDropIndexOplogEntry(coll, BSON(idxKey("1") << 1), idxName("1"), 5);
-
- auto remoteOplog = {commonOp};
- auto localOplog = {dropIndex1Op, dropIndex0Op, createIndex1Op, createIndex0Op, commonOp};
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(
- 1,
- numIndexesOnColl(
- _opCtx.get(),
- nss,
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss)));
-}
-
-TEST_F(RSRollbackTest, RollbackMultipleCreateIndexesOnSameCollection) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- auto commonOp = makeOpAndRecordId(1);
-
- // Create all of the necessary indexes.
- _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, idxSpec(nss, "0"));
- _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, idxSpec(nss, "1"));
- _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, idxSpec(nss, "2"));
- ASSERT_EQUALS(4, numIndexesOnColl(_opCtx.get(), nss, coll));
-
- // The ops that will be rolled back.
- auto createIndex0Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 2);
- auto createIndex1Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("1") << 1), idxName("1"), 3);
- auto createIndex2Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("2") << 1), idxName("2"), 4);
-
- auto remoteOplog = {commonOp};
- auto localOplog = {createIndex2Op, createIndex1Op, createIndex0Op, commonOp};
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(
- 1,
- numIndexesOnColl(
- _opCtx.get(),
- nss,
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss)));
-}
-
-TEST_F(RSRollbackTest, RollbackCreateDropRecreateIndexOnCollection) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- // Create the necessary indexes. Index 0 is created, dropped, and created again in the
- // sequence of ops, so we create that index.
- auto indexSpec = BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON(idxKey("0") << 1)
- << "name" << idxName("0"));
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto commonOp = makeOpAndRecordId(1);
-
- // The ops that will be rolled back.
- auto createIndex0Op = makeCreateIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 2);
- auto dropIndex0Op = makeDropIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 3);
- auto createIndex0AgainOp =
- makeCreateIndexOplogEntry(coll, BSON(idxKey("0") << 1), idxName("0"), 4);
-
- auto remoteOplog = {commonOp};
- auto localOplog = {createIndex0AgainOp, dropIndex0Op, createIndex0Op, commonOp};
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(
- 1,
- numIndexesOnColl(
- _opCtx.get(),
- nss,
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss)));
-}
-
-TEST_F(RSRollbackTest, RollbackCommitIndexBuild) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- // Create the necessary index.
- auto indexSpec = BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON(idxKey("0") << 1)
- << "name" << idxName("0") << "collation"
- << BSON("locale"
- << "fr"));
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto buildUUID = UUID::gen();
- // Store the commit quorum value for the index build in config.system.indexBuilds collection.
- _insertDocument(_opCtx.get(),
- NamespaceString::kIndexBuildEntryNamespace,
- BSON("_id" << buildUUID << "collectionUUID" << options.uuid.get()
- << "indexNames" << BSON_ARRAY(idxName("0")) << "commitQuorum" << 0));
-
- auto commonOp = makeOpAndRecordId(1);
-
- auto commitIndexBuild = makeCommitIndexBuildOplogEntry(coll, buildUUID, indexSpec, 2);
-
- // Roll back a commit oplog entry, which will drop and restart the index build.
- auto remoteOplog = {commonOp};
- auto localOplog = {commitIndexBuild, commonOp};
-
- // Collection pointer will be stale after rollback
- coll = nullptr;
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- auto collAfterRollback =
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss);
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(1, numIndexesOnColl(_opCtx.get(), nss, collAfterRollback));
- ASSERT_EQUALS(1, numIndexesInProgress(_opCtx.get(), nss, collAfterRollback));
-
- // Kill the index build we just restarted so the fixture can shut down.
- ASSERT_OK(_coordinator->setFollowerMode(MemberState::RS_ROLLBACK));
- ASSERT(IndexBuildsCoordinator::get(_opCtx.get())
- ->abortIndexBuildByBuildUUID(
- _opCtx.get(), buildUUID, IndexBuildAction::kRollbackAbort, ""));
-}
-
-TEST_F(RSRollbackTest, RollbackAbortIndexBuild) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- // Create the necessary index.
- auto indexSpec = BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON(idxKey("0") << 1)
- << "name" << idxName("0") << "collation"
- << BSON("locale"
- << "fr"));
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto buildUUID = UUID::gen();
- // Store the commit quorum value for the index build in config.system.indexBuilds collection.
- _insertDocument(_opCtx.get(),
- NamespaceString::kIndexBuildEntryNamespace,
- BSON("_id" << buildUUID << "collectionUUID" << options.uuid.get()
- << "indexNames" << BSON_ARRAY(idxName("0")) << "commitQuorum" << 0));
-
- auto commonOp = makeOpAndRecordId(1);
-
- auto abortIndexBuild = makeAbortIndexBuildOplogEntry(coll, buildUUID, indexSpec, 2);
-
- // Roll back an abort oplog entry, which will drop and restart the index build.
- auto remoteOplog = {commonOp};
- auto localOplog = {abortIndexBuild, commonOp};
-
- // Collection pointer will be stale after rollback
- coll = nullptr;
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- auto collAfterRollback =
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss);
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(1, numIndexesOnColl(_opCtx.get(), nss, collAfterRollback));
- ASSERT_EQUALS(1, numIndexesInProgress(_opCtx.get(), nss, collAfterRollback));
-
- // Kill the index build we just restarted so the fixture can shut down.
- ASSERT_OK(_coordinator->setFollowerMode(MemberState::RS_ROLLBACK));
- ASSERT(IndexBuildsCoordinator::get(_opCtx.get())
- ->abortIndexBuildByBuildUUID(
- _opCtx.get(), buildUUID, IndexBuildAction::kRollbackAbort, ""));
-}
-
-TEST_F(RSRollbackTest, AbortedIndexBuildsAreRestarted) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- // Create the necessary index.
- auto indexSpec = BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON(idxKey("0") << 1)
- << "name" << idxName("0") << "collation"
- << BSON("locale"
- << "fr"));
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto buildUUID = UUID::gen();
- // Store the commit quorum value for the index build in config.system.indexBuilds collection.
- _insertDocument(_opCtx.get(),
- NamespaceString::kIndexBuildEntryNamespace,
- BSON("_id" << buildUUID << "collectionUUID" << options.uuid.get()
- << "indexNames" << BSON_ARRAY(idxName("0")) << "commitQuorum" << 0));
-
- auto commonOp = makeOpAndRecordId(1);
-
- // Don't roll-back anything.
- auto remoteOplog = {commonOp};
- auto localOplog = {commonOp};
-
- // Even though the index has already completed, simulate that we aborted the index build before
- // rollback. We expect the index to be dropped and rebuilt.
- IndexBuildDetails build(coll->uuid());
- build.indexSpecs.push_back(indexSpec);
-
- IndexBuilds abortedBuilds{{buildUUID, build}};
-
- // Collection pointer will be stale after rollback
- coll = nullptr;
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- abortedBuilds,
- {},
- _coordinator,
- _replicationProcess.get()));
-
- auto collAfterRollback =
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss);
-
- // Make sure the collection indexes are in the proper state post-rollback.
- ASSERT_EQUALS(1, numIndexesOnColl(_opCtx.get(), nss, collAfterRollback));
- ASSERT_EQUALS(1, numIndexesInProgress(_opCtx.get(), nss, collAfterRollback));
-
- // Kill the index build we just restarted so the fixture can shut down.
- ASSERT_OK(_coordinator->setFollowerMode(MemberState::RS_ROLLBACK));
- ASSERT(IndexBuildsCoordinator::get(_opCtx.get())
- ->abortIndexBuildByBuildUUID(
- _opCtx.get(), buildUUID, IndexBuildAction::kRollbackAbort, ""));
-}
-
-TEST_F(RSRollbackTest, AbortedIndexBuildsAreNotRestartedWhenStartIsRolledBack) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- NamespaceString nss("test", "coll");
- auto coll = _createCollection(_opCtx.get(), nss.toString(), options);
-
- // Create the necessary index.
- auto indexSpec = BSON("v" << static_cast<int>(kIndexVersion) << "key" << BSON(idxKey("0") << 1)
- << "name" << idxName("0") << "collation"
- << BSON("locale"
- << "fr"));
-
- int numIndexes = _createIndexOnEmptyCollection(_opCtx.get(), coll, nss, indexSpec);
- ASSERT_EQUALS(2, numIndexes);
-
- auto commonOp = makeOpAndRecordId(1);
-
- // Roll-back a startIndexBuild oplog entry. This will cancel out with the aborted index build,
- // and the index will be dropped after rollback.
- auto buildUUID = UUID::gen();
- auto startIndexBuildOp = makeStartIndexBuildOplogEntry(coll, buildUUID, indexSpec, 2);
-
- auto remoteOplog = {commonOp};
- auto localOplog = {startIndexBuildOp, commonOp};
-
- // Create an index build to abort.
- IndexBuildDetails build(coll->uuid());
- build.indexSpecs.push_back(indexSpec);
- IndexBuilds abortedBuilds{{buildUUID, build}};
-
- // Collection pointer will be stale after rollback
- coll = nullptr;
-
- // Set up the mock rollback source and then run rollback.
- RollbackSourceMock rollbackSource(std::make_unique<OplogInterfaceMock>(remoteOplog));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock(localOplog),
- rollbackSource,
- abortedBuilds,
- {},
- _coordinator,
- _replicationProcess.get()));
-
- auto collAfterRollback =
- CollectionCatalog::get(_opCtx.get())->lookupCollectionByNamespace(_opCtx.get(), nss);
-
- // The aborted index build should have been dropped.
- ASSERT_EQUALS(1, numIndexesOnColl(_opCtx.get(), nss, collAfterRollback));
- ASSERT_EQUALS(0, numIndexesInProgress(_opCtx.get(), nss, collAfterRollback));
-}
-
-TEST_F(RSRollbackTest, RollbackUnknownCommand) {
- createOplog(_opCtx.get());
- auto commonOperation = makeOpAndRecordId(1);
- auto unknownCommandOperation = std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "c"
- << "ui" << UUID::gen() << "ns"
- << "test.t"
- << "wall" << Date_t() << "o"
- << BSON("emptycapped"
- << "t")),
- RecordId(2));
-
- auto status =
- syncRollback(_opCtx.get(),
- OplogInterfaceMock({unknownCommandOperation, commonOperation}),
- RollbackSourceMock(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- }))),
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
- ASSERT_STRING_CONTAINS(status.reason(), "unable to determine common point");
-}
-
-TEST_F(RSRollbackTest, RollbackRenameCollectionInSameDatabaseCommand) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto collection = _createCollection(_opCtx.get(), "test.y", options);
- UUID collectionUUID = collection->uuid();
-
- OpTime renameTime = OpTime(Timestamp(2, 0), 5);
-
- auto commonOperation = makeOpAndRecordId(1);
- auto renameCollectionOperation = makeRenameCollectionOplogEntry(NamespaceString("test.x"),
- NamespaceString("test.y"),
- collectionUUID,
- boost::none,
- false,
- renameTime);
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- {
- AutoGetCollectionForReadCommand renamedColl(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_TRUE(renamedColl.getCollection());
-
- AutoGetCollectionForReadCommand oldCollName(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_FALSE(oldCollName.getCollection());
- }
-
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({renameCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- {
- AutoGetCollectionForReadCommand renamedColl(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_FALSE(renamedColl.getCollection());
-
- AutoGetCollectionForReadCommand oldCollName(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_TRUE(oldCollName.getCollection());
-
- // Remote collection options should have been empty.
- auto collAfterRollbackOptions = oldCollName->getCollectionOptions();
- ASSERT_BSONOBJ_EQ(BSON("uuid" << *options.uuid), collAfterRollbackOptions.toBSON());
- }
-}
-
-TEST_F(RSRollbackTest,
- RollingBackRenameCollectionFromTempToPermanentCollectionSetsCollectionOptionToTemp) {
- createOplog(_opCtx.get());
-
- auto renameFromNss = NamespaceString("test.renameFrom");
- auto renameToNss = NamespaceString("test.renameTo");
-
- CollectionOptions options;
- options.uuid = UUID::gen();
- ASSERT_FALSE(options.temp);
-
- // Create the collection and save its UUID.
- auto collection = _createCollection(_opCtx.get(), renameToNss, options);
- auto collectionUUID = collection->uuid();
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)) {}
- StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const {
- getCollectionInfoCalled = true;
- return BSON("info" << BSON("uuid" << uuid) << "options" << BSON("temp" << true));
- }
- mutable bool getCollectionInfoCalled = false;
- };
-
- auto commonOperation = makeOpAndRecordId(1);
-
- bool stayTemp = false;
- auto renameCollectionOperation = makeRenameCollectionOplogEntry(NamespaceString(renameFromNss),
- NamespaceString(renameToNss),
- collectionUUID,
- boost::none,
- stayTemp,
- OpTime(Timestamp(2, 0), 5));
-
- RollbackSourceLocal rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({renameCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- ASSERT_TRUE(rollbackSource.getCollectionInfoCalled);
-
- AutoGetCollectionForReadCommand autoColl(_opCtx.get(), NamespaceString(renameFromNss));
- auto collAfterRollbackOptions = autoColl->getCollectionOptions();
- ASSERT_TRUE(collAfterRollbackOptions.temp);
- ASSERT_BSONOBJ_EQ(BSON("uuid" << *options.uuid << "temp" << true),
- collAfterRollbackOptions.toBSON());
-}
-
-TEST_F(RSRollbackTest, RollbackRenameCollectionInDatabaseWithDropTargetTrueCommand) {
- createOplog(_opCtx.get());
-
- OpTime dropTime = OpTime(Timestamp(2, 0), 5);
- auto dpns = NamespaceString("test.y").makeDropPendingNamespace(dropTime);
- CollectionOptions droppedCollOptions;
- droppedCollOptions.uuid = UUID::gen();
- auto droppedColl = _createCollection(_opCtx.get(), dpns, droppedCollOptions);
- _dropPendingCollectionReaper->addDropPendingNamespace(_opCtx.get(), dropTime, dpns);
- auto droppedCollectionUUID = droppedColl->uuid();
-
- CollectionOptions renamedCollOptions;
- renamedCollOptions.uuid = UUID::gen();
- auto renamedCollection = _createCollection(_opCtx.get(), "test.y", renamedCollOptions);
- auto renamedCollectionUUID = renamedCollection->uuid();
-
- auto commonOperation = makeOpAndRecordId(1);
- auto renameCollectionOperation = makeRenameCollectionOplogEntry(NamespaceString("test.x"),
- NamespaceString("test.y"),
- renamedCollectionUUID,
- droppedCollectionUUID,
- false,
- dropTime);
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- {
- AutoGetCollectionForReadCommand autoCollDropPending(_opCtx.get(), dpns);
- ASSERT_TRUE(autoCollDropPending.getCollection());
-
- AutoGetCollectionForReadCommand renamedColl(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_TRUE(renamedColl.getCollection());
-
- AutoGetCollectionForReadCommand oldCollName(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_FALSE(oldCollName.getCollection());
- }
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({renameCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- {
- AutoGetCollectionForReadCommand autoCollDropPending(_opCtx.get(), dpns);
- ASSERT_FALSE(autoCollDropPending.getCollection());
-
- AutoGetCollectionForReadCommand renamedColl(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_TRUE(renamedColl.getCollection());
- ASSERT_EQUALS(renamedColl.getCollection()->uuid(), renamedCollectionUUID);
-
- AutoGetCollectionForReadCommand droppedColl(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_TRUE(droppedColl.getCollection());
- ASSERT_EQUALS(droppedColl.getCollection()->uuid(), droppedCollectionUUID);
- }
-}
-
-
-void _testRollbackRenamingCollectionsToEachOther(OperationContext* opCtx,
- ReplicationCoordinator* replicationCoordinator,
- ReplicationProcess* replicationProcess,
- const CollectionOptions& coll1Options,
- const CollectionOptions& coll2Options) {
- createOplog(opCtx);
-
- auto collection1 = RollbackTest::_createCollection(opCtx, "test.y", coll1Options);
- auto collection1UUID = collection1->uuid();
-
- auto collection2 = RollbackTest::_createCollection(opCtx, "test.x", coll2Options);
- auto collection2UUID = collection2->uuid();
-
- ASSERT_NOT_EQUALS(collection1UUID, collection2UUID);
-
- auto commonOperation = makeOpAndRecordId(1);
- auto renameCollectionOperationXtoZ = makeRenameCollectionOplogEntry(NamespaceString("test.x"),
- NamespaceString("test.z"),
- collection1UUID,
- boost::none,
- false,
- OpTime(Timestamp(2, 0), 5));
-
- auto renameCollectionOperationYtoX = makeRenameCollectionOplogEntry(NamespaceString("test.y"),
- NamespaceString("test.x"),
- collection2UUID,
- boost::none,
- false,
- OpTime(Timestamp(3, 0), 5));
-
- auto renameCollectionOperationZtoY = makeRenameCollectionOplogEntry(NamespaceString("test.z"),
- NamespaceString("test.y"),
- collection1UUID,
- boost::none,
- false,
- OpTime(Timestamp(4, 0), 5));
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- ASSERT_OK(syncRollback(opCtx,
- OplogInterfaceMock({renameCollectionOperationZtoY,
- renameCollectionOperationYtoX,
- renameCollectionOperationXtoZ,
- commonOperation}),
- rollbackSource,
- {},
- {},
- replicationCoordinator,
- replicationProcess));
-
- {
-
- AutoGetCollectionForReadCommand coll1(opCtx, NamespaceString("test.x"));
- ASSERT_TRUE(coll1.getCollection());
- ASSERT_EQUALS(coll1.getCollection()->uuid(), collection1UUID);
-
- AutoGetCollectionForReadCommand coll2(opCtx, NamespaceString("test.y"));
- ASSERT_TRUE(coll2.getCollection());
- ASSERT_EQUALS(coll2.getCollection()->uuid(), collection2UUID);
- }
-}
-
-TEST_F(RSRollbackTest, RollbackRenamingCollectionsToEachOtherWithoutValidationOptions) {
- CollectionOptions coll1Options;
- coll1Options.uuid = UUID::gen();
-
- CollectionOptions coll2Options;
- coll2Options.uuid = UUID::gen();
-
- _testRollbackRenamingCollectionsToEachOther(
- _opCtx.get(), _coordinator, _replicationProcess.get(), coll1Options, coll2Options);
-}
-
-TEST_F(RSRollbackTest, RollbackRenamingCollectionsToEachOtherWithValidationOptions) {
- CollectionOptions coll1Options;
- coll1Options.uuid = UUID::gen();
- coll1Options.validator = BSON("x" << BSON("$exists" << 1));
- coll1Options.validationLevel = ValidationLevelEnum::moderate;
- coll1Options.validationAction = ValidationActionEnum::warn;
-
- CollectionOptions coll2Options;
- coll2Options.uuid = UUID::gen();
- coll2Options.validator = BSON("y" << BSON("$exists" << 1));
- coll2Options.validationLevel = ValidationLevelEnum::strict;
- coll2Options.validationAction = ValidationActionEnum::error;
-
- // renameOutOfTheWay() uses a temporary namespace to rename either of the two collections
- // affected by rollback. The temporary namespace should be able to support collections with
- // validation enabled.
- _testRollbackRenamingCollectionsToEachOther(
- _opCtx.get(), _coordinator, _replicationProcess.get(), coll1Options, coll2Options);
-}
-
-TEST_F(RSRollbackTest, RollbackDropCollectionThenRenameCollectionToDroppedCollectionNS) {
- createOplog(_opCtx.get());
-
- CollectionOptions renamedCollOptions;
- renamedCollOptions.uuid = UUID::gen();
- auto renamedCollection = _createCollection(_opCtx.get(), "test.x", renamedCollOptions);
- auto renamedCollectionUUID = renamedCollection->uuid();
-
- OpTime dropTime = OpTime(Timestamp(2, 0), 5);
- auto dpns = NamespaceString("test.x").makeDropPendingNamespace(dropTime);
- CollectionOptions droppedCollOptions;
- droppedCollOptions.uuid = UUID::gen();
- auto droppedCollection = _createCollection(_opCtx.get(), dpns, droppedCollOptions);
- auto droppedCollectionUUID = droppedCollection->uuid();
- _dropPendingCollectionReaper->addDropPendingNamespace(_opCtx.get(), dropTime, dpns);
-
- auto commonOperation = makeOpAndRecordId(1);
-
- auto dropCollectionOperation =
- std::make_pair(BSON("ts" << dropTime.getTimestamp() << "t" << dropTime.getTerm() << "op"
- << "c"
- << "ui" << droppedCollectionUUID << "ns"
- << "test.x"
- << "wall" << Date_t() << "o"
- << BSON("drop"
- << "x")),
- RecordId(2));
-
- auto renameCollectionOperation = makeRenameCollectionOplogEntry(NamespaceString("test.y"),
- NamespaceString("test.x"),
- renamedCollectionUUID,
- boost::none,
- false,
- OpTime(Timestamp(3, 0), 5));
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- {
- AutoGetCollectionForReadCommand autoCollDropPending(_opCtx.get(), dpns);
- ASSERT_TRUE(autoCollDropPending.getCollection());
- AutoGetCollectionForReadCommand autoCollX(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_TRUE(autoCollX.getCollection());
- AutoGetCollectionForReadCommand autoCollY(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_FALSE(autoCollY.getCollection());
- }
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({renameCollectionOperation, dropCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- {
- AutoGetCollectionForReadCommand autoCollDropPending(_opCtx.get(), dpns);
- ASSERT_FALSE(autoCollDropPending.getCollection());
-
- AutoGetCollectionForReadCommand autoCollX(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_TRUE(autoCollX.getCollection());
- ASSERT_EQUALS(autoCollX.getCollection()->uuid(), droppedCollectionUUID);
-
- AutoGetCollectionForReadCommand autoCollY(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_TRUE(autoCollY.getCollection());
- ASSERT_EQUALS(autoCollY.getCollection()->uuid(), renamedCollectionUUID);
- }
-}
-
-TEST_F(RSRollbackTest, RollbackRenameCollectionThenCreateNewCollectionWithOldName) {
- createOplog(_opCtx.get());
-
- CollectionOptions renamedCollOptions;
- renamedCollOptions.uuid = UUID::gen();
- auto renamedCollection = _createCollection(_opCtx.get(), "test.y", renamedCollOptions);
- auto renamedCollectionUUID = renamedCollection->uuid();
-
- CollectionOptions createdCollOptions;
- createdCollOptions.uuid = UUID::gen();
- auto createdCollection = _createCollection(_opCtx.get(), "test.x", createdCollOptions);
- auto createdCollectionUUID = createdCollection->uuid();
-
- auto commonOperation = makeOpAndRecordId(1);
-
- auto renameCollectionOperation = makeRenameCollectionOplogEntry(NamespaceString("test.x"),
- NamespaceString("test.y"),
- renamedCollectionUUID,
- boost::none,
- false,
- OpTime(Timestamp(2, 0), 5));
-
- auto createCollectionOperation =
- std::make_pair(BSON("ts" << Timestamp(Seconds(3), 0) << "op"
- << "c"
- << "ui" << createdCollectionUUID << "ns"
- << "test.x"
- << "wall" << Date_t() << "o"
- << BSON("create"
- << "x")),
- RecordId(3));
-
-
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- {
- AutoGetCollectionForReadCommand renamedColl(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_TRUE(renamedColl.getCollection());
- AutoGetCollectionForReadCommand createdColl(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_TRUE(createdColl.getCollection());
- }
- ASSERT_OK(syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({createCollectionOperation, renameCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-
- {
-
- AutoGetCollectionForReadCommand renamedColl(_opCtx.get(), NamespaceString("test.x"));
- ASSERT_TRUE(renamedColl.getCollection());
- ASSERT_EQUALS(renamedColl.getCollection()->uuid(), renamedCollectionUUID);
-
- AutoGetCollectionForReadCommand createdColl(_opCtx.get(), NamespaceString("test.y"));
- ASSERT_FALSE(createdColl.getCollection());
- }
-}
-
-TEST_F(RSRollbackTest, RollbackCollModCommandFailsIfRBIDChangesWhileSyncingCollectionMetadata) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
-
- auto commonOperation = makeOpAndRecordId(1);
- auto collModOperation = std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "c"
- << "ui" << coll->uuid() << "ns"
- << "test.t"
- << "wall" << Date_t() << "o"
- << BSON("collMod"
- << "t"
- << "validationLevel"
- << "off")),
- RecordId(2));
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- using RollbackSourceMock::RollbackSourceMock;
- int getRollbackId() const override {
- return getCollectionInfoCalled ? 1 : 0;
- }
- StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db,
- const UUID& uuid) const override {
- getCollectionInfoCalled = true;
- return BSONObj();
- }
- mutable bool getCollectionInfoCalled = false;
- };
- RollbackSourceLocal rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
-
- ASSERT_THROWS_CODE(syncRollback(_opCtx.get(),
- OplogInterfaceMock({collModOperation, commonOperation}),
- rollbackSource,
- {},
- 0,
- _coordinator,
- _replicationProcess.get()),
- DBException,
- 40508);
- ASSERT(rollbackSource.getCollectionInfoCalled);
-}
-
-TEST_F(RSRollbackTest, RollbackDropDatabaseCommand) {
- createOplog(_opCtx.get());
- auto commonOperation = makeOpAndRecordId(1);
- // 'dropDatabase' operations are special and do not include a UUID field.
- auto dropDatabaseOperation =
- std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "c"
- << "ns"
- << "test.$cmd"
- << "wall" << Date_t() << "o" << BSON("dropDatabase" << 1)),
- RecordId(2));
- RollbackSourceMock rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({dropDatabaseOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
-}
-
-BSONObj makeApplyOpsOplogEntry(Timestamp ts, std::initializer_list<BSONObj> ops) {
- // applyOps oplog entries are special and do not include a UUID field.
- BSONObjBuilder entry;
- entry << "ts" << ts << "op"
- << "c"
- << "ns"
- << "admin"
- << "wall" << Date_t();
- {
- BSONObjBuilder cmd(entry.subobjStart("o"));
- BSONArrayBuilder subops(entry.subarrayStart("applyOps"));
- for (const auto& op : ops) {
- subops << op;
- }
- }
- return entry.obj();
-}
-
-OpTime getOpTimeFromOplogEntry(const BSONObj& entry) {
- const BSONElement tsElement = entry["ts"];
- const BSONElement termElement = entry["t"];
- ASSERT_EQUALS(bsonTimestamp, tsElement.type()) << entry;
- ASSERT_TRUE(termElement.eoo() || termElement.isNumber()) << entry;
- long long term = 1LL;
- if (!termElement.eoo()) {
- term = termElement.numberLong();
- }
- return OpTime(tsElement.timestamp(), term);
-}
-
-TEST_F(RSRollbackTest, RollbackApplyOpsCommand) {
- createOplog(_opCtx.get());
- CollectionPtr coll;
- CollectionOptions options;
- options.uuid = UUID::gen();
- {
- AutoGetDb autoDb(_opCtx.get(), "test", MODE_X);
- mongo::WriteUnitOfWork wuow(_opCtx.get());
- coll = CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), NamespaceString("test.t"));
- if (!coll) {
- auto db = autoDb.ensureDbExists(_opCtx.get());
- coll = db->createCollection(_opCtx.get(), NamespaceString("test.t"), options);
- }
- ASSERT(coll);
- OpDebug* const nullOpDebug = nullptr;
- ASSERT_OK(coll->insertDocument(
- _opCtx.get(), InsertStatement(BSON("_id" << 1 << "v" << 2)), nullOpDebug, false));
- ASSERT_OK(coll->insertDocument(
- _opCtx.get(), InsertStatement(BSON("_id" << 2 << "v" << 4)), nullOpDebug, false));
- ASSERT_OK(coll->insertDocument(
- _opCtx.get(), InsertStatement(BSON("_id" << 4)), nullOpDebug, false));
- wuow.commit();
- }
- UUID uuid = coll->uuid();
- const auto commonOperation = makeOpAndRecordId(1);
- const auto applyOpsOperation =
- std::make_pair(makeApplyOpsOplogEntry(
- Timestamp(Seconds(2), 0),
- {BSON("op"
- << "u"
- << "ui" << uuid << "ts" << Timestamp(1, 1) << "t" << 1LL << "ns"
- << "test.t"
- << "o2" << BSON("_id" << 1) << "wall" << Date_t() << "o"
- << BSON("_id" << 1 << "v" << 2)),
- BSON("op"
- << "u"
- << "ui" << uuid << "ts" << Timestamp(2, 1) << "t" << 1LL << "ns"
- << "test.t"
- << "o2" << BSON("_id" << 2) << "wall" << Date_t() << "o"
- << BSON("_id" << 2 << "v" << 4)),
- BSON("op"
- << "d"
- << "ui" << uuid << "ts" << Timestamp(3, 1) << "t" << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 3)),
- BSON("op"
- << "i"
- << "ui" << uuid << "ts" << Timestamp(4, 1) << "t" << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 4)),
- // applyOps internal oplog entries are not required
- // to have a timestamp.
- BSON("op"
- << "i"
- << "ui" << uuid << "ts" << Timestamp(4, 1) << "t" << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 4)),
- BSON("op"
- << "i"
- << "ui" << uuid << "t" << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 4)),
- BSON("op"
- << "i"
- << "ui" << uuid << "t" << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 4))}),
- RecordId(2));
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)) {}
-
- std::pair<BSONObj, NamespaceString> findOneByUUID(const std::string& db,
- UUID uuid,
- const BSONObj& filter) const override {
- int numFields = 0;
- for (const auto& element : filter) {
- ++numFields;
- ASSERT_EQUALS("_id", element.fieldNameStringData()) << filter;
- }
- ASSERT_EQUALS(1, numFields) << filter;
- searchedIds.insert(filter.firstElement().numberInt());
- switch (filter.firstElement().numberInt()) {
- case 1:
- return {BSON("_id" << 1 << "v" << 1), NamespaceString()};
- case 2:
- return {BSON("_id" << 2 << "v" << 3), NamespaceString()};
- case 3:
- return {BSON("_id" << 3 << "v" << 5), NamespaceString()};
- case 4:
- return {};
- }
- FAIL("Unexpected findOne request") << filter;
- return {}; // Unreachable; why doesn't compiler know?
- }
-
- mutable std::multiset<int> searchedIds;
- } rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})));
-
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({applyOpsOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- ASSERT_EQUALS(4U, rollbackSource.searchedIds.size());
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(1));
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(2));
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(3));
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(4));
-
- AutoGetCollectionForReadCommand acr(_opCtx.get(), NamespaceString("test.t"));
- BSONObj result;
- ASSERT(Helpers::findOne(_opCtx.get(), acr.getCollection(), BSON("_id" << 1), result));
- ASSERT_EQUALS(1, result["v"].numberInt()) << result;
- ASSERT(Helpers::findOne(_opCtx.get(), acr.getCollection(), BSON("_id" << 2), result));
- ASSERT_EQUALS(3, result["v"].numberInt()) << result;
- ASSERT(Helpers::findOne(_opCtx.get(), acr.getCollection(), BSON("_id" << 3), result));
- ASSERT_EQUALS(5, result["v"].numberInt()) << result;
- ASSERT_FALSE(Helpers::findOne(_opCtx.get(), acr.getCollection(), BSON("_id" << 4), result))
- << result;
-}
-
-TEST_F(RSRollbackTest, RollbackCreateCollectionCommand) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
-
- auto commonOperation = makeOpAndRecordId(1);
- auto createCollectionOperation = std::make_pair(BSON("ts" << Timestamp(Seconds(2), 0) << "op"
- << "c"
- << "ui" << coll->uuid() << "ns"
- << "test.t"
- << "wall" << Date_t() << "o"
- << BSON("create"
- << "t")),
- RecordId(2));
- RollbackSourceMock rollbackSource(
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})));
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({createCollectionOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- {
- Lock::DBLock dbLock(_opCtx.get(), "test", MODE_S);
- auto databaseHolder = DatabaseHolder::get(_opCtx.get());
- auto db = databaseHolder->getDb(_opCtx.get(), TenantDatabaseName(boost::none, "test"));
- ASSERT_TRUE(db);
- ASSERT_FALSE(CollectionCatalog::get(_opCtx.get())
- ->lookupCollectionByNamespace(_opCtx.get(), NamespaceString("test.t")));
- }
-}
-
-TEST_F(RSRollbackTest, RollbackCollectionModificationCommand) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
-
- auto commonOperation = makeOpAndRecordId(1);
-
- BSONObj collModCmd = BSON("collMod"
- << "t"
- << "validationLevel"
- << "strict");
- auto collectionModificationOperation =
- makeCommandOp(Timestamp(Seconds(2), 0), coll->uuid(), "test.t", collModCmd, 2);
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)), called(false) {}
-
- // Remote collection options are empty.
- StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const {
- called = true;
- return BSON("options" << BSONObj() << "info" << BSON("uuid" << uuid));
- }
- mutable bool called;
- };
- RollbackSourceLocal rollbackSource(
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})));
-
- startCapturingLogMessages();
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({collectionModificationOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- stopCapturingLogMessages();
-
- ASSERT_TRUE(rollbackSource.called);
- for (const auto& message : getCapturedTextFormatLogMessages()) {
- ASSERT_TRUE(message.find("ignoring op with no _id during rollback. ns: test.t") ==
- std::string::npos);
- }
-
- // Make sure the collection options are correct.
- AutoGetCollectionForReadCommand autoColl(_opCtx.get(), NamespaceString("test.t"));
- auto collAfterRollbackOptions = autoColl->getCollectionOptions();
- ASSERT_BSONOBJ_EQ(BSON("uuid" << *options.uuid), collAfterRollbackOptions.toBSON());
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest,
- FullRemoteCollectionValidationOptionsAndEmptyLocalValidationOptions) {
- // Empty local collection options.
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
-
- // Full remote collection validation options.
- BSONObj remoteCollOptionsObj =
- BSON("validator" << BSON("x" << BSON("$exists" << 1)) << "validationLevel"
- << "moderate"
- << "validationAction"
- << "warn");
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest,
- PartialRemoteCollectionValidationOptionsAndEmptyLocalValidationOptions) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
-
- BSONObj remoteCollOptionsObj = BSON("validationLevel"
- << "moderate"
- << "validationAction"
- << "warn");
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest,
- PartialRemoteCollectionValidationOptionsAndFullLocalValidationOptions) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
- localCollOptions.validator = BSON("x" << BSON("$exists" << 1));
- localCollOptions.validationLevel = ValidationLevelEnum::moderate;
- localCollOptions.validationAction = ValidationActionEnum::warn;
-
- BSONObj remoteCollOptionsObj = BSON("validationLevel"
- << "strict"
- << "validationAction"
- << "error");
-
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest,
- EmptyRemoteCollectionValidationOptionsAndEmptyLocalValidationOptions) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
-
- BSONObj remoteCollOptionsObj = BSONObj();
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest,
- EmptyRemoteCollectionValidationOptionsAndFullLocalValidationOptions) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
- localCollOptions.validator = BSON("x" << BSON("$exists" << 1));
- localCollOptions.validationLevel = ValidationLevelEnum::moderate;
- localCollOptions.validationAction = ValidationActionEnum::warn;
-
- BSONObj remoteCollOptionsObj = BSONObj();
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest, LocalTempCollectionRemotePermanentCollection) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
- localCollOptions.temp = true;
-
- BSONObj remoteCollOptionsObj = BSONObj();
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest, LocalPermanentCollectionRemoteTempCollection) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
-
- BSONObj remoteCollOptionsObj = BSON("temp" << true);
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest, BothCollectionsTemp) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
- localCollOptions.temp = true;
-
- BSONObj remoteCollOptionsObj = BSON("temp" << true);
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RollbackResyncsCollectionOptionsTest, ChangingTempStatusAlsoChangesOtherCollectionOptions) {
- CollectionOptions localCollOptions;
- localCollOptions.uuid = UUID::gen();
- localCollOptions.temp = true;
-
- BSONObj remoteCollOptionsObj = BSON("validationLevel"
- << "strict"
- << "validationAction"
- << "error");
-
- resyncCollectionOptionsTest(localCollOptions, remoteCollOptionsObj);
-}
-
-TEST_F(RSRollbackTest, RollbackCollectionModificationCommandInvalidCollectionOptions) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
-
- auto commonOperation = makeOpAndRecordId(1);
-
- BSONObj collModCmd = BSON("collMod"
- << "t"
- << "validationLevel"
- << "strict");
- auto collectionModificationOperation =
- makeCommandOp(Timestamp(Seconds(2), 0), coll->uuid(), "test.t", collModCmd, 2);
-
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)) {}
- StatusWith<BSONObj> getCollectionInfoByUUID(const std::string& db, const UUID& uuid) const {
- return BSON("options" << 12345);
- }
- };
- RollbackSourceLocal rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({
- commonOperation,
- })));
- auto status =
- syncRollback(_opCtx.get(),
- OplogInterfaceMock({collectionModificationOperation, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
- ASSERT_STRING_CONTAINS(status.reason(), "Failed to parse options");
-}
-
-TEST(RSRollbackTest, LocalEntryWithoutNsIsFatal) {
- const auto validOplogEntry = BSON("op"
- << "i"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1));
- FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, validOplogEntry, false));
- const auto invalidOplogEntry = BSON("op"
- << "i"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << ""
- << "wall" << Date_t() << "o"
- << BSON("_id" << 1 << "a" << 1));
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, invalidOplogEntry, false),
- RSFatalException);
-}
-
-TEST(RSRollbackTest, LocalEntryWithoutOIsFatal) {
- const auto validOplogEntry = BSON("op"
- << "i"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1));
- FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, validOplogEntry, false));
- const auto invalidOplogEntry = BSON("op"
- << "i"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSONObj());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, invalidOplogEntry, false),
- RSFatalException);
-}
-
-DEATH_TEST_F(RSRollbackTest, LocalUpdateEntryWithoutO2IsFatal, "Fatal assertion") {
- const auto invalidOplogEntry = BSON("op"
- << "u"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o"
- << BSON("_id" << 1 << "a" << 1));
- FixUpInfo fui;
- updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, invalidOplogEntry, false)
- .ignore();
-}
-
-TEST(RSRollbackTest, LocalUpdateEntryWithEmptyO2IsFatal) {
- const auto validOplogEntry = BSON("op"
- << "u"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1)
- << "o2" << BSON("_id" << 1));
- FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, validOplogEntry, false));
- const auto invalidOplogEntry = BSON("op"
- << "u"
- << "ui" << UUID::gen() << "ts" << Timestamp(1, 1) << "t"
- << 1LL << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1)
- << "o2" << BSONObj());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, invalidOplogEntry, false),
- RSFatalException);
-}
-
-DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutSessionIdIsFatal, "invariant") {
- auto validOplogEntry = BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "op"
- << "i"
- << "ui" << UUID::gen() << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1));
- FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, validOplogEntry, false));
-
- const auto txnNumber = BSON("txnNumber" << 1LL);
- const auto noSessionIdOrStmtId = validOplogEntry.addField(txnNumber.firstElement());
-
- const auto stmtId = BSON("stmtId" << 1);
- const auto noSessionId = noSessionIdOrStmtId.addField(stmtId.firstElement());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, noSessionId, false),
- RSFatalException);
-}
-
-TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutTxnTableUUIDIsFatal) {
- // If txnNumber is present, but the transaction collection has no UUID, rollback fails.
- UUID uuid = UUID::gen();
- auto lsid = makeLogicalSessionIdForTest();
- auto entryWithTxnNumber =
- BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1) << "txnNumber" << 1LL
- << "stmtId" << 1 << "lsid" << lsid.toBSON());
-
- FixUpInfo fui;
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, entryWithTxnNumber, false),
- RSFatalException);
-}
-
-TEST_F(RSRollbackTest, LocalEntryWithTxnNumberAddsTransactionTableDocToBeRefetched) {
- FixUpInfo fui;
-
- // With no txnNumber present, no extra documents need to be refetched.
- auto entryWithoutTxnNumber =
- BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "op"
- << "i"
- << "ui" << UUID::gen() << "ns"
- << "test.t2"
- << "wall" << Date_t() << "o" << BSON("_id" << 2 << "a" << 2));
-
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, entryWithoutTxnNumber, false));
- ASSERT_EQ(fui.docsToRefetch.size(), 1U);
-
- // If txnNumber is present, and the transaction table exists and has a UUID, the session
- // transactions table document corresponding to the oplog entry's sessionId also needs to be
- // refetched.
- UUID uuid = UUID::gen();
- auto lsid = makeLogicalSessionIdForTest();
- auto entryWithTxnNumber =
- BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1) << "txnNumber" << 1LL
- << "stmtId" << 1 << "lsid" << lsid.toBSON());
- UUID transactionTableUUID = UUID::gen();
- fui.transactionTableUUID = transactionTableUUID;
-
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, entryWithTxnNumber, false));
- ASSERT_EQ(fui.docsToRefetch.size(), 3U);
-
- auto expectedObj = BSON("_id" << lsid.toBSON());
- DocID expectedTxnDoc(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end());
-}
-
-TEST_F(RSRollbackTest, LocalEntryWithPartialTxnAddsTransactionTableDocToBeRefetched) {
- FixUpInfo fui;
-
- // If txnNumber is present, and the transaction table exists and has a UUID, the session
- // transactions table document corresponding to the oplog entry's sessionId also needs to be
- // refetched. This is true even if "partialTxn" is set indicating this is part of a transaction
- // that may not have been committed.
- UUID uuid = UUID::gen();
- auto lsid = makeLogicalSessionIdForTest();
- auto entryWithTxnNumber =
- BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 1 << "a" << 1)))
- << "partialTxn" << true)
- << "txnNumber" << 1LL << "stmtId" << 1 << "lsid" << lsid.toBSON());
- UUID transactionTableUUID = UUID::gen();
- fui.transactionTableUUID = transactionTableUUID;
-
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, entryWithTxnNumber, false));
- ASSERT_EQ(fui.docsToRefetch.size(), 1U);
-
- auto expectedObj = BSON("_id" << lsid.toBSON());
- DocID expectedTxnDoc(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end());
-}
-
-TEST_F(RSRollbackTest, LocalAbortTxnRefetchesTransactionTableEntry) {
- // A rolled back abort, even if we rolled back no transaction operations, should refetch the
- // transaction table entry.
- FixUpInfo fui;
-
- auto lsid = makeLogicalSessionIdForTest();
- auto abortTxnEntry =
- BSON("ts" << Timestamp(Seconds(1), 1) << "t" << 1LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o" << BSON("abortTransaction" << 1) << "txnNumber"
- << 1LL << "stmtId" << 1 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(1), 0) << "t" << 1LL));
-
- UUID transactionTableUUID = UUID::gen();
- fui.transactionTableUUID = transactionTableUUID;
-
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, abortTxnEntry, false));
- ASSERT_EQ(fui.docsToRefetch.size(), 1U);
-
- auto expectedObj = BSON("_id" << lsid.toBSON());
- DocID expectedTxnDoc(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end());
-}
-
-TEST_F(RSRollbackTest, LocalEntryWithAbortedPartialTxnRefetchesOnlyTransactionTableEntry) {
- FixUpInfo fui;
-
- // If txnNumber is present, and the transaction table exists and has a UUID, the session
- // transactions table document corresponding to the oplog entry's sessionId also needs to be
- // refetched. This is true even if "partialTxn" is set indicating this is part of a transaction
- // that may not have been committed, and even if it is known that the transaction aborted.
- UUID uuid = UUID::gen();
- auto lsid = makeLogicalSessionIdForTest();
- auto abortTxnEntry =
- BSON("ts" << Timestamp(Seconds(1), 2) << "t" << 1LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o" << BSON("abortTransaction" << 1) << "txnNumber"
- << 1LL << "stmtId" << 1 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(1), 1) << "t" << 1LL));
-
- auto entryWithTxnNumber =
- BSON("ts" << Timestamp(Seconds(1), 1) << "t" << 1LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 1 << "a" << 1)))
- << "partialTxn" << true)
- << "txnNumber" << 1LL << "stmtId" << 1 << "lsid" << lsid.toBSON());
- UUID transactionTableUUID = UUID::gen();
- fui.transactionTableUUID = transactionTableUUID;
-
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, abortTxnEntry, false));
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, entryWithTxnNumber, false));
- ASSERT_EQ(fui.docsToRefetch.size(), 1U);
-
- auto expectedObj = BSON("_id" << lsid.toBSON());
- DocID expectedTxnDoc(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end());
-}
-
-TEST_F(RSRollbackTest, LocalEntryWithCommittedTxnRefetchesDocsAndTransactionTableEntry) {
- FixUpInfo fui;
- UUID uuid = UUID::gen();
- auto lsid = makeLogicalSessionIdForTest();
- auto commitTxnEntry =
- BSON("ts" << Timestamp(Seconds(1), 2) << "t" << 1LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 2 << "a" << 2)))
- << "count" << 2)
- << "txnNumber" << 1LL << "stmtId" << 2 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(1), 1) << "t" << 1LL));
- auto commitTxnOperation = std::make_pair(commitTxnEntry, RecordId(2));
-
- auto partialTxnEntry =
- BSON("ts" << Timestamp(Seconds(1), 1) << "t" << 1LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 1 << "a" << 1)))
- << "partialTxn" << true)
- << "txnNumber" << 1LL << "stmtId" << 1 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(0, 0) << "t" << -1LL));
-
- auto partialTxnOperation = std::make_pair(partialTxnEntry, RecordId(1));
- UUID transactionTableUUID = UUID::gen();
- fui.transactionTableUUID = transactionTableUUID;
-
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */,
- OplogInterfaceMock({commitTxnOperation, partialTxnOperation}),
- fui,
- commitTxnEntry,
- false));
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */,
- OplogInterfaceMock({commitTxnOperation, partialTxnOperation}),
- fui,
- partialTxnEntry,
- false));
- ASSERT_EQ(fui.docsToRefetch.size(), 3U);
-
- auto expectedObj = BSON("_id" << lsid.toBSON());
- DocID expectedTxnDoc(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedTxnDoc) != fui.docsToRefetch.end());
-
- auto expectedCrudObj = BSON("_id" << 2);
- auto expectedCrudDoc = DocID(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedCrudDoc) != fui.docsToRefetch.end());
-
- expectedCrudObj = BSON("_id" << 1);
- expectedCrudDoc = DocID(expectedObj, expectedObj.firstElement(), transactionTableUUID);
- ASSERT_TRUE(fui.docsToRefetch.find(expectedCrudDoc) != fui.docsToRefetch.end());
-}
-
-TEST_F(RSRollbackTest, RollbackFetchesTransactionOperationBeforeCommonPoint) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
- options.uuid = UUID::gen();
- auto txnTable = _createCollection(_opCtx.get(), "config.transactions", options);
-
- auto commonOperation = makeOpAndRecordId(10);
- UUID uuid = coll->uuid();
- auto lsid = makeLogicalSessionIdForTest();
- auto commitTxnEntry =
- BSON("ts" << Timestamp(Seconds(10), 12) << "t" << 10LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 0 << "a" << 0)))
- << "count" << 3)
- << "txnNumber" << 1LL << "stmtId" << 3 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(10), 11) << "t" << 10LL));
- auto commitTxnOperation = std::make_pair(commitTxnEntry, RecordId(12));
-
- auto entryAfterCommonPoint =
- BSON("ts" << Timestamp(Seconds(10), 11) << "t" << 10LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 1 << "a" << 1)))
- << "partialTxn" << true)
- << "txnNumber" << 1LL << "stmtId" << 2 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(10), 9) << "t" << 10LL));
- auto operationAfterCommonPoint = std::make_pair(entryAfterCommonPoint, RecordId(11));
-
- auto entryBeforeCommonPoint =
- BSON("ts" << Timestamp(Seconds(10), 9) << "t" << 10LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 2 << "a" << 2)))
- << "partialTxn" << true)
- << "txnNumber" << 1LL << "stmtId" << 1 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(0, 0) << "t" << -1LL));
- auto operationBeforeCommonPoint = std::make_pair(entryBeforeCommonPoint, RecordId(9));
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog, const UUID& txnTableUuid)
- : RollbackSourceMock(std::move(oplog)), _txnTableUuid(txnTableUuid) {}
-
- std::pair<BSONObj, NamespaceString> findOneByUUID(const std::string& db,
- UUID uuid,
- const BSONObj& filter) const override {
- int numFields = 0;
- if (uuid == _txnTableUuid) {
- // This unit test does not test transaction table fetches.
- return {BSONObj(), NamespaceString::kSessionTransactionsTableNamespace};
- }
- for (const auto& element : filter) {
- ++numFields;
- ASSERT_EQUALS("_id", element.fieldNameStringData()) << filter;
- }
- ASSERT_EQUALS(1, numFields) << filter;
- searchedIds.insert(filter.firstElement().numberInt());
- switch (filter.firstElement().numberInt()) {
- case 0:
- return {BSON("_id" << 0 << "v" << 0), NamespaceString()};
- case 1:
- return {BSON("_id" << 1 << "v" << 1), NamespaceString()};
- case 2:
- return {BSON("_id" << 2 << "v" << 3), NamespaceString()};
- }
- FAIL("Unexpected findOne request") << filter;
- return {}; // Unreachable; why doesn't compiler know?
- }
-
- mutable std::multiset<int> searchedIds;
-
- private:
- UUID _txnTableUuid;
-
- } rollbackSource(std::unique_ptr<OplogInterface>(
- new OplogInterfaceMock({commonOperation, operationBeforeCommonPoint})),
- txnTable->uuid());
-
- ASSERT_OK(syncRollback(_opCtx.get(),
- OplogInterfaceMock({commitTxnOperation,
- operationAfterCommonPoint,
- commonOperation,
- operationBeforeCommonPoint}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get()));
- ASSERT_EQUALS(3U, rollbackSource.searchedIds.size());
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(0));
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(1));
- ASSERT_EQUALS(1U, rollbackSource.searchedIds.count(2));
-}
-
-TEST_F(RSRollbackTest, RollbackIncompleteTransactionReturnsUnrecoverableRollbackError) {
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
- options.uuid = UUID::gen();
- auto txnTable = _createCollection(_opCtx.get(), "config.transactions", options);
-
- auto commonOperation = makeOpAndRecordId(10);
- UUID uuid = coll->uuid();
- auto lsid = makeLogicalSessionIdForTest();
- auto commitTxnEntry =
- BSON("ts" << Timestamp(Seconds(10), 12) << "t" << 10LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 0 << "a" << 0)))
- << "count" << 3)
- << "stmtId" << 3 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(10), 11) << "t" << 10LL));
- auto commitTxnOperation = std::make_pair(commitTxnEntry, RecordId(12));
-
- auto entryAfterCommonPoint =
- BSON("ts" << Timestamp(Seconds(10), 11) << "t" << 10LL << "op"
- << "c"
- << "ns"
- << "admin.$cmd"
- << "wall" << Date_t() << "o"
- << BSON("applyOps" << BSON_ARRAY(BSON("op"
- << "i"
- << "ui" << uuid << "ns"
- << "test.t"
- << "o" << BSON("_id" << 1 << "a" << 1)))
- << "partialTxn" << true)
- << "txnNumber" << 1LL << "stmtId" << 2 << "lsid" << lsid.toBSON() << "prevOpTime"
- << BSON("ts" << Timestamp(Seconds(10), 9) << "t" << 10LL));
- auto operationAfterCommonPoint = std::make_pair(entryAfterCommonPoint, RecordId(11));
-
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog, const UUID& txnTableUuid)
- : RollbackSourceMock(std::move(oplog)), _txnTableUuid(txnTableUuid) {}
-
- std::pair<BSONObj, NamespaceString> findOneByUUID(const std::string& db,
- UUID uuid,
- const BSONObj& filter) const override {
- if (uuid == _txnTableUuid) {
- // This unit test does not test transaction table fetches.
- return {BSONObj(), NamespaceString::kSessionTransactionsTableNamespace};
- } else {
- return {BSONObj(), NamespaceString()};
- }
- }
-
- private:
- UUID _txnTableUuid;
- } rollbackSource(std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})),
- txnTable->uuid());
-
-
- auto status = syncRollback(
- _opCtx.get(),
- OplogInterfaceMock({commitTxnOperation, operationAfterCommonPoint, commonOperation}),
- rollbackSource,
- {},
- {},
- _coordinator,
- _replicationProcess.get());
- ASSERT_EQUALS(ErrorCodes::UnrecoverableRollbackError, status.code());
-}
-
-TEST_F(RSRollbackTest, RollbackFailsIfTransactionDocumentRefetchReturnsDifferentNamespace) {
- createOplog(_opCtx.get());
-
- // Create a valid FixUpInfo struct for rolling back a single CRUD operation that has a
- // transaction number and session id.
- FixUpInfo fui;
-
- auto entryWithTxnNumber =
- BSON("ts" << Timestamp(Seconds(2), 1) << "t" << 1LL << "op"
- << "i"
- << "ui" << UUID::gen() << "ns"
- << "test.t"
- << "wall" << Date_t() << "o" << BSON("_id" << 1 << "a" << 1) << "txnNumber" << 1LL
- << "stmtId" << 1 << "lsid" << makeLogicalSessionIdForTest().toBSON());
-
- UUID transactionTableUUID = UUID::gen();
- fui.transactionTableUUID = transactionTableUUID;
-
- auto commonOperation = makeOpAndRecordId(1);
- fui.commonPoint = OpTime(Timestamp(Seconds(1), 1), 1LL);
- fui.commonPointOurDiskloc = RecordId(1);
-
- fui.rbid = 1;
-
- // The FixUpInfo will have an extra doc to refetch: the corresponding transaction table entry.
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(
- nullptr /* opCtx */, OplogInterfaceMock(), fui, entryWithTxnNumber, false));
- ASSERT_EQ(fui.docsToRefetch.size(), 2U);
-
- {
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)) {}
- std::pair<BSONObj, NamespaceString> findOneByUUID(
- const std::string& db, UUID uuid, const BSONObj& filter) const override {
- return {BSONObj(), NamespaceString::kSessionTransactionsTableNamespace};
- }
- int getRollbackId() const override {
- return 1;
- }
- };
-
- // Should not throw, since findOneByUUID will return the expected namespace.
- syncFixUp(_opCtx.get(),
- fui,
- RollbackSourceLocal(
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation}))),
- _coordinator,
- _replicationProcess.get());
- }
-
- {
- class RollbackSourceLocal : public RollbackSourceMock {
- public:
- RollbackSourceLocal(std::unique_ptr<OplogInterface> oplog)
- : RollbackSourceMock(std::move(oplog)) {}
- std::pair<BSONObj, NamespaceString> findOneByUUID(
- const std::string& db, UUID uuid, const BSONObj& filter) const override {
- return {BSONObj(), NamespaceString("foo.bar")};
- }
- int getRollbackId() const override {
- return 1;
- }
- };
-
- // The returned namespace will not be the expected one, implying a rename/drop of the
- // transactions collection across this node and the sync source, so rollback should fail.
- ASSERT_THROWS(syncFixUp(_opCtx.get(),
- fui,
- RollbackSourceLocal(std::unique_ptr<OplogInterface>(
- new OplogInterfaceMock({commonOperation}))),
- _coordinator,
- _replicationProcess.get()),
- RSFatalException);
- }
-}
-
-TEST_F(RSRollbackTest, RollbackReturnsImmediatelyOnFailureToTransitionToRollback) {
- // On failing to transition to ROLLBACK, rollback() should return immediately and not call
- // syncRollback(). We provide an empty oplog so that if syncRollback() is called erroneously,
- // we would go fatal.
- OplogInterfaceMock localOplogWithSingleOplogEntry({makeNoopOplogEntryAndRecordId(Seconds(1))});
- RollbackSourceMock rollbackSourceWithInvalidOplog(std::make_unique<OplogInterfaceMock>());
-
- // Inject ReplicationCoordinator::setFollowerMode() error. We set the current member state
- // because it will be logged by rollback() on failing to transition to ROLLBACK.
- ASSERT_OK(_coordinator->setFollowerMode(MemberState::RS_SECONDARY));
- _coordinator->failSettingFollowerMode(MemberState::RS_ROLLBACK, ErrorCodes::NotSecondary);
-
- startCapturingLogMessages();
- rollback(_opCtx.get(),
- localOplogWithSingleOplogEntry,
- rollbackSourceWithInvalidOplog,
- {},
- _coordinator,
- _replicationProcess.get());
- stopCapturingLogMessages();
-
- ASSERT_EQUALS(1,
- countTextFormatLogLinesContaining("Cannot perform replica set state transition"));
- ASSERT_EQUALS(MemberState(MemberState::RS_SECONDARY), _coordinator->getMemberState());
-}
-
-DEATH_TEST_REGEX_F(RSRollbackTest,
- RollbackUnrecoverableRollbackErrorTriggersFatalAssertion,
- "Unable to complete rollback. A full resync may be needed") {
- // rollback() should abort on getting UnrecoverableRollbackError from syncRollback(). An empty
- // local oplog will make syncRollback() return the intended error.
- OplogInterfaceMock localOplogWithSingleOplogEntry({makeNoopOplogEntryAndRecordId(Seconds(1))});
- RollbackSourceMock rollbackSourceWithInvalidOplog(std::make_unique<OplogInterfaceMock>());
-
- rollback(_opCtx.get(),
- localOplogWithSingleOplogEntry,
- rollbackSourceWithInvalidOplog,
- {},
- _coordinator,
- _replicationProcess.get());
-}
-
-TEST_F(RSRollbackTest, RollbackLogsRetryMessageAndReturnsOnNonUnrecoverableRollbackError) {
- // If local oplog is empty, syncRollback() returns OplogStartMissing (instead of
- // UnrecoverableRollbackError when the remote oplog is missing). rollback() should log a message
- // about retrying rollback later before returning.
- OplogInterfaceMock localOplogWithNoEntries;
- OplogInterfaceMock::Operations remoteOperations({makeNoopOplogEntryAndRecordId(Seconds(1))});
- auto remoteOplog = std::make_unique<OplogInterfaceMock>(remoteOperations);
- RollbackSourceMock rollbackSourceWithValidOplog(std::move(remoteOplog));
- auto noopSleepSecsFn = [](int) {};
-
- startCapturingLogMessages();
- rollback(_opCtx.get(),
- localOplogWithNoEntries,
- rollbackSourceWithValidOplog,
- {},
- _coordinator,
- _replicationProcess.get(),
- noopSleepSecsFn);
- stopCapturingLogMessages();
-
- ASSERT_EQUALS(1,
- countTextFormatLogLinesContaining(
- "Rollback cannot complete at this time (retrying later)"));
- ASSERT_EQUALS(MemberState(MemberState::RS_RECOVERING), _coordinator->getMemberState());
-}
-
-DEATH_TEST_F(RSRollbackTest,
- RollbackTriggersFatalAssertionOnDetectingShardIdentityDocumentRollback,
- "shardIdentity document rollback detected. Shutting down to clear in-memory sharding "
- "state. Restarting this process should safely return it to a healthy state") {
- auto commonOperation = makeNoopOplogEntryAndRecordId(Seconds(1));
- OplogInterfaceMock localOplog({commonOperation});
- RollbackSourceMock rollbackSource(
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})));
-
- ASSERT_FALSE(ShardIdentityRollbackNotifier::get(_opCtx.get())->didRollbackHappen());
- ShardIdentityRollbackNotifier::get(_opCtx.get())->recordThatRollbackHappened();
- ASSERT_TRUE(ShardIdentityRollbackNotifier::get(_opCtx.get())->didRollbackHappen());
-
- createOplog(_opCtx.get());
- rollback(_opCtx.get(), localOplog, rollbackSource, {}, _coordinator, _replicationProcess.get());
-}
-
-DEATH_TEST_REGEX_F(
- RSRollbackTest,
- RollbackTriggersFatalAssertionOnFailingToTransitionToRecoveringAfterSyncRollbackReturns,
- "Failed to perform replica set state transition") {
- auto commonOperation = makeNoopOplogEntryAndRecordId(Seconds(1));
- OplogInterfaceMock localOplog({commonOperation});
- RollbackSourceMock rollbackSource(
- std::unique_ptr<OplogInterface>(new OplogInterfaceMock({commonOperation})));
-
- _coordinator->failSettingFollowerMode(MemberState::RS_RECOVERING, ErrorCodes::IllegalOperation);
-
- createOplog(_opCtx.get());
- rollback(_opCtx.get(), localOplog, rollbackSource, {}, _coordinator, _replicationProcess.get());
-}
-
-// The testcases used here are trying to detect off-by-one errors in
-// FixUpInfo::removeAllDocsToRefectchFor.
-TEST(FixUpInfoTest, RemoveAllDocsToRefetchForWorks) {
- const auto normalHolder = BSON("" << OID::gen());
- const auto normalKey = normalHolder.firstElement();
-
- UUID uuid1 = UUID::gen();
- UUID uuid2 = UUID::gen();
- UUID uuid3 = UUID::gen();
-
- // Can't use ASSERT_EQ with this since it isn't ostream-able. Failures will at least give you
- // the size. If that isn't enough, use GDB.
- using DocSet = std::set<DocID>;
-
- FixUpInfo fui;
- fui.docsToRefetch = {
- DocID::minFor(uuid1),
- DocID{{}, normalKey, uuid1},
- DocID::maxFor(uuid1),
-
- DocID::minFor(uuid2),
- DocID{{}, normalKey, uuid2},
- DocID::maxFor(uuid2),
-
- DocID::minFor(uuid3),
- DocID{{}, normalKey, uuid3},
- DocID::maxFor(uuid3),
- };
-
- // Remove from the middle.
- fui.removeAllDocsToRefetchFor(uuid2);
- ASSERT((fui.docsToRefetch ==
- DocSet{
- DocID::minFor(uuid1),
- DocID{{}, normalKey, uuid1},
- DocID::maxFor(uuid1),
-
- DocID::minFor(uuid3),
- DocID{{}, normalKey, uuid3},
- DocID::maxFor(uuid3),
- }))
- << "remaining docs: " << fui.docsToRefetch.size();
-
- // Remove from the end.
- fui.removeAllDocsToRefetchFor(uuid3);
- ASSERT((fui.docsToRefetch ==
- DocSet{
- DocID::minFor(uuid1), // This comment helps clang-format.
- DocID{{}, normalKey, uuid1},
- DocID::maxFor(uuid1),
- }))
- << "remaining docs: " << fui.docsToRefetch.size();
-
- // Everything else.
- fui.removeAllDocsToRefetchFor(uuid1);
- ASSERT((fui.docsToRefetch == DocSet{})) << "remaining docs: " << fui.docsToRefetch.size();
-}
-
-TEST_F(RSRollbackTest, RollbackInvalidatesDefaultRWConcernCache) {
- auto& rwcDefaults = ReadWriteConcernDefaults::get(getServiceContext());
-
- // Put initial defaults in the cache.
- {
- RWConcernDefault origDefaults;
- origDefaults.setUpdateOpTime(Timestamp(10, 20));
- origDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
- _lookupMock.setLookupCallReturnValue(std::move(origDefaults));
- }
- auto origCachedDefaults = rwcDefaults.getDefault(_opCtx.get());
- ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getUpdateOpTime());
- ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getUpdateWallClockTime());
-
- // Change the mock's defaults, but don't invalidate the cache yet. The cache should still return
- // the original defaults.
- {
- RWConcernDefault newDefaults;
- newDefaults.setUpdateOpTime(Timestamp(50, 20));
- newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678));
- _lookupMock.setLookupCallReturnValue(std::move(newDefaults));
-
- auto cachedDefaults = rwcDefaults.getDefault(_opCtx.get());
- ASSERT_EQ(Timestamp(10, 20), *cachedDefaults.getUpdateOpTime());
- ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *cachedDefaults.getUpdateWallClockTime());
- }
-
- // Rollback via refetch should invalidate the cache and getting the defaults should now return
- // the latest value.
- createOplog(_opCtx.get());
- CollectionOptions options;
- options.uuid = UUID::gen();
- auto coll = _createCollection(_opCtx.get(), "test.t", options);
- BSONObj doc = BSON("_id" << 0 << "a" << 1);
- _testRollbackDelete(_opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), doc);
-
- auto newCachedDefaults = rwcDefaults.getDefault(_opCtx.get());
- ASSERT_EQ(Timestamp(50, 20), *newCachedDefaults.getUpdateOpTime());
- ASSERT_EQ(Date_t::fromMillisSinceEpoch(5678), *newCachedDefaults.getUpdateWallClockTime());
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp
index 060e18a0361..6912206db8c 100644
--- a/src/mongo/db/repl/storage_interface_impl_test.cpp
+++ b/src/mongo/db/repl/storage_interface_impl_test.cpp
@@ -52,6 +52,7 @@
#include "mongo/db/repl/replication_coordinator_mock.h"
#include "mongo/db/repl/storage_interface_impl.h"
#include "mongo/db/service_context_d_test_fixture.h"
+#include "mongo/db/storage/snapshot_manager.h"
#include "mongo/stdx/thread.h"
#include "mongo/transport/transport_layer_mock.h"
#include "mongo/unittest/unittest.h"
@@ -385,12 +386,6 @@ TEST_F(StorageInterfaceImplTest, GetRollbackIDReturnsBadStatusIfRollbackIDIsNotI
ASSERT_EQUALS(ErrorCodes::TypeMismatch, storage.getRollbackID(opCtx).getStatus());
}
-TEST_F(StorageInterfaceImplTest, SnapshotSupported) {
- auto opCtx = getOperationContext();
- Status status = opCtx->recoveryUnit()->majorityCommittedSnapshotAvailable();
- ASSERT(status.isOK());
-}
-
TEST_F(StorageInterfaceImplTest, InsertDocumentsReturnsOKWhenNoOperationsAreGiven) {
auto opCtx = getOperationContext();
auto nss = makeNamespace(_agent);
diff --git a/src/mongo/db/repl/storage_timestamp_test.cpp b/src/mongo/db/repl/storage_timestamp_test.cpp
index f03c8306fa3..fead4dab5ee 100644
--- a/src/mongo/db/repl/storage_timestamp_test.cpp
+++ b/src/mongo/db/repl/storage_timestamp_test.cpp
@@ -251,12 +251,7 @@ public:
repl::ReplicationConsistencyMarkers* _consistencyMarkers;
StorageTimestampTest()
- : ServiceContextMongoDTest("wiredTiger",
- ServiceContextMongoDTest::RepairAction::kNoRepair,
- kDefaultStorageEngineInitFlags,
- true, // useReplSettings
- true // useMockClock
- ) {
+ : ServiceContextMongoDTest(Options{}.useReplSettings(true).useMockClock(true)) {
// Set up mongod.
ServiceContextMongoDTest::setUp();
diff --git a/src/mongo/db/repl/tenant_migration_donor_service_test.cpp b/src/mongo/db/repl/tenant_migration_donor_service_test.cpp
index 765c8de625f..e76097432b6 100644
--- a/src/mongo/db/repl/tenant_migration_donor_service_test.cpp
+++ b/src/mongo/db/repl/tenant_migration_donor_service_test.cpp
@@ -60,11 +60,6 @@ class TenantMigrationDonorServiceTest : public ServiceContextMongoDTest {
ServiceContextMongoDTest::setUp();
auto serviceContext = getServiceContext();
- // Set up clocks.
- serviceContext->setFastClockSource(std::make_unique<SharedClockSourceAdapter>(_clkSource));
- serviceContext->setPreciseClockSource(
- std::make_unique<SharedClockSourceAdapter>(_clkSource));
-
WaitForMajorityService::get(getServiceContext()).startup(getServiceContext());
{
@@ -130,9 +125,11 @@ class TenantMigrationDonorServiceTest : public ServiceContextMongoDTest {
}
protected:
+ TenantMigrationDonorServiceTest() : ServiceContextMongoDTest(Options{}.useMockClock(true)) {}
+
PrimaryOnlyServiceRegistry* _registry;
PrimaryOnlyService* _service;
- std::shared_ptr<ClockSourceMock> _clkSource = std::make_shared<ClockSourceMock>();
+ ClockSourceMock _clkSource;
long long _term = 0;
const TenantMigrationPEMPayload kDonorPEMPayload = [&] {
@@ -170,7 +167,7 @@ protected:
TEST_F(TenantMigrationDonorServiceTest, CheckSettingMigrationStartDate) {
// Advance the clock by some arbitrary amount of time so we are not starting at 0 seconds.
- _clkSource->advance(Milliseconds(10000));
+ _clkSource.advance(Milliseconds(10000));
auto taskFp =
globalFailPointRegistry().find("pauseTenantMigrationAfterPersistingInitialDonorStateDoc");
diff --git a/src/mongo/db/repl/tenant_migration_recipient_service_test.cpp b/src/mongo/db/repl/tenant_migration_recipient_service_test.cpp
index 2763fe2bfd6..589b3e63cd0 100644
--- a/src/mongo/db/repl/tenant_migration_recipient_service_test.cpp
+++ b/src/mongo/db/repl/tenant_migration_recipient_service_test.cpp
@@ -181,11 +181,6 @@ public:
ConnectionString::setConnectionHook(mongo::MockConnRegistry::get()->getConnStrHook());
- // Set up clocks.
- serviceContext->setFastClockSource(std::make_unique<SharedClockSourceAdapter>(_clkSource));
- serviceContext->setPreciseClockSource(
- std::make_unique<SharedClockSourceAdapter>(_clkSource));
-
WaitForMajorityService::get(serviceContext).startup(serviceContext);
// Automatically mark the state doc garbage collectable after data sync completion.
@@ -256,10 +251,6 @@ public:
auto fetchCommittedTransactionsFp =
globalFailPointRegistry().find("skipFetchingCommittedTransactions");
fetchCommittedTransactionsFp->setMode(FailPoint::alwaysOn);
-
- // Timestamps of "0 seconds" are not allowed, so we must advance our clock mock to the first
- // real second.
- _clkSource->advance(Milliseconds(1000));
}
void tearDown() override {
@@ -305,6 +296,9 @@ public:
}
protected:
+ TenantMigrationRecipientServiceTest()
+ : ServiceContextMongoDTest(Options{}.useMockClock(true)) {}
+
PrimaryOnlyServiceRegistry* _registry;
PrimaryOnlyService* _service;
long long _term = 0;
@@ -416,14 +410,14 @@ protected:
* Advance the time by millis on both clock source mocks.
*/
void advanceTime(Milliseconds millis) {
- _clkSource->advance(millis);
+ _clkSource.advance(millis);
}
/**
* Assumes that the times on both clock source mocks is the same.
*/
Date_t now() {
- return _clkSource->now();
+ return _clkSource.now();
};
/*
@@ -456,11 +450,11 @@ protected:
}
ClockSource* clock() {
- return _clkSource.get();
+ return &_clkSource;
}
private:
- std::shared_ptr<ClockSourceMock> _clkSource = std::make_shared<ClockSourceMock>();
+ ClockSourceMock _clkSource;
unittest::MinimumLoggedSeverityGuard _replicationSeverityGuard{
logv2::LogComponent::kReplication, logv2::LogSeverity::Debug(1)};