diff options
-rw-r--r-- | jstests/auth/security_token.js | 5 | ||||
-rw-r--r-- | jstests/auth/token_privileges.js | 6 | ||||
-rw-r--r-- | src/mongo/db/catalog/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_catalog.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection.h | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection_test.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/catalog/rename_collection.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/catalog/rename_collection.h | 1 | ||||
-rw-r--r-- | src/mongo/db/catalog/rename_collection_test.cpp | 252 | ||||
-rw-r--r-- | src/mongo/db/op_observer/op_observer_impl_test.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/idempotency_test.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/idempotency_test_fixture.cpp | 38 | ||||
-rw-r--r-- | src/mongo/db/repl/idempotency_test_fixture.h | 7 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog.cpp | 41 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog_applier_impl_test.cpp | 493 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog_entry.cpp | 1 |
18 files changed, 693 insertions, 217 deletions
diff --git a/jstests/auth/security_token.js b/jstests/auth/security_token.js index b41c175545a..7de1a5fbb2b 100644 --- a/jstests/auth/security_token.js +++ b/jstests/auth/security_token.js @@ -154,16 +154,13 @@ function runTests(enabled) { MongoRunner.stopMongod(standalone); } - // TODO SERVER-62395: Uncomment this test once we use tid to construct namespace when applying - // non-txn commands. - /* { + { const rst = new ReplSetTest({nodes: 2, nodeOptions: opts}); rst.startSet({keyFile: 'jstests/libs/key1'}); rst.initiate(); runTest(rst.getPrimary(), enabled, rst); rst.stopSet(); } - */ // Do not test sharding since mongos must have an authenticated connection to // all mongod nodes, and this conflicts with proxying tokens which we'll be // performing in mongoq. diff --git a/jstests/auth/token_privileges.js b/jstests/auth/token_privileges.js index e97a8179c59..c372202a210 100644 --- a/jstests/auth/token_privileges.js +++ b/jstests/auth/token_privileges.js @@ -73,13 +73,11 @@ const opts = { MongoRunner.stopMongod(standalone); } -// TODO SERVER-62395: Uncomment this test once we use tid to construct namespace when applying -// non-txn commands. -/* { +{ const rst = new ReplSetTest({nodes: 2, nodeOptions: opts}); rst.startSet({keyFile: 'jstests/libs/key1'}); rst.initiate(); runTest(rst.getPrimary(), rst); rst.stopSet(); -} */ +} })(); diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index f8ba8c26dfa..127e34ce2e2 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -533,6 +533,7 @@ env.Library( '$BUILD_DIR/mongo/db/ops/write_ops', '$BUILD_DIR/mongo/db/query_exec', '$BUILD_DIR/mongo/db/repl/drop_pending_collection_reaper', + '$BUILD_DIR/mongo/db/server_feature_flags', '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/shard_role', '$BUILD_DIR/mongo/db/stats/top', @@ -543,6 +544,7 @@ env.Library( '$BUILD_DIR/mongo/db/ttl_collection_cache', '$BUILD_DIR/mongo/db/views/view_catalog_helpers', '$BUILD_DIR/mongo/db/views/views', + '$BUILD_DIR/mongo/util/namespace_string_util', 'cannot_convert_index_to_unique_info', 'clustered_collection_options', 'collection_options', diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp index 999db1230da..5c0f8dc72ed 100644 --- a/src/mongo/db/catalog/collection_catalog.cpp +++ b/src/mongo/db/catalog/collection_catalog.cpp @@ -1328,7 +1328,7 @@ NamespaceString CollectionCatalog::resolveNamespaceStringOrUUID( uassert(ErrorCodes::NamespaceNotFound, str::stream() << "UUID " << nsOrUUID.toString() << " specified in " << nsOrUUID.dbname() << " resolved to a collection in a different database: " << *resolvedNss, - resolvedNss->db() == nsOrUUID.dbname()); + resolvedNss->dbName() == nsOrUUID.dbName()); return std::move(*resolvedNss); } diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index 02a3a421755..33c5ea93a1a 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -673,14 +673,13 @@ Status createCollection(OperationContext* opCtx, const CreateCommand& cmd) { return createCollection(opCtx, cmd.getNamespace(), options, idIndex); } -// TODO SERVER-62395 Pass DatabaseName instead of dbName, and pass to isDbLockedForMode. Status createCollectionForApplyOps(OperationContext* opCtx, - const std::string& dbname, + const DatabaseName& dbName, const boost::optional<UUID>& ui, const BSONObj& cmdObj, const bool allowRenameOutOfTheWay, const boost::optional<BSONObj>& idIndex) { - const DatabaseName dbName(boost::none, dbname); + invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_IX)); const NamespaceString newCollName(CommandHelpers::parseNsCollectionRequired(dbName, cmdObj)); diff --git a/src/mongo/db/catalog/create_collection.h b/src/mongo/db/catalog/create_collection.h index 6b8b4b51801..08754d89daa 100644 --- a/src/mongo/db/catalog/create_collection.h +++ b/src/mongo/db/catalog/create_collection.h @@ -79,7 +79,7 @@ Status createVirtualCollection(OperationContext* opCtx, * error. */ Status createCollectionForApplyOps(OperationContext* opCtx, - const std::string& dbName, + const DatabaseName& dbName, const boost::optional<UUID>& ui, const BSONObj& cmdObj, bool allowRenameOutOfTheWay, diff --git a/src/mongo/db/catalog/create_collection_test.cpp b/src/mongo/db/catalog/create_collection_test.cpp index c1b47d20166..aa069473543 100644 --- a/src/mongo/db/catalog/create_collection_test.cpp +++ b/src/mongo/db/catalog/create_collection_test.cpp @@ -179,7 +179,7 @@ TEST_F(CreateCollectionTest, CreateCollectionForApplyOpsWithSpecificUuidNoExisti auto uuid = UUID::gen(); Lock::DBLock lock(opCtx.get(), newNss.dbName(), MODE_IX); ASSERT_OK(createCollectionForApplyOps(opCtx.get(), - newNss.db().toString(), + newNss.dbName(), uuid, BSON("create" << newNss.coll()), /*allowRenameOutOfTheWay*/ false)); @@ -208,7 +208,7 @@ TEST_F(CreateCollectionTest, // This should rename the existing collection 'curNss' to the collection 'newNss' we are trying // to create. ASSERT_OK(createCollectionForApplyOps(opCtx.get(), - newNss.db().toString(), + newNss.dbName(), uuid, BSON("create" << newNss.coll()), /*allowRenameOutOfTheWay*/ true)); @@ -237,7 +237,7 @@ TEST_F(CreateCollectionTest, // This should rename the existing collection 'newNss' to a randomly generated collection name. ASSERT_OK(createCollectionForApplyOps(opCtx.get(), - newNss.db().toString(), + newNss.dbName(), uuid, BSON("create" << newNss.coll()), /*allowRenameOutOfTheWay*/ true)); @@ -278,7 +278,7 @@ TEST_F(CreateCollectionTest, // state. ASSERT_EQUALS(ErrorCodes::NamespaceExists, createCollectionForApplyOps(opCtx.get(), - newNss.db().toString(), + newNss.dbName(), uuid, BSON("create" << newNss.coll()), /*allowRenameOutOfTheWay*/ false)); diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index eaeca865775..96cdde914fa 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -60,6 +60,7 @@ #include "mongo/db/service_context.h" #include "mongo/logv2/log.h" #include "mongo/util/fail_point.h" +#include "mongo/util/namespace_string_util.h" #include "mongo/util/scopeguard.h" #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand @@ -886,6 +887,11 @@ Status renameCollection(OperationContext* opCtx, "renaming system.js collection or renaming to system.js is not allowed"); } + if (source.tenantId() != target.tenantId()) { + return Status(ErrorCodes::IllegalOperation, + "renaming a collection between tenants is not allowed"); + } + StringData dropTargetMsg = options.dropTarget ? "yes"_sd : "no"_sd; LOGV2(20400, "renameCollectionForCommand: rename {source} to {target}{dropTargetMsg}", @@ -902,7 +908,6 @@ Status renameCollection(OperationContext* opCtx, } Status renameCollectionForApplyOps(OperationContext* opCtx, - const std::string& dbName, const boost::optional<UUID>& uuidToRename, const BSONObj& cmd, const repl::OpTime& renameOpTime) { @@ -914,8 +919,14 @@ Status renameCollectionForApplyOps(OperationContext* opCtx, "renameCollection() cannot accept a rename optime when writes are replicated."); } - const auto sourceNsElt = cmd.firstElement(); + const auto tenantIdElt = cmd["tid"]; + const auto sourceNsElt = cmd["renameCollection"]; const auto targetNsElt = cmd["to"]; + + if (!tenantIdElt.eoo()) + uassert(ErrorCodes::TypeMismatch, + "'tid' must be of type OID", + tenantIdElt.type() == BSONType::jstOID); uassert(ErrorCodes::TypeMismatch, "'renameCollection' must be of type String", sourceNsElt.type() == BSONType::String); @@ -923,8 +934,20 @@ Status renameCollectionForApplyOps(OperationContext* opCtx, "'to' must be of type String", targetNsElt.type() == BSONType::String); - NamespaceString sourceNss(sourceNsElt.valueStringData()); - NamespaceString targetNss(targetNsElt.valueStringData()); + boost::optional<TenantId> tenantId = tenantIdElt.eoo() + ? boost::none + : boost::optional<TenantId>{TenantId::parseFromBSON(tenantIdElt)}; + + NamespaceString sourceNss{ + NamespaceStringUtil::deserialize(tenantId, sourceNsElt.valueStringData())}; + NamespaceString targetNss{ + NamespaceStringUtil::deserialize(tenantId, targetNsElt.valueStringData())}; + + // TODO: not needed once we are no longer parsing for prefixed tenantIds + uassert(ErrorCodes::IllegalOperation, + "moving a collection between tenants is not allowed", + sourceNss.tenantId() == targetNss.tenantId()); + if (uuidToRename) { auto nss = CollectionCatalog::get(opCtx)->lookupNSSByUUID(opCtx, uuidToRename.value()); if (nss) diff --git a/src/mongo/db/catalog/rename_collection.h b/src/mongo/db/catalog/rename_collection.h index 253a358f89c..6920b130977 100644 --- a/src/mongo/db/catalog/rename_collection.h +++ b/src/mongo/db/catalog/rename_collection.h @@ -75,7 +75,6 @@ Status renameCollection(OperationContext* opCtx, * drop-pending collection. */ Status renameCollectionForApplyOps(OperationContext* opCtx, - const std::string& dbName, const boost::optional<UUID>& uuidToRename, const BSONObj& cmd, const repl::OpTime& renameOpTime); diff --git a/src/mongo/db/catalog/rename_collection_test.cpp b/src/mongo/db/catalog/rename_collection_test.cpp index dfd7d690968..8b0296b59f0 100644 --- a/src/mongo/db/catalog/rename_collection_test.cpp +++ b/src/mongo/db/catalog/rename_collection_test.cpp @@ -56,6 +56,7 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_mock.h" #include "mongo/db/service_context_d_test_fixture.h" +#include "mongo/idl/server_parameter_test_util.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" #include "mongo/util/assert_util.h" @@ -300,25 +301,24 @@ void OpObserverMock::_logOp(OperationContext* opCtx, } oplogEntries.push_back(operationName); } - class RenameCollectionTest : public ServiceContextMongoDTest { + public: static ServiceContext::UniqueOperationContext makeOpCtx(); -private: +protected: void setUp() override; void tearDown() override; -protected: explicit RenameCollectionTest(Options options = {}) : ServiceContextMongoDTest(std::move(options)) {} ServiceContext::UniqueOperationContext _opCtx; repl::ReplicationCoordinatorMock* _replCoord = nullptr; OpObserverMock* _opObserver = nullptr; - NamespaceString _sourceNss; - NamespaceString _targetNss; - NamespaceString _targetNssDifferentDb; + NamespaceString _sourceNss{NamespaceString(boost::none, "test.foo")}; + NamespaceString _targetNss{NamespaceString(boost::none, "test.bar")}; + NamespaceString _targetNssDifferentDb{NamespaceString(boost::none, "test2.bar")}; }; // static @@ -353,10 +353,6 @@ void RenameCollectionTest::setUp() { _opObserver = mockObserver.get(); opObserver->addObserver(std::move(mockObserver)); service->setOpObserver(std::move(opObserver)); - - _sourceNss = NamespaceString("test.foo"); - _targetNss = NamespaceString("test.bar"); - _targetNssDifferentDb = NamespaceString("test2.bar"); } void RenameCollectionTest::tearDown() { @@ -590,10 +586,9 @@ TEST_F(RenameCollectionTest, auto dropPendingNss = _sourceNss.makeDropPendingNamespace(dropOpTime); _createCollection(_opCtx.get(), dropPendingNss); - auto dbName = _sourceNss.db().toString(); auto cmd = BSON("renameCollection" << dropPendingNss.ns() << "to" << _targetNss.ns()); ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, - renameCollectionForApplyOps(_opCtx.get(), dbName, boost::none, cmd, {})); + renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); // Source collections stays in drop-pending state. ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss)); @@ -608,11 +603,10 @@ TEST_F( auto options = _makeCollectionOptionsWithUuid(); _createCollection(_opCtx.get(), dropPendingNss, options); - auto dbName = _sourceNss.db().toString(); - NamespaceString ignoredSourceNss(dbName, "ignored"); + NamespaceString ignoredSourceNss(_sourceNss.dbName(), "ignored"); auto cmd = BSON("renameCollection" << ignoredSourceNss.ns() << "to" << _targetNss.ns()); ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, - renameCollectionForApplyOps(_opCtx.get(), dbName, options.uuid, cmd, {})); + renameCollectionForApplyOps(_opCtx.get(), options.uuid, cmd, {})); // Source collections stays in drop-pending state. ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss)); @@ -621,30 +615,27 @@ TEST_F( } TEST_F(RenameCollectionTest, RenameCollectionToItselfByNsForApplyOps) { - auto dbName = _sourceNss.db().toString(); auto uuid = _createCollectionWithUUID(_opCtx.get(), _sourceNss); auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _sourceNss.ns() << "dropTarget" << true); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuid, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), uuid, cmd, {})); ASSERT_TRUE(_collectionExists(_opCtx.get(), _sourceNss)); } TEST_F(RenameCollectionTest, RenameCollectionToItselfByUUIDForApplyOps) { - auto dbName = _targetNss.db().toString(); auto uuid = _createCollectionWithUUID(_opCtx.get(), _targetNss); auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget" << true); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuid, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), uuid, cmd, {})); ASSERT_TRUE(_collectionExists(_opCtx.get(), _targetNss)); } TEST_F(RenameCollectionTest, RenameCollectionByUUIDRatherThanNsForApplyOps) { auto realRenameFromNss = NamespaceString("test.bar2"); - auto dbName = realRenameFromNss.db().toString(); auto uuid = _createCollectionWithUUID(_opCtx.get(), realRenameFromNss); auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget" << true); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, uuid, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), uuid, cmd, {})); ASSERT_TRUE(_collectionExists(_opCtx.get(), _targetNss)); } @@ -652,13 +643,12 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetDo const auto& collA = NamespaceString("test.A"); const auto& collB = NamespaceString("test.B"); const auto& collC = NamespaceString("test.C"); - auto dbName = collA.db().toString(); auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA); auto collCUUID = _createCollectionWithUUID(_opCtx.get(), collC); // Rename A to B, drop C, where B is not an existing collection auto cmd = BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, collAUUID, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), collAUUID, cmd, {})); // A and C should be dropped ASSERT_FALSE(_collectionExists(_opCtx.get(), collA)); ASSERT_FALSE(_collectionExists(_opCtx.get(), collC)); @@ -674,7 +664,6 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetEx const auto& collA = NamespaceString("test.A"); const auto& collB = NamespaceString("test.B"); const auto& collC = NamespaceString("test.C"); - auto dbName = collA.db().toString(); auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA); auto collBUUID = _createCollectionWithUUID(_opCtx.get(), collB); auto collCUUID = _createCollectionWithUUID(_opCtx.get(), collC); @@ -682,7 +671,7 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetEx // B should be kept but with a temporary name auto cmd = BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, collAUUID, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), collAUUID, cmd, {})); // A and C should be dropped ASSERT_FALSE(_collectionExists(_opCtx.get(), collA)); ASSERT_FALSE(_collectionExists(_opCtx.get(), collC)); @@ -708,14 +697,13 @@ TEST_F(RenameCollectionTest, _createCollection(_opCtx.get(), collB, collectionOptions); auto collBUUID = _getCollectionUuid(_opCtx.get(), collB); - auto dbName = collA.db().toString(); auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA); auto collCUUID = _createCollectionWithUUID(_opCtx.get(), collC); // Rename A to B, drop C, where B is an existing collection // B should be kept but with a temporary name auto cmd = BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, collAUUID, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), collAUUID, cmd, {})); // A and C should be dropped ASSERT_FALSE(_collectionExists(_opCtx.get(), collA)); ASSERT_FALSE(_collectionExists(_opCtx.get(), collC)); @@ -734,7 +722,6 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDTargetExistsButRealDropTargetDoesNotExist) { const auto& collA = NamespaceString("test.A"); const auto& collB = NamespaceString("test.B"); - auto dbName = collA.db().toString(); auto collAUUID = _createCollectionWithUUID(_opCtx.get(), collA); auto collBUUID = _createCollectionWithUUID(_opCtx.get(), collB); auto collCUUID = UUID::gen(); @@ -742,7 +729,7 @@ TEST_F(RenameCollectionTest, // B should be kept but with a temporary name auto cmd = BSON("renameCollection" << collA.ns() << "to" << collB.ns() << "dropTarget" << collCUUID); - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, collAUUID, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), collAUUID, cmd, {})); // A and C should be dropped ASSERT_FALSE(_collectionExists(_opCtx.get(), collA)); // B (originally A) should exist @@ -784,12 +771,10 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsRejectsRenameOpTimeIfWri ASSERT_TRUE(_opCtx->writesAreReplicated()); _createCollection(_opCtx.get(), _sourceNss); - auto dbName = _sourceNss.db().toString(); auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns()); auto renameOpTime = _opObserver->renameOpTime; - ASSERT_EQUALS( - ErrorCodes::BadValue, - renameCollectionForApplyOps(_opCtx.get(), dbName, boost::none, cmd, renameOpTime)); + ASSERT_EQUALS(ErrorCodes::BadValue, + renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, renameOpTime)); } DEATH_TEST_F(RenameCollectionTest, @@ -800,21 +785,19 @@ DEATH_TEST_F(RenameCollectionTest, _createCollection(_opCtx.get(), _sourceNss); auto dropTargetUUID = _createCollectionWithUUID(_opCtx.get(), _targetNss); - auto dbName = _sourceNss.db().toString(); auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget" << dropTargetUUID); repl::OpTime renameOpTime = {Timestamp(Seconds(200), 1U), 1LL}; - ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), dbName, boost::none, cmd, renameOpTime)); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, renameOpTime)); } TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsSourceAndTargetDoNotExist) { auto uuid = UUID::gen(); auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << _targetNss.ns() << "dropTarget" << "true"); - ASSERT_EQUALS( - ErrorCodes::NamespaceNotFound, - renameCollectionForApplyOps(_opCtx.get(), _sourceNss.db().toString(), uuid, cmd, {})); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, + renameCollectionForApplyOps(_opCtx.get(), uuid, cmd, {})); ASSERT_FALSE(_collectionExists(_opCtx.get(), _sourceNss)); ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss)); } @@ -826,8 +809,7 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetEvenIfSourceDo auto cmd = BSON("renameCollection" << missingSourceNss.ns() << "to" << _targetNss.ns() << "dropTarget" << "true"); - ASSERT_OK( - renameCollectionForApplyOps(_opCtx.get(), missingSourceNss.db().toString(), uuid, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), uuid, cmd, {})); ASSERT_FALSE(_collectionExists(_opCtx.get(), _targetNss)); } @@ -839,8 +821,7 @@ TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsDropTargetByUUIDEvenIfSo auto uuid = UUID::gen(); auto cmd = BSON("renameCollection" << missingSourceNss.ns() << "to" << _targetNss.ns() << "dropTarget" << dropTargetUUID); - ASSERT_OK( - renameCollectionForApplyOps(_opCtx.get(), missingSourceNss.db().toString(), uuid, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), uuid, cmd, {})); ASSERT_TRUE(_collectionExists(_opCtx.get(), _targetNss)); ASSERT_FALSE(_collectionExists(_opCtx.get(), dropTargetNss)); } @@ -947,8 +928,7 @@ void _testRenameCollectionAcrossDatabaseOplogEntries( if (forApplyOps) { auto cmd = BSON("renameCollection" << sourceNss.ns() << "to" << targetNss.ns() << "dropTarget" << true); - ASSERT_OK( - renameCollectionForApplyOps(opCtx, sourceNss.db().toString(), boost::none, cmd, {})); + ASSERT_OK(renameCollectionForApplyOps(opCtx, boost::none, cmd, {})); } else { RenameCollectionOptions options; options.dropTarget = true; @@ -1095,7 +1075,6 @@ TEST_F(RenameCollectionTest, FailRenameCollectionFromUnreplicatedToReplicatedDB) TEST_F(RenameCollectionTest, RenameCollectionForApplyOpsReturnsInvalidNamespaceIfTargetNamespaceIsInvalid) { _createCollection(_opCtx.get(), _sourceNss); - auto dbName = _sourceNss.db().toString(); // Create a namespace that is not in the form "database.collection". NamespaceString invalidTargetNss("invalidNamespace"); @@ -1103,7 +1082,7 @@ TEST_F(RenameCollectionTest, auto cmd = BSON("renameCollection" << _sourceNss.ns() << "to" << invalidTargetNss.ns()); ASSERT_EQUALS(ErrorCodes::InvalidNamespace, - renameCollectionForApplyOps(_opCtx.get(), dbName, boost::none, cmd, {})); + renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); } TEST_F(RenameCollectionTest, FailRenameCollectionFromSystemJavascript) { @@ -1144,5 +1123,186 @@ TEST_F(RenameCollectionTest, FailRenameCollectionToSystemJavascript) { status.reason()); } + +class RenameCollectionTestMultitenancy : public RenameCollectionTest { + +private: + void setUp() override; + +protected: + TenantId _tenantId{TenantId(OID::gen())}; + TenantId _otherTenantId{TenantId(OID::gen())}; + std::string _sourceNs{"test.foo"}; + std::string _otherNs{"test.bar"}; + + NamespaceString _sourceNssTid{NamespaceString(_tenantId, _sourceNs)}; +}; + +void RenameCollectionTestMultitenancy::setUp() { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + + RenameCollectionTest::setUp(); + + _createCollection(_opCtx.get(), _sourceNssTid); + _createIndexOnEmptyCollection(_opCtx.get(), _sourceNssTid, "a_1"); + _insertDocument(_opCtx.get(), _sourceNssTid, BSON("_id" << 0)); + + ASSERT_TRUE(_collectionExists(_opCtx.get(), _sourceNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOps) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + const NamespaceString targetNssTid(_tenantId, _otherNs); + ASSERT_NOT_EQUALS(_sourceNssTid, targetNssTid); + + // A tid field supersedes tenantIds maintained in source or target, ie. we don't take into + // account the tenantId from the _sourceNssTid or targetNssTid objects, only the _tenantId obj. + // This makes it impossible to mismatch tenantIds between source and target if the only tenantId + // being considered is common to both; if it doesn't match the source, rename will fail being + // unable to locate the source collection. If the targetNss tenantId doesn't match, well we're + // going to rename the db/collection within the _tenantId, so this effectively ensures it isn't + // possible to rename across tenantIds. + auto cmd = BSON("renameCollection" << _sourceNssTid.toString() << "to" + << targetNssTid.toString() << "tid" << _tenantId); + + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); + ASSERT_TRUE(_collectionExists(_opCtx.get(), targetNssTid)); + ASSERT_FALSE(_collectionExists(_opCtx.get(), _sourceNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsCommonRandomTid) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + const NamespaceString targetNssTid(_otherTenantId, _sourceNs); + ASSERT_NOT_EQUALS(_sourceNssTid, targetNssTid); + + // This test only has a single tenantId that belongs to neither source nor target. + auto cmd = BSON("renameCollection" << _sourceNssTid.toString() << "to" + << targetNssTid.toString() << "tid" << TenantId(OID::gen())); + + // Because the tenantId doesn't belong to the source, we should see a collection not found + // error. + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, + renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); + ASSERT_TRUE(_collectionExists(_opCtx.get(), _sourceNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsCommonTid) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + const NamespaceString targetNssTid(_otherTenantId, _otherNs); + ASSERT_NOT_EQUALS(_sourceNssTid, targetNssTid); + + // A tid field supersedes tenantIds maintained in source or target. See above. + auto cmd = BSON("renameCollection" << _sourceNssTid.toString() << "to" + << targetNssTid.toString() << "tid" << _tenantId); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); + ASSERT_TRUE( + _collectionExists(_opCtx.get(), NamespaceString(_tenantId, targetNssTid.toString()))); + ASSERT_FALSE(_collectionExists(_opCtx.get(), _sourceNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsSourceExistsOnWrongTenant) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + const NamespaceString otherSourceNssTid(_otherTenantId, _sourceNs); + const NamespaceString targetNssTid(_otherTenantId, _otherNs); + ASSERT_NOT_EQUALS(otherSourceNssTid, targetNssTid); + + // A tid field supersedes tenantIds maintained in source or target. See above. + auto cmd = BSON("renameCollection" << otherSourceNssTid.toString() << "to" + << targetNssTid.toString() << "tid" << _otherTenantId); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, + renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); + ASSERT_TRUE(_collectionExists(_opCtx.get(), _sourceNssTid)); + ASSERT_FALSE(_collectionExists(_opCtx.get(), otherSourceNssTid)); + ASSERT_FALSE(_collectionExists(_opCtx.get(), targetNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, + RenameCollectionForApplyOpsSourceExistsOnWrongTenantRequireTenantIdFalse) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + const NamespaceString otherSourceNssTid(_otherTenantId, _sourceNs); + const NamespaceString targetNssTid(_otherTenantId, _otherNs); + ASSERT_NOT_EQUALS(otherSourceNssTid, targetNssTid); + + // A tid field supersedes tenantIds maintained in source or target. See above. + auto cmd = BSON("renameCollection" << otherSourceNssTid.toStringWithTenantId() << "to" + << targetNssTid.toStringWithTenantId()); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, + renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); + ASSERT_TRUE(_collectionExists(_opCtx.get(), _sourceNssTid)); + ASSERT_FALSE(_collectionExists(_opCtx.get(), otherSourceNssTid)); + ASSERT_FALSE(_collectionExists(_opCtx.get(), targetNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsRequireTenantIdFalse) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + const NamespaceString targetNssTid(_tenantId, _otherNs); + ASSERT_NOT_EQUALS(_sourceNssTid, targetNssTid); + + auto cmd = BSON("renameCollection" << _sourceNssTid.toStringWithTenantId() << "to" + << targetNssTid.toStringWithTenantId()); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); + ASSERT_TRUE(_collectionExists(_opCtx.get(), targetNssTid)); + ASSERT_FALSE(_collectionExists(_opCtx.get(), _sourceNssTid)); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsSameNS) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + // A tid field supersedes tenantIds maintained in source or target. See above. + auto cmd = BSON("renameCollection" << _sourceNssTid.toString() << "to" + << _sourceNssTid.toString() << "tid" << _tenantId); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsSameNSRequireTenantIdFalse) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + auto cmd = BSON("renameCollection" << _sourceNssTid.toStringWithTenantId() << "to" + << _sourceNssTid.toStringWithTenantId()); + ASSERT_OK(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {})); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionForApplyOpsAcrossTenantIds) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + const NamespaceString targetNssTid(_otherTenantId, _sourceNs); + ASSERT_NOT_EQUALS(_sourceNssTid, targetNssTid); + + // This test is valid during the transition period, before featureFlagRequireTenantID is + // enforced, and will prefix the tenantIds onto the ns fields. + auto cmd = BSON("renameCollection" << _sourceNssTid.toStringWithTenantId() << "to" + << targetNssTid.toStringWithTenantId()); + ASSERT_THROWS_CODE(renameCollectionForApplyOps(_opCtx.get(), boost::none, cmd, {}), + AssertionException, + ErrorCodes::IllegalOperation); +} + +TEST_F(RenameCollectionTestMultitenancy, RenameCollectionAcrossTenantIds) { + RAIIServerParameterControllerForTest multitenancyController("multitenancySupport", true); + const NamespaceString targetNssTid(_otherTenantId, _otherNs); + ASSERT_NOT_EQUALS(_sourceNssTid, targetNssTid); + + // Renaming collections across tenantIds is not allowed + RenameCollectionOptions options; + options.dropTarget = true; + ASSERT_EQUALS(ErrorCodes::IllegalOperation, + renameCollection(_opCtx.get(), _sourceNssTid, targetNssTid, options)); +} + } // namespace } // namespace mongo diff --git a/src/mongo/db/op_observer/op_observer_impl_test.cpp b/src/mongo/db/op_observer/op_observer_impl_test.cpp index 4c7b546f925..a3bdd9c2155 100644 --- a/src/mongo/db/op_observer/op_observer_impl_test.cpp +++ b/src/mongo/db/op_observer/op_observer_impl_test.cpp @@ -689,8 +689,8 @@ TEST_F(OpObserverTest, OnRenameCollectionIncludesTenantIdFeatureFlagOff) { auto dropTargetUuid = UUID::gen(); auto stayTemp = false; auto tid{TenantId(OID::gen())}; // rename should not occur across tenants - NamespaceString sourceNss(boost::none, str::stream() << tid.toString() << "_test.foo"); - NamespaceString targetNss(boost::none, str::stream() << tid.toString() << "_test.bar"); + NamespaceString sourceNss(tid, "test.foo"); + NamespaceString targetNss(tid, "test.bar"); // Write to the oplog. { @@ -707,10 +707,8 @@ TEST_F(OpObserverTest, OnRenameCollectionIncludesTenantIdFeatureFlagOff) { // Ensure that renameCollection fields were properly added to oplog entry. ASSERT_EQUALS(uuid, unittest::assertGet(UUID::parse(oplogEntryObj["ui"]))); ASSERT_FALSE(oplogEntry.getTid()); - // TODO: SERVER-62395: Assert sourceNss and oplogEntry.getNss() are equal. - ASSERT_EQUALS(NamespaceStringUtil::deserialize( - boost::none, str::stream() << sourceNss.dbName().toString() << ".$cmd"), - oplogEntry.getNss()); + ASSERT_EQUALS(sourceNss.getCommandNS(), oplogEntry.getNss()); + auto oExpected = BSON("renameCollection" << sourceNss.toStringWithTenantId() << "to" << targetNss.toStringWithTenantId() << "stayTemp" << stayTemp << "dropTarget" << dropTargetUuid); diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index b54c6b5d172..f0107256592 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -96,6 +96,7 @@ env.Library( '$BUILD_DIR/mongo/idl/idl_parser', '$BUILD_DIR/mongo/rpc/command_status', '$BUILD_DIR/mongo/s/common_s', + '$BUILD_DIR/mongo/util/namespace_string_util', 'dbcheck', 'image_collection_entry', 'repl_coordinator_interface', @@ -552,6 +553,7 @@ env.Library( '$BUILD_DIR/mongo/db/session/kill_sessions_local', '$BUILD_DIR/mongo/db/session/session_catalog_mongod', '$BUILD_DIR/mongo/db/storage/historical_ident_tracker', + '$BUILD_DIR/mongo/util/namespace_string_util', 'drop_pending_collection_reaper', ], ) diff --git a/src/mongo/db/repl/idempotency_test.cpp b/src/mongo/db/repl/idempotency_test.cpp index 64b5a7e0ad5..ac9b01526ad 100644 --- a/src/mongo/db/repl/idempotency_test.cpp +++ b/src/mongo/db/repl/idempotency_test.cpp @@ -116,9 +116,9 @@ BSONObj RandomizedIdempotencyTest::canonicalizeDocumentForDataHash(const BSONObj return canonicalizeBSONObjForDataHash(obj); } BSONObj RandomizedIdempotencyTest::getDoc() { - AutoGetCollectionForReadCommand autoColl(_opCtx.get(), nss); + AutoGetCollectionForReadCommand autoColl(_opCtx.get(), _nss); BSONObj doc; - Helpers::findById(_opCtx.get(), nss, kDocIdQuery, doc); + Helpers::findById(_opCtx.get(), _nss, kDocIdQuery, doc); return doc.getOwned(); } diff --git a/src/mongo/db/repl/idempotency_test_fixture.cpp b/src/mongo/db/repl/idempotency_test_fixture.cpp index 2f836b798cd..43fae2f3382 100644 --- a/src/mongo/db/repl/idempotency_test_fixture.cpp +++ b/src/mongo/db/repl/idempotency_test_fixture.cpp @@ -173,6 +173,10 @@ Status IdempotencyTest::resetState() { return Status::OK(); } +void IdempotencyTest::setNss(const NamespaceString& nss) { + _nss = nss; +} + void IdempotencyTest::testOpsAreIdempotent(std::vector<OplogEntry> ops, SequenceType sequenceType) { ASSERT_OK(resetState()); @@ -215,37 +219,37 @@ void IdempotencyTest::testOpsAreIdempotent(std::vector<OplogEntry> ops, Sequence } OplogEntry IdempotencyTest::createCollection(UUID uuid) { - return makeCreateCollectionOplogEntry(nextOpTime(), nss, BSON("uuid" << uuid)); + return makeCreateCollectionOplogEntry(nextOpTime(), _nss, BSON("uuid" << uuid)); } OplogEntry IdempotencyTest::dropCollection() { - return makeCommandOplogEntry(nextOpTime(), nss, BSON("drop" << nss.coll())); + return makeCommandOplogEntry(nextOpTime(), _nss, BSON("drop" << _nss.coll())); } OplogEntry IdempotencyTest::insert(const BSONObj& obj) { - return makeInsertDocumentOplogEntry(nextOpTime(), nss, obj); + return makeInsertDocumentOplogEntry(nextOpTime(), _nss, obj); } template <class IdType> OplogEntry IdempotencyTest::update(IdType _id, const BSONObj& obj) { - return makeUpdateDocumentOplogEntry(nextOpTime(), nss, BSON("_id" << _id), obj); + return makeUpdateDocumentOplogEntry(nextOpTime(), _nss, BSON("_id" << _id), obj); } OplogEntry IdempotencyTest::buildIndex(const BSONObj& indexSpec, const BSONObj& options, const UUID& uuid) { BSONObjBuilder bob; - bob.append("createIndexes", nss.coll()); + bob.append("createIndexes", _nss.coll()); bob.append("v", 2); bob.append("key", indexSpec); bob.append("name", std::string(indexSpec.firstElementFieldName()) + "_index"); bob.appendElementsUnique(options); - return makeCommandOplogEntry(nextOpTime(), nss, bob.obj(), uuid); + return makeCommandOplogEntry(nextOpTime(), _nss, bob.obj(), uuid); } OplogEntry IdempotencyTest::dropIndex(const std::string& indexName, const UUID& uuid) { - auto cmd = BSON("dropIndexes" << nss.coll() << "index" << indexName); - return makeCommandOplogEntry(nextOpTime(), nss, cmd, uuid); + auto cmd = BSON("dropIndexes" << _nss.coll() << "index" << indexName); + return makeCommandOplogEntry(nextOpTime(), _nss, cmd, uuid); } OplogEntry IdempotencyTest::prepare(LogicalSessionId lsid, @@ -258,7 +262,7 @@ OplogEntry IdempotencyTest::prepare(LogicalSessionId lsid, info.setTxnNumber(txnNum); return makeOplogEntry(nextOpTime(), OpTypeEnum::kCommand, - nss.getCommandNS(), + _nss.getCommandNS(), BSON("applyOps" << ops << "prepare" << true), boost::none /* o2 */, info /* sessionInfo */, @@ -277,7 +281,7 @@ OplogEntry IdempotencyTest::commitUnprepared(LogicalSessionId lsid, info.setSessionId(lsid); info.setTxnNumber(txnNum); return makeCommandOplogEntryWithSessionInfoAndStmtIds( - nextOpTime(), nss, BSON("applyOps" << ops), lsid, txnNum, {stmtId}, prevOpTime); + nextOpTime(), _nss, BSON("applyOps" << ops), lsid, txnNum, {stmtId}, prevOpTime); } OplogEntry IdempotencyTest::commitPrepared(LogicalSessionId lsid, @@ -286,7 +290,7 @@ OplogEntry IdempotencyTest::commitPrepared(LogicalSessionId lsid, OpTime prepareOpTime) { return makeCommandOplogEntryWithSessionInfoAndStmtIds( nextOpTime(), - nss, + _nss, BSON("commitTransaction" << 1 << "commitTimestamp" << prepareOpTime.getTimestamp()), lsid, txnNum, @@ -299,7 +303,7 @@ OplogEntry IdempotencyTest::abortPrepared(LogicalSessionId lsid, StmtId stmtId, OpTime prepareOpTime) { return makeCommandOplogEntryWithSessionInfoAndStmtIds( - nextOpTime(), nss, BSON("abortTransaction" << 1), lsid, txnNum, {stmtId}, prepareOpTime); + nextOpTime(), _nss, BSON("abortTransaction" << 1), lsid, txnNum, {stmtId}, prepareOpTime); } OplogEntry IdempotencyTest::partialTxn(LogicalSessionId lsid, @@ -312,7 +316,7 @@ OplogEntry IdempotencyTest::partialTxn(LogicalSessionId lsid, info.setTxnNumber(txnNum); return makeOplogEntry(nextOpTime(), OpTypeEnum::kCommand, - nss.getCommandNS(), + _nss.getCommandNS(), BSON("applyOps" << ops << "partialTxn" << true), boost::none /* o2 */, info /* sessionInfo */, @@ -372,7 +376,7 @@ std::vector<CollectionState> IdempotencyTest::validateAllCollections() { CollectionState IdempotencyTest::validate(const NamespaceString& nss) { auto collUUID = [&]() -> boost::optional<UUID> { - AutoGetCollectionForReadCommand autoColl(_opCtx.get(), nss); + AutoGetCollectionForReadCommand autoColl(_opCtx.get(), _nss); if (const auto& collection = autoColl.getCollection()) { return collection->uuid(); } @@ -386,7 +390,7 @@ CollectionState IdempotencyTest::validate(const NamespaceString& nss) { } { - AutoGetCollectionForReadCommand collection(_opCtx.get(), nss); + AutoGetCollectionForReadCommand collection(_opCtx.get(), _nss); if (!collection) { // Return a mostly default initialized CollectionState struct with exists set to false @@ -401,7 +405,7 @@ CollectionState IdempotencyTest::validate(const NamespaceString& nss) { ASSERT_OK( CollectionValidation::validate(_opCtx.get(), - nss, + _nss, CollectionValidation::ValidateMode::kForegroundFull, CollectionValidation::RepairMode::kNone, &validateResults, @@ -409,7 +413,7 @@ CollectionState IdempotencyTest::validate(const NamespaceString& nss) { ASSERT_TRUE(validateResults.valid); } - AutoGetCollectionForReadCommand collection(_opCtx.get(), nss); + AutoGetCollectionForReadCommand collection(_opCtx.get(), _nss); std::string dataHash = computeDataHash(collection.getCollection()); diff --git a/src/mongo/db/repl/idempotency_test_fixture.h b/src/mongo/db/repl/idempotency_test_fixture.h index 52b0ccb2c4f..33d4f1c4ffa 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() { + IdempotencyTest() : _nss(boost::none, "test.foo") { globalFailPointRegistry() .find("doUntimestampedWritesForIdempotencyTests") ->setMode(FailPoint::alwaysOn); @@ -135,6 +135,7 @@ protected: const BSONArray& ops); virtual Status resetState(); + void setNss(const NamespaceString& nss); /** * This method returns true if running the list of operations a single time is equivalent to * running them two times. It returns false otherwise. @@ -160,10 +161,10 @@ protected: /** * Validate data and indexes. Return the MD5 hash of the documents ordered by _id. */ - CollectionState validate(const NamespaceString& nss = NamespaceString("test.foo")); + CollectionState validate(const NamespaceString& nss); std::vector<CollectionState> validateAllCollections(); - NamespaceString nss{"test.foo"}; + NamespaceString _nss; }; } // namespace repl diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 3d62207dff0..95ad2505eab 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -108,6 +108,7 @@ #include "mongo/util/elapsed_tracker.h" #include "mongo/util/fail_point.h" #include "mongo/util/file.h" +#include "mongo/util/namespace_string_util.h" #include "mongo/util/str.h" #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kReplication @@ -786,14 +787,14 @@ std::vector<OplogSlot> getNextOpTimes(OperationContext* opCtx, std::size_t count // ------------------------------------- namespace { -NamespaceString extractNs(StringData db, const BSONObj& cmdObj) { +NamespaceString extractNs(DatabaseName dbName, const BSONObj& cmdObj) { BSONElement first = cmdObj.firstElement(); uassert(40073, str::stream() << "collection name has invalid type " << typeName(first.type()), first.canonicalType() == canonicalizeBSONType(mongo::String)); StringData coll = first.valueStringData(); uassert(28635, "no collection name specified", !coll.empty()); - return NamespaceString(db, coll); + return NamespaceString(dbName, coll); } NamespaceString extractNsFromUUID(OperationContext* opCtx, const UUID& uuid) { @@ -807,7 +808,7 @@ NamespaceString extractNsFromUUIDorNs(OperationContext* opCtx, const NamespaceString& ns, const boost::optional<UUID>& ui, const BSONObj& cmd) { - return ui ? extractNsFromUUID(opCtx, ui.value()) : extractNs(ns.db(), cmd); + return ui ? extractNsFromUUID(opCtx, ui.value()) : extractNs(ns.dbName(), cmd); } using OpApplyFn = std::function<Status( @@ -835,7 +836,8 @@ const StringMap<ApplyOpMetadata> kOpsMap = { {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { const auto& ui = entry.getUuid(); const auto& cmd = entry.getObject(); - const NamespaceString nss(extractNs(entry.getNss().db(), cmd)); + + const NamespaceString nss(extractNs(entry.getNss().dbName(), cmd)); const auto& migrationId = entry.getFromTenantMigration(); if (migrationId) { @@ -854,7 +856,7 @@ const StringMap<ApplyOpMetadata> kOpsMap = { // Remove "idIndex" field from command. auto cmdWithoutIdIndex = cmd.removeField("idIndex"); return createCollectionForApplyOps(opCtx, - nss.db().toString(), + nss.dbName(), ui, cmdWithoutIdIndex, allowRenameOutOfTheWay, @@ -864,7 +866,7 @@ const StringMap<ApplyOpMetadata> kOpsMap = { // Collections clustered by _id do not need _id indexes. if (auto clusteredElem = cmd["clusteredIndex"]) { return createCollectionForApplyOps( - opCtx, nss.db().toString(), ui, cmd, allowRenameOutOfTheWay, boost::none); + opCtx, nss.dbName(), ui, cmd, allowRenameOutOfTheWay, boost::none); } // No _id index spec was provided, so we should build a v:1 _id index. @@ -873,12 +875,8 @@ const StringMap<ApplyOpMetadata> kOpsMap = { static_cast<int>(IndexVersion::kV1)); idIndexSpecBuilder.append(IndexDescriptor::kIndexNameFieldName, "_id_"); idIndexSpecBuilder.append(IndexDescriptor::kKeyPatternFieldName, BSON("_id" << 1)); - return createCollectionForApplyOps(opCtx, - nss.db().toString(), - ui, - cmd, - allowRenameOutOfTheWay, - idIndexSpecBuilder.done()); + return createCollectionForApplyOps( + opCtx, nss.dbName(), ui, cmd, allowRenameOutOfTheWay, idIndexSpecBuilder.done()); }, {ErrorCodes::NamespaceExists}}}, {"createIndexes", @@ -969,8 +967,12 @@ const StringMap<ApplyOpMetadata> kOpsMap = { {"collMod", {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { const auto& cmd = entry.getObject(); - auto opMsg = OpMsgRequest::fromDBAndBody(entry.getNss().db(), cmd); - auto collModCmd = CollMod::parse(IDLParserContext("collModOplogEntry"), opMsg); + auto opMsg = OpMsgRequestBuilder::create(entry.getNss().dbName(), cmd); + + auto collModCmd = CollMod::parse(IDLParserContext("collModOplogEntry", + false /* apiStrict */, + entry.getNss().tenantId()), + opMsg.body); const auto nssOrUUID([&collModCmd, &entry, mode]() -> NamespaceStringOrUUID { // Oplog entries from secondary oplog application will allways have the Uuid set and // it is only invocations of applyOps directly that may omit it @@ -979,7 +981,7 @@ const StringMap<ApplyOpMetadata> kOpsMap = { return collModCmd.getNamespace(); } - return {collModCmd.getDbName().toString(), *entry.getUuid()}; + return {collModCmd.getDbName(), *entry.getUuid()}; }()); return processCollModCommandForApplyOps(opCtx, nssOrUUID, collModCmd, mode); }, @@ -1051,14 +1053,15 @@ const StringMap<ApplyOpMetadata> kOpsMap = { if (!opCtx->writesAreReplicated()) { opTime = entry.getOpTime(); } - return renameCollectionForApplyOps( - opCtx, entry.getNss().db().toString(), entry.getUuid(), entry.getObject(), opTime); + return renameCollectionForApplyOps(opCtx, entry.getUuid(), entry.getObject(), opTime); }, {ErrorCodes::NamespaceNotFound, ErrorCodes::NamespaceExists}}}, {"importCollection", {[](OperationContext* opCtx, const OplogEntry& entry, OplogApplication::Mode mode) -> Status { auto importEntry = mongo::ImportCollectionOplogEntry::parse( - IDLParserContext("importCollectionOplogEntry"), entry.getObject()); + IDLParserContext( + "importCollectionOplogEntry", false /* apiStrict */, entry.getNss().tenantId()), + entry.getObject()); applyImportCollection(opCtx, importEntry.getImportUUID(), importEntry.getImportCollection(), @@ -1967,7 +1970,7 @@ Status applyCommand_inlock(OperationContext* opCtx, if ((mode == OplogApplication::Mode::kInitialSync) && (std::find(allowlistedOps.begin(), allowlistedOps.end(), o.firstElementFieldName()) == allowlistedOps.end()) && - extractNs(nss.db(), o) == NamespaceString::kServerConfigurationNamespace) { + extractNs(nss.dbName(), o) == NamespaceString::kServerConfigurationNamespace) { return Status(ErrorCodes::OplogOperationUnsupported, str::stream() << "Applying command to feature compatibility version " "collection not supported in initial sync: " diff --git a/src/mongo/db/repl/oplog_applier_impl_test.cpp b/src/mongo/db/repl/oplog_applier_impl_test.cpp index d7eca322b58..07dbe78a257 100644 --- a/src/mongo/db/repl/oplog_applier_impl_test.cpp +++ b/src/mongo/db/repl/oplog_applier_impl_test.cpp @@ -41,6 +41,7 @@ #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog/document_validation.h" +#include "mongo/db/catalog/import_collection_oplog_entry_gen.h" #include "mongo/db/change_stream_pre_images_collection_manager.h" #include "mongo/db/client.h" #include "mongo/db/concurrency/d_concurrency.h" @@ -512,7 +513,7 @@ TEST_F(OplogApplierImplTest, applyOplogEntryToRecordChangeStreamPreImages) { } } -TEST_F(OplogApplierImplTest, applyOplogEntryOrGroupedInsertsCommand) { +TEST_F(OplogApplierImplTest, CreateCollectionCommand) { NamespaceString nss("test.t"); auto op = BSON("op" @@ -537,6 +538,294 @@ TEST_F(OplogApplierImplTest, applyOplogEntryOrGroupedInsertsCommand) { ASSERT_TRUE(applyCmdCalled); } +TEST_F(OplogApplierImplTest, CreateCollectionCommandMultitenant) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + auto tid{TenantId(OID::gen())}; + NamespaceString nss(tid, "test.foo"); + + auto op = BSON("create" << nss.coll()); + bool applyCmdCalled = false; + _opObserver->onCreateCollectionFn = [&](OperationContext* opCtx, + const CollectionPtr&, + const NamespaceString& collNss, + const CollectionOptions&, + const BSONObj&) { + applyCmdCalled = true; + ASSERT_TRUE(opCtx); + ASSERT_TRUE(opCtx->lockState()->isDbLockedForMode(nss.dbName(), MODE_IX)); + ASSERT_TRUE(collNss.tenantId()); + ASSERT_EQ(tid, collNss.tenantId().get()); + ASSERT_EQUALS(nss, collNss); + return Status::OK(); + }; + + auto entry = makeCommandOplogEntry(nextOpTime(), nss, op, UUID::gen()); + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &entry, OplogApplication::Mode::kSecondary)); + ASSERT_TRUE(applyCmdCalled); +} + +TEST_F(OplogApplierImplTest, CreateCollectionCommandMultitenantRequireTenantIDFalse) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + auto tid{TenantId(OID::gen())}; + NamespaceString nss(tid, "test.foo"); + + auto op = + BSON("op" + << "c" + << "ns" << nss.getCommandNS().toStringWithTenantId() << "wall" << Date_t() << "o" + << BSON("create" << nss.coll()) << "ts" << Timestamp(1, 1) << "ui" << UUID::gen()); + + + bool applyCmdCalled = false; + _opObserver->onCreateCollectionFn = [&](OperationContext* opCtx, + const CollectionPtr&, + const NamespaceString& collNss, + const CollectionOptions&, + const BSONObj&) { + applyCmdCalled = true; + ASSERT_TRUE(opCtx); + ASSERT_TRUE(opCtx->lockState()->isDbLockedForMode(nss.dbName(), MODE_IX)); + ASSERT_TRUE(collNss.tenantId()); + ASSERT_EQ(tid, collNss.tenantId().get()); + ASSERT_EQUALS(nss, collNss); + return Status::OK(); + }; + + auto entry = OplogEntry(op); + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &entry, OplogApplication::Mode::kSecondary)); + ASSERT_TRUE(applyCmdCalled); +} + +TEST_F(OplogApplierImplTest, CreateCollectionCommandMultitenantAlreadyExists) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + auto tid1{TenantId(OID::gen())}; + auto tid2{TenantId(OID::gen())}; + std::string commonNamespace("test.foo"); + NamespaceString nssTenant1(tid1, commonNamespace); + NamespaceString nssTenant2(tid2, commonNamespace); + ASSERT_NE(tid1, tid2); + + CollectionOptions options; + options.uuid = kUuid; + + repl::createCollection(_opCtx.get(), nssTenant1, options); + auto op1 = BSON("create" << nssTenant1.coll()); + auto op2 = BSON("create" << nssTenant2.coll()); + + bool applyCmdCalled = false; + + // Target this callback to only work with nssTenant2 + _opObserver->onCreateCollectionFn = [&](OperationContext* opCtx, + const CollectionPtr&, + const NamespaceString& collNss, + const CollectionOptions&, + const BSONObj&) { + applyCmdCalled = true; + ASSERT_TRUE(opCtx); + ASSERT_TRUE(opCtx->lockState()->isDbLockedForMode(nssTenant2.dbName(), MODE_IX)); + ASSERT_TRUE(collNss.tenantId()); + ASSERT_EQ(tid2, collNss.tenantId().get()); + ASSERT_EQUALS(nssTenant2, collNss); + return Status::OK(); + }; + + ASSERT_TRUE(collectionExists(_opCtx.get(), nssTenant1)); + ASSERT_FALSE(collectionExists(_opCtx.get(), nssTenant2)); + + auto entry1 = makeCommandOplogEntry(nextOpTime(), nssTenant1, op1, kUuid); + auto entry2 = makeCommandOplogEntry(nextOpTime(), nssTenant2, op2, UUID::gen()); + + // This fails silently so we won't see any indication of a collision, but we can also assert + // that the opObserver event above won't be called in the event of a collision. + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &entry1, OplogApplication::Mode::kSecondary)); + ASSERT_FALSE(applyCmdCalled); + + ASSERT_TRUE(collectionExists(_opCtx.get(), nssTenant1)); + ASSERT_FALSE(collectionExists(_opCtx.get(), nssTenant2)); + + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &entry2, OplogApplication::Mode::kSecondary)); + ASSERT_TRUE(applyCmdCalled); + + ASSERT_TRUE(collectionExists(_opCtx.get(), nssTenant1)); + ASSERT_TRUE(collectionExists(_opCtx.get(), nssTenant2)); +} + +TEST_F(OplogApplierImplTest, RenameCollectionCommandMultitenant) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + auto tid{TenantId(OID::gen())}; // rename should not occur across tenants + const NamespaceString sourceNss(tid, "test.foo"); + const NamespaceString targetNss(tid, "test.bar"); + + auto oRename = BSON("renameCollection" << sourceNss.toString() << "to" << targetNss.toString() + << "tid" << tid); + + repl::createCollection(_opCtx.get(), sourceNss, {}); + // createCollection uses an actual opTime, so we must generate an actually opTime in the future. + auto opTime = [opCtx = _opCtx.get()] { + WriteUnitOfWork wuow{opCtx}; + ScopeGuard guard{[&wuow] { wuow.commit(); }}; + return repl::getNextOpTime(opCtx); + }(); + auto op = makeCommandOplogEntry(opTime, sourceNss, oRename, {}); + + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &op, OplogApplication::Mode::kSecondary)); + ASSERT_FALSE(collectionExists(_opCtx.get(), sourceNss)); + ASSERT_TRUE(collectionExists(_opCtx.get(), targetNss)); +} + +TEST_F(OplogApplierImplTest, RenameCollectionCommandMultitenantRequireTenantIDFalse) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + auto tid{TenantId(OID::gen())}; // rename should not occur across tenants + const NamespaceString sourceNss(tid, "test.foo"); + const NamespaceString targetNss(tid, "test.bar"); + + auto oRename = BSON("renameCollection" << sourceNss.toStringWithTenantId() << "to" + << targetNss.toStringWithTenantId()); + + repl::createCollection(_opCtx.get(), sourceNss, {}); + // createCollection uses an actual opTime, so we must generate an actually opTime in the future. + auto opTime = [opCtx = _opCtx.get()] { + WriteUnitOfWork wuow{opCtx}; + ScopeGuard guard{[&wuow] { wuow.commit(); }}; + return repl::getNextOpTime(opCtx); + }(); + auto op = makeCommandOplogEntry(opTime, sourceNss, oRename, {}); + + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &op, OplogApplication::Mode::kSecondary)); + ASSERT_FALSE(collectionExists(_opCtx.get(), sourceNss)); + ASSERT_TRUE(collectionExists(_opCtx.get(), targetNss)); +} + +TEST_F(OplogApplierImplTest, RenameCollectionCommandMultitenantAcrossTenantsRequireTenantIDFalse) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + + auto tid{TenantId(OID::gen())}; + auto wrongTid{TenantId(OID::gen())}; // rename should not occur across tenants + const NamespaceString sourceNss(tid, "test.foo"); + const NamespaceString targetNss(tid, "test.bar"); + const NamespaceString wrongTargetNss(wrongTid, targetNss.toString()); + + ASSERT_NE(sourceNss, wrongTargetNss); + + auto oRename = BSON("renameCollection" << sourceNss.toStringWithTenantId() << "to" + << wrongTargetNss.toStringWithTenantId()); + + repl::createCollection(_opCtx.get(), sourceNss, {}); + // createCollection uses an actual opTime, so we must generate an actually opTime in the future. + auto opTime = [opCtx = _opCtx.get()] { + WriteUnitOfWork wuow{opCtx}; + ScopeGuard guard{[&wuow] { wuow.commit(); }}; + return repl::getNextOpTime(opCtx); + }(); + auto op = makeCommandOplogEntry(opTime, sourceNss, oRename, {}); + + ASSERT_EQ(ErrorCodes::IllegalOperation, + _applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &op, OplogApplication::Mode::kSecondary)); + ASSERT_TRUE(collectionExists(_opCtx.get(), sourceNss)); + ASSERT_FALSE(collectionExists(_opCtx.get(), targetNss)); + ASSERT_FALSE(collectionExists(_opCtx.get(), wrongTargetNss)); +} + +TEST_F(IdempotencyTest, CollModCommandMultitenant) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + auto tid{TenantId(OID::gen())}; + const NamespaceString nss(tid, "test.foo"); + + setNss(nss); // IdempotencyTest keeps nss state, update with tid + + bool applyCmdCalled = false; + _opObserver->onCollModFn = [&](OperationContext* opCtx, + const NamespaceString& targetNss, + const UUID& uuid, + const BSONObj& collModCmd, + const CollectionOptions& oldCollOptions, + boost::optional<IndexCollModInfo> indexInfo) { + applyCmdCalled = true; + + ASSERT_TRUE(targetNss.tenantId()); + ASSERT_EQ(targetNss, nss); + }; + + ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) + ->setFollowerMode(MemberState::RS_SECONDARY)); + ASSERT_OK( + runOpInitialSync(makeCreateCollectionOplogEntry(nextOpTime(), nss, BSON("uuid" << kUuid)))); + ASSERT_OK(runOpInitialSync( + buildIndex(BSON("createdAt" << 1), BSON("expireAfterSeconds" << 3600), kUuid))); + + auto indexChange = fromjson("{keyPattern: {createdAt:1}, expireAfterSeconds:4000}}"); + auto collModCmd = BSON("collMod" << nss.coll() << "index" << indexChange); + auto op = makeCommandOplogEntry(nextOpTime(), nss, collModCmd, kUuid); + + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &op, OplogApplication::Mode::kSecondary)); + ASSERT_TRUE(applyCmdCalled); +} + +TEST_F(IdempotencyTest, CollModCommandMultitenantWrongTenant) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + auto tid1{TenantId(OID::gen())}; + auto tid2{TenantId(OID::gen())}; + std::string commonNamespace("test.foo"); + NamespaceString nssTenant1(tid1, commonNamespace); + NamespaceString nssTenant2(tid2, commonNamespace); + ASSERT_NE(tid1, tid2); + + setNss(nssTenant1); // IdempotencyTest keeps nss state, update with tid of the created nss + + bool applyCmdCalled = false; + _opObserver->onCollModFn = [&](OperationContext* opCtx, + const NamespaceString& targetNss, + const UUID& uuid, + const BSONObj& collModCmd, + const CollectionOptions& oldCollOptions, + boost::optional<IndexCollModInfo> indexInfo) { + applyCmdCalled = true; // We should not be able to reach this + }; + + ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) + ->setFollowerMode(MemberState::RS_SECONDARY)); + ASSERT_OK(runOpInitialSync( + makeCreateCollectionOplogEntry(nextOpTime(), nssTenant1, BSON("uuid" << kUuid)))); + ASSERT_OK(runOpInitialSync( + buildIndex(BSON("createdAt" << 1), BSON("expireAfterSeconds" << 3600), kUuid))); + + auto indexChange = fromjson("{keyPattern: {createdAt:1}, expireAfterSeconds:4000}}"); + auto collModCmd = BSON("collMod" << nssTenant2.coll() << "index" << indexChange); + auto op = makeCommandOplogEntry(nextOpTime(), nssTenant2, collModCmd, kUuid); + + // A NamespaceNotFound error is absorbed by the applier, but we can still determine the + // op_observer callback was never called + ASSERT_OK(_applyOplogEntryOrGroupedInsertsWrapper( + _opCtx.get(), &op, OplogApplication::Mode::kSecondary)); + ASSERT_FALSE(applyCmdCalled); +} + +// TODO SERVER-70295: Ensure the collMod gFeatureFlagRequireTenantID=false tests work + + /** * Test only subclass of OplogApplierImpl that does not apply oplog entries, but tracks ops. */ @@ -2617,16 +2906,16 @@ TEST_F(IdempotencyTest, CreateCollectionWithValidation) { auto runOpsAndValidate = [this, uuidObj]() { auto options1 = fromjson("{'validator' : {'phone' : {'$type' : 'string' } } }"); options1 = options1.addField(uuidObj.firstElement()); - auto createColl1 = makeCreateCollectionOplogEntry(nextOpTime(), nss, options1); - auto dropColl = makeCommandOplogEntry(nextOpTime(), nss, BSON("drop" << nss.coll())); + auto createColl1 = makeCreateCollectionOplogEntry(nextOpTime(), _nss, options1); + auto dropColl = makeCommandOplogEntry(nextOpTime(), _nss, BSON("drop" << _nss.coll())); auto options2 = fromjson("{'validator' : {'phone' : {'$type' : 'number' } } }"); options2 = options2.addField(uuidObj.firstElement()); - auto createColl2 = makeCreateCollectionOplogEntry(nextOpTime(), nss, options2); + auto createColl2 = makeCreateCollectionOplogEntry(nextOpTime(), _nss, options2); auto ops = {createColl1, dropColl, createColl2}; ASSERT_OK(runOpsInitialSync(ops)); - auto state = validate(); + auto state = validate(_nss); return state; }; @@ -2654,7 +2943,7 @@ TEST_F(IdempotencyTest, CreateCollectionWithCollation) { << "normalization" << false << "backwards" << false << "version" << "57.1") << "uuid" << uuid); - auto createColl = makeCreateCollectionOplogEntry(nextOpTime(), nss, options); + auto createColl = makeCreateCollectionOplogEntry(nextOpTime(), _nss, options); auto insertOp1 = insert(fromjson("{ _id: 'foo' }")); auto insertOp2 = insert(fromjson("{ _id: 'Foo', x: 1 }")); auto updateOp = update("foo", @@ -2665,7 +2954,7 @@ TEST_F(IdempotencyTest, CreateCollectionWithCollation) { // to wait until second-phase drop to completely finish. auto ops = {createColl, insertOp1, insertOp2, updateOp}; ASSERT_OK(runOpsInitialSync(ops)); - auto state = validate(); + auto state = validate(_nss); return state; }; @@ -2684,14 +2973,14 @@ TEST_F(IdempotencyTest, CreateCollectionWithView) { // Create data collection ASSERT_OK(runOpInitialSync(createCollection())); // Create "system.views" collection - auto viewNss = NamespaceString(nss.db(), "system.views"); + auto viewNss = NamespaceString(_nss.db(), "system.views"); ASSERT_OK( runOpInitialSync(makeCreateCollectionOplogEntry(nextOpTime(), viewNss, options.toBSON()))); - auto viewDoc = BSON("_id" << NamespaceString(nss.db(), "view").ns() << "viewOn" << nss.coll() + auto viewDoc = BSON("_id" << NamespaceString(_nss.db(), "view").ns() << "viewOn" << _nss.coll() << "pipeline" << fromjson("[ { '$project' : { 'x' : 1 } } ]")); auto insertViewOp = makeInsertDocumentOplogEntry(nextOpTime(), viewNss, viewDoc); - auto dropColl = makeCommandOplogEntry(nextOpTime(), nss, BSON("drop" << nss.coll())); + auto dropColl = makeCommandOplogEntry(nextOpTime(), _nss, BSON("drop" << _nss.coll())); auto ops = {insertViewOp, dropColl}; testOpsAreIdempotent(ops); @@ -2706,9 +2995,9 @@ TEST_F(IdempotencyTest, CollModNamespaceNotFound) { buildIndex(BSON("createdAt" << 1), BSON("expireAfterSeconds" << 3600), kUuid))); auto indexChange = fromjson("{keyPattern: {createdAt:1}, expireAfterSeconds:4000}}"); - auto collModCmd = BSON("collMod" << nss.coll() << "index" << indexChange); - auto collModOp = makeCommandOplogEntry(nextOpTime(), nss, collModCmd, kUuid); - auto dropCollOp = makeCommandOplogEntry(nextOpTime(), nss, BSON("drop" << nss.coll()), kUuid); + auto collModCmd = BSON("collMod" << _nss.coll() << "index" << indexChange); + auto collModOp = makeCommandOplogEntry(nextOpTime(), _nss, collModCmd, kUuid); + auto dropCollOp = makeCommandOplogEntry(nextOpTime(), _nss, BSON("drop" << _nss.coll()), kUuid); auto ops = {collModOp, dropCollOp}; testOpsAreIdempotent(ops); @@ -2723,8 +3012,8 @@ TEST_F(IdempotencyTest, CollModIndexNotFound) { buildIndex(BSON("createdAt" << 1), BSON("expireAfterSeconds" << 3600), kUuid))); auto indexChange = fromjson("{keyPattern: {createdAt:1}, expireAfterSeconds:4000}}"); - auto collModCmd = BSON("collMod" << nss.coll() << "index" << indexChange); - auto collModOp = makeCommandOplogEntry(nextOpTime(), nss, collModCmd, kUuid); + auto collModCmd = BSON("collMod" << _nss.coll() << "index" << indexChange); + auto collModOp = makeCommandOplogEntry(nextOpTime(), _nss, collModCmd, kUuid); auto dropIndexOp = dropIndex("createdAt_index", kUuid); auto ops = {collModOp, dropIndexOp}; @@ -3503,15 +3792,15 @@ TEST_F(OplogApplierImplTxnTableTest, NonMigrateNoOpEntriesShouldNotUpdateTxnTabl TEST_F(IdempotencyTest, EmptyCappedNamespaceNotFound) { // Create a BSON "emptycapped" command. - auto emptyCappedCmd = BSON("emptycapped" << nss.coll()); + auto emptyCappedCmd = BSON("emptycapped" << _nss.coll()); // Create an "emptycapped" oplog entry. - auto emptyCappedOp = makeCommandOplogEntry(nextOpTime(), nss, emptyCappedCmd); + auto emptyCappedOp = makeCommandOplogEntry(nextOpTime(), _nss, emptyCappedCmd); // Ensure that NamespaceNotFound is acceptable. ASSERT_OK(runOpInitialSync(emptyCappedOp)); - AutoGetCollectionForReadCommand autoColl(_opCtx.get(), nss); + AutoGetCollectionForReadCommand autoColl(_opCtx.get(), _nss); ASSERT_FALSE(autoColl); } @@ -3543,10 +3832,10 @@ typedef SetSteadyStateConstraints<IdempotencyTest, true> TEST_F(IdempotencyTestDisableSteadyStateConstraints, AcceptableErrorsRecordedInSteadyStateMode) { // Create a BSON "emptycapped" command. - auto emptyCappedCmd = BSON("emptycapped" << nss.coll()); + auto emptyCappedCmd = BSON("emptycapped" << _nss.coll()); // Create a "emptycapped" oplog entry. - auto emptyCappedOp = makeCommandOplogEntry(nextOpTime(), nss, emptyCappedCmd); + auto emptyCappedOp = makeCommandOplogEntry(nextOpTime(), _nss, emptyCappedCmd); // Ensure that NamespaceNotFound is "acceptable" but counted. int prevAcceptableError = replOpCounters.getAcceptableErrorInCommand()->load(); @@ -3565,10 +3854,10 @@ TEST_F(IdempotencyTestDisableSteadyStateConstraints, AcceptableErrorsRecordedInS TEST_F(IdempotencyTestEnableSteadyStateConstraints, AcceptableErrorsNotAcceptableInSteadyStateMode) { // Create a BSON "emptycapped" command. - auto emptyCappedCmd = BSON("emptycapped" << nss.coll()); + auto emptyCappedCmd = BSON("emptycapped" << _nss.coll()); // Create a "emptyCapped" oplog entry. - auto emptyCappedOp = makeCommandOplogEntry(nextOpTime(), nss, emptyCappedCmd); + auto emptyCappedOp = makeCommandOplogEntry(nextOpTime(), _nss, emptyCappedCmd); // Ensure that NamespaceNotFound is returned. ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, runOpSteadyState(emptyCappedOp)); @@ -3582,12 +3871,12 @@ const BSONObj doc2 = fromjson("{_id: 2}"); TEST_F(IdempotencyTestTxns, CommitUnpreparedTransaction) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto commitOp = commitUnprepared( - lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) ->setFollowerMode(MemberState::RS_RECOVERING)); @@ -3600,12 +3889,12 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransaction) { commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionDataPartiallyApplied) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); @@ -3615,16 +3904,16 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionDataPartiallyApplied) { auto commitOp = commitUnprepared(lsid, txnNum, StmtId(0), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc) + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc) << makeInsertApplyOpsEntry(nss2, uuid2, doc))); // Manually insert one of the documents so that the data will partially reflect the transaction // when the commitTransaction oplog entry is applied during initial sync. ASSERT_OK(getStorageInterface()->insertDocument(_opCtx.get(), - nss, + _nss, {doc, commitOp.getOpTime().getTimestamp()}, commitOp.getOpTime().getTerm())); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); ASSERT_FALSE(docExists(_opCtx.get(), nss2, doc)); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -3638,18 +3927,18 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionDataPartiallyApplied) { commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); ASSERT_TRUE(docExists(_opCtx.get(), nss2, doc)); } TEST_F(IdempotencyTestTxns, CommitPreparedTransaction) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto prepareOp = - prepare(lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + prepare(lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto commitOp = commitPrepared(lsid, txnNum, StmtId(1), prepareOp.getOpTime()); @@ -3664,12 +3953,12 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransaction) { commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, CommitPreparedTransactionDataPartiallyApplied) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); @@ -3679,7 +3968,7 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionDataPartiallyApplied) { auto prepareOp = prepare(lsid, txnNum, StmtId(0), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc) + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc) << makeInsertApplyOpsEntry(nss2, uuid2, doc))); auto commitOp = commitPrepared(lsid, txnNum, StmtId(1), prepareOp.getOpTime()); @@ -3687,10 +3976,10 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionDataPartiallyApplied) { // Manually insert one of the documents so that the data will partially reflect the transaction // when the commitTransaction oplog entry is applied during initial sync. ASSERT_OK(getStorageInterface()->insertDocument(_opCtx.get(), - nss, + _nss, {doc, commitOp.getOpTime().getTimestamp()}, commitOp.getOpTime().getTerm())); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); ASSERT_FALSE(docExists(_opCtx.get(), nss2, doc)); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -3704,18 +3993,18 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionDataPartiallyApplied) { commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); ASSERT_TRUE(docExists(_opCtx.get(), nss2, doc)); } TEST_F(IdempotencyTestTxns, AbortPreparedTransaction) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto prepareOp = - prepare(lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + prepare(lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto abortOp = abortPrepared(lsid, txnNum, StmtId(1), prepareOp.getOpTime()); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -3729,17 +4018,17 @@ TEST_F(IdempotencyTestTxns, AbortPreparedTransaction) { abortOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kAborted); - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, SinglePartialTxnOp) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) ->setFollowerMode(MemberState::RS_RECOVERING)); @@ -3755,22 +4044,22 @@ TEST_F(IdempotencyTestTxns, SinglePartialTxnOp) { DurableTxnStateEnum::kInProgress); // Document should not be visible yet. - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, MultiplePartialTxnOps) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp1 = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto partialOp2 = partialTxn(lsid, txnNum, StmtId(1), partialOp1.getOpTime(), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2))); + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2))); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) ->setFollowerMode(MemberState::RS_RECOVERING)); @@ -3784,23 +4073,23 @@ TEST_F(IdempotencyTestTxns, MultiplePartialTxnOps) { expectedStartOpTime, DurableTxnStateEnum::kInProgress); // Document should not be visible yet. - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionWithPartialTxnOps) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto commitOp = commitUnprepared(lsid, txnNum, StmtId(1), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2)), + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2)), partialOp.getOpTime()); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -3814,25 +4103,25 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionWithPartialTxnOps) { commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, CommitTwoUnpreparedTransactionsWithPartialTxnOpsAtOnce) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum1(1); TxnNumber txnNum2(2); auto partialOp1 = partialTxn( - lsid, txnNum1, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum1, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto commitOp1 = commitUnprepared(lsid, txnNum1, StmtId(1), BSONArray(), partialOp1.getOpTime()); // The second transaction (with a different transaction number) in the same session. auto partialOp2 = partialTxn( - lsid, txnNum2, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2))); + lsid, txnNum2, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2))); auto commitOp2 = commitUnprepared(lsid, txnNum2, StmtId(1), BSONArray(), partialOp2.getOpTime()); @@ -3853,24 +4142,24 @@ TEST_F(IdempotencyTestTxns, CommitTwoUnpreparedTransactionsWithPartialTxnOpsAtOn commitOp2.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, CommitAndAbortTwoTransactionsWithPartialTxnOpsAtOnce) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum1(1); TxnNumber txnNum2(2); auto partialOp1 = partialTxn( - lsid, txnNum1, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum1, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto abortOp1 = abortPrepared(lsid, txnNum1, StmtId(1), partialOp1.getOpTime()); // The second transaction (with a different transaction number) in the same session. auto partialOp2 = partialTxn( - lsid, txnNum2, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2))); + lsid, txnNum2, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2))); auto commitOp2 = commitUnprepared(lsid, txnNum2, StmtId(1), BSONArray(), partialOp2.getOpTime()); @@ -3891,23 +4180,23 @@ TEST_F(IdempotencyTestTxns, CommitAndAbortTwoTransactionsWithPartialTxnOpsAtOnce commitOp2.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionWithPartialTxnOpsAndDataPartiallyApplied) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto commitOp = commitUnprepared(lsid, txnNum, StmtId(1), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2)), + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2)), partialOp.getOpTime()); // Manually insert the first document so that the data will partially reflect the transaction @@ -3915,7 +4204,7 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionWithPartialTxnOpsAndDataP // case where the transaction committed on the sync source at a point during the initial sync, // such that we cloned 'doc' but missed 'doc2'. ASSERT_OK(getStorageInterface()->insertDocument(_opCtx.get(), - nss, + _nss, {doc, commitOp.getOpTime().getTimestamp()}, commitOp.getOpTime().getTerm())); @@ -3930,22 +4219,22 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionWithPartialTxnOpsAndDataP commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, PrepareTransactionWithPartialTxnOps) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto prepareOp = prepare(lsid, txnNum, StmtId(1), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2)), + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2)), partialOp.getOpTime()); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -3960,7 +4249,7 @@ TEST_F(IdempotencyTestTxns, PrepareTransactionWithPartialTxnOps) { partialOp.getOpTime(), DurableTxnStateEnum::kPrepared); // Document should not be visible yet. - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, EmptyPrepareTransaction) { @@ -3986,16 +4275,16 @@ TEST_F(IdempotencyTestTxns, EmptyPrepareTransaction) { TEST_F(IdempotencyTestTxns, CommitPreparedTransactionWithPartialTxnOps) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto prepareOp = prepare(lsid, txnNum, StmtId(1), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2)), + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2)), partialOp.getOpTime()); auto commitOp = commitPrepared(lsid, txnNum, StmtId(2), prepareOp.getOpTime()); @@ -4010,25 +4299,25 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionWithPartialTxnOps) { commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, CommitTwoPreparedTransactionsWithPartialTxnOpsAtOnce) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum1(1); TxnNumber txnNum2(2); auto partialOp1 = partialTxn( - lsid, txnNum1, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum1, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto prepareOp1 = prepare(lsid, txnNum1, StmtId(1), BSONArray(), partialOp1.getOpTime()); auto commitOp1 = commitPrepared(lsid, txnNum1, StmtId(2), prepareOp1.getOpTime()); // The second transaction (with a different transaction number) in the same session. auto partialOp2 = partialTxn( - lsid, txnNum2, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2))); + lsid, txnNum2, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2))); auto prepareOp2 = prepare(lsid, txnNum2, StmtId(1), BSONArray(), partialOp2.getOpTime()); auto commitOp2 = commitPrepared(lsid, txnNum2, StmtId(2), prepareOp2.getOpTime()); @@ -4049,22 +4338,22 @@ TEST_F(IdempotencyTestTxns, CommitTwoPreparedTransactionsWithPartialTxnOpsAtOnce commitOp2.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, CommitPreparedTransactionWithPartialTxnOpsAndDataPartiallyApplied) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto prepareOp = prepare(lsid, txnNum, StmtId(1), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2)), + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2)), partialOp.getOpTime()); auto commitOp = commitPrepared(lsid, txnNum, StmtId(2), prepareOp.getOpTime()); @@ -4073,7 +4362,7 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionWithPartialTxnOpsAndDataPar // case where the transaction committed on the sync source at a point during the initial sync, // such that we cloned 'doc' but missed 'doc2'. ASSERT_OK(getStorageInterface()->insertDocument(_opCtx.get(), - nss, + _nss, {doc, commitOp.getOpTime().getTimestamp()}, commitOp.getOpTime().getTerm())); @@ -4088,22 +4377,22 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionWithPartialTxnOpsAndDataPar commitOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kCommitted); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc)); - ASSERT_TRUE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_TRUE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, AbortPreparedTransactionWithPartialTxnOps) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto prepareOp = prepare(lsid, txnNum, StmtId(1), - BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc2)), + BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc2)), partialOp.getOpTime()); auto abortOp = abortPrepared(lsid, txnNum, StmtId(2), prepareOp.getOpTime()); @@ -4118,18 +4407,18 @@ TEST_F(IdempotencyTestTxns, AbortPreparedTransactionWithPartialTxnOps) { abortOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kAborted); - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc2)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc2)); } TEST_F(IdempotencyTestTxns, AbortInProgressTransaction) { createCollectionWithUuid(_opCtx.get(), NamespaceString::kSessionTransactionsTableNamespace); - auto uuid = createCollectionWithUuid(_opCtx.get(), nss); + auto uuid = createCollectionWithUuid(_opCtx.get(), _nss); auto lsid = makeLogicalSessionId(_opCtx.get()); TxnNumber txnNum(0); auto partialOp = partialTxn( - lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc))); + lsid, txnNum, StmtId(0), OpTime(), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc))); auto abortOp = abortPrepared(lsid, txnNum, StmtId(1), partialOp.getOpTime()); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -4143,7 +4432,7 @@ TEST_F(IdempotencyTestTxns, AbortInProgressTransaction) { abortOp.getWallClockTime(), boost::none, DurableTxnStateEnum::kAborted); - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionIgnoresNamespaceNotFoundErrors) { @@ -4162,7 +4451,7 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionIgnoresNamespaceNotFoundE TxnNumber txnNum(0); auto commitOp = commitUnprepared( - lsid, txnNum, StmtId(1), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc)), OpTime()); + lsid, txnNum, StmtId(1), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc)), OpTime()); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) ->setFollowerMode(MemberState::RS_RECOVERING)); @@ -4171,7 +4460,7 @@ TEST_F(IdempotencyTestTxns, CommitUnpreparedTransactionIgnoresNamespaceNotFoundE // The op should have thrown a NamespaceNotFound error, which should have been ignored, so the // operation has no effect. - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); } TEST_F(IdempotencyTestTxns, CommitPreparedTransactionIgnoresNamespaceNotFoundErrors) { @@ -4190,7 +4479,7 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionIgnoresNamespaceNotFoundErr TxnNumber txnNum(0); auto prepareOp = prepare( - lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(nss, uuid, doc)), OpTime()); + lsid, txnNum, StmtId(0), BSON_ARRAY(makeInsertApplyOpsEntry(_nss, uuid, doc)), OpTime()); auto commitOp = commitPrepared(lsid, txnNum, StmtId(1), prepareOp.getOpTime()); ASSERT_OK(ReplicationCoordinator::get(getGlobalServiceContext()) @@ -4199,7 +4488,7 @@ TEST_F(IdempotencyTestTxns, CommitPreparedTransactionIgnoresNamespaceNotFoundErr // The op should have thrown a NamespaceNotFound error, which should have been ignored, so the // operation has no effect. - ASSERT_FALSE(docExists(_opCtx.get(), nss, doc)); + ASSERT_FALSE(docExists(_opCtx.get(), _nss, doc)); } class GlobalIndexTest : public OplogApplierImplTest { diff --git a/src/mongo/db/repl/oplog_entry.cpp b/src/mongo/db/repl/oplog_entry.cpp index 830de179bb1..1bbcc9027be 100644 --- a/src/mongo/db/repl/oplog_entry.cpp +++ b/src/mongo/db/repl/oplog_entry.cpp @@ -37,6 +37,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/server_feature_flags_gen.h" #include "mongo/logv2/redaction.h" +#include "mongo/util/namespace_string_util.h" #include "mongo/util/time_support.h" #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kReplication |