summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/auth/security_token.js5
-rw-r--r--jstests/auth/token_privileges.js6
-rw-r--r--src/mongo/db/catalog/SConscript2
-rw-r--r--src/mongo/db/catalog/collection_catalog.cpp2
-rw-r--r--src/mongo/db/catalog/create_collection.cpp5
-rw-r--r--src/mongo/db/catalog/create_collection.h2
-rw-r--r--src/mongo/db/catalog/create_collection_test.cpp8
-rw-r--r--src/mongo/db/catalog/rename_collection.cpp31
-rw-r--r--src/mongo/db/catalog/rename_collection.h1
-rw-r--r--src/mongo/db/catalog/rename_collection_test.cpp252
-rw-r--r--src/mongo/db/op_observer/op_observer_impl_test.cpp10
-rw-r--r--src/mongo/db/repl/SConscript2
-rw-r--r--src/mongo/db/repl/idempotency_test.cpp4
-rw-r--r--src/mongo/db/repl/idempotency_test_fixture.cpp38
-rw-r--r--src/mongo/db/repl/idempotency_test_fixture.h7
-rw-r--r--src/mongo/db/repl/oplog.cpp41
-rw-r--r--src/mongo/db/repl/oplog_applier_impl_test.cpp493
-rw-r--r--src/mongo/db/repl/oplog_entry.cpp1
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