summaryrefslogtreecommitdiff
path: root/jstests/libs
diff options
context:
space:
mode:
authorMatt Broadstone <mbroadst@mongodb.com>2022-05-05 10:40:16 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-05-05 11:58:46 +0000
commit750f2cabe28900e86950695b7711c860a4165c94 (patch)
treed926e2332c6c5404066c9db79977989840c4f2e8 /jstests/libs
parent8c7cff6c0312cf874947ae8d41578428475ed01b (diff)
downloadmongo-750f2cabe28900e86950695b7711c860a4165c94.tar.gz
SERVER-65042 Run more than one shard split during passthrough tests
Diffstat (limited to 'jstests/libs')
-rw-r--r--jstests/libs/override_methods/inject_tenant_prefix.js129
1 files changed, 110 insertions, 19 deletions
diff --git a/jstests/libs/override_methods/inject_tenant_prefix.js b/jstests/libs/override_methods/inject_tenant_prefix.js
index 4cbaaf382d1..2de372e873c 100644
--- a/jstests/libs/override_methods/inject_tenant_prefix.js
+++ b/jstests/libs/override_methods/inject_tenant_prefix.js
@@ -12,18 +12,76 @@ load("jstests/libs/transactions_util.js");
// Save references to the original methods in the IIFE's scope.
// This scoping allows the original methods to be called by the overrides below.
const originalRunCommand = Mongo.prototype.runCommand;
-const originalMarkNodeAsFailed = Mongo.prototype._markNodeAsFailed;
+const originalCloseMethod = Mongo.prototype.close;
+// Save a reference to the connection created at shell startup. This will be used as a proxy for
+// multiple internal routing connections for the lifetime of the test execution.
+const initialConn = db.getMongo();
+
+/**
+ * Asserts that the provided connection is an internal routing connection, not the top-level proxy
+ * connection. The proxy connection also has an internal routing connection, so it is excluded from
+ * this check.
+ */
+function assertRoutingConnection(conn) {
+ if (conn !== initialConn) {
+ assert.eq(null,
+ conn._internalRoutingConnection,
+ "Expected connection to have no internal routing connection.");
+ }
+}
+
+/**
+ * @returns The internal routing connection for a provided connection
+ */
function getRoutingConnection(conn) {
- if (conn._conn == null) {
- conn._conn = conn;
+ if (conn === initialConn && conn._internalRoutingConnection == null) {
+ conn._internalRoutingConnection = conn;
+ }
+
+ // Since we are patching the prototype below, there must eventually be a "base case" for
+ // determining which connection to run a method on. If the provided `conn` has no internal
+ // routing connection, we assume that it _is_ the internal routing connection, and return
+ // here.
+ if (conn._internalRoutingConnection == null) {
+ return conn;
}
- return conn._conn;
+ // Sanity check ensuring we have not accidentally created an internal routing connection on an
+ // internal routing connection.
+ assertRoutingConnection(conn._internalRoutingConnection);
+ return conn._internalRoutingConnection;
}
+/**
+ * Assigns a newly establish connection as the internal routing connection for a Mongo instance.
+ *
+ * @param {Mongo} conn The original Mongo connection
+ * @param {Mongo} mongo The newly established, internal connection
+ */
function setRoutingConnection(conn, mongo) {
- conn._conn = mongo;
+ assert.neq(null,
+ conn._internalRoutingConnection,
+ "Expected connection to already have an internal routing connection.");
+ conn._internalRoutingConnection = mongo;
+}
+
+function closeRoutingConnection(conn) {
+ if (conn === initialConn) {
+ // We need to close the initial connection differently, since we patch the close method
+ // below to always proxy calls to the internal routing connection.
+ return originalCloseMethod.apply(conn);
+ }
+
+ // For all other connections we are safe to call close directly
+ conn.close();
+}
+
+/**
+ * @returns Whether we are currently running a shard split passthrough.
+ */
+function isShardSplitPassthrough() {
+ return !!TestData.tenantIds;
}
const kDenylistedDbNames = new Set(["config", "admin", "local"]);
@@ -45,8 +103,8 @@ function prependTenantIdToDbNameIfApplicable(dbName) {
}
let prefix;
- // If `TestData.tenantIds` is present, then assign a database to a randomly selected tenant
- if (TestData.tenantIds) {
+ // If running shard split passthroughs, then assign a database to a randomly selected tenant
+ if (isShardSplitPassthrough()) {
if (!kTenantPrefixMap[dbName]) {
const tenantId =
TestData.tenantIds[Math.floor(Math.random() * TestData.tenantIds.length)];
@@ -80,7 +138,7 @@ function prependTenantIdToNsIfApplicable(ns) {
* Remove a tenant prefix from the provided database name, if applicable.
*/
function extractOriginalDbName(dbName) {
- if (TestData.tenantIds) {
+ if (isShardSplitPassthrough()) {
const anyTenantPrefixOnceRegex = new RegExp(Object.values(kTenantPrefixMap).join('|'), '');
return dbName.replace(anyTenantPrefixOnceRegex, "");
}
@@ -101,7 +159,7 @@ function extractOriginalNs(ns) {
* Removes all occurrences of a tenant prefix in the provided string.
*/
function removeTenantIdFromString(string) {
- if (TestData.tenantIds) {
+ if (isShardSplitPassthrough()) {
const anyTenantPrefixGlobalRegex =
new RegExp(Object.values(kTenantPrefixMap).join('|'), 'g');
return string.replace(anyTenantPrefixGlobalRegex, "");
@@ -369,7 +427,7 @@ function removeSuccessfulOpIndexesExceptForUpserted(resObj, indexMap, ordered) {
*/
function convertServerConnectionStringToURI(input) {
const inputParts = input.split('/');
- return `mongodb://${inputParts[1]}/${inputParts[0]}`;
+ return `mongodb://${inputParts[1]}/?replicaSet=${inputParts[0]}`;
}
/**
@@ -377,9 +435,9 @@ function convertServerConnectionStringToURI(input) {
* that there is only one such operation.
*/
function getOperationStateDocument(conn) {
- const collection = TestData.tenantIds ? "tenantSplitDonors" : "tenantMigrationDonors";
+ const collection = isShardSplitPassthrough() ? "tenantSplitDonors" : "tenantMigrationDonors";
const filter =
- TestData.tenantIds ? {tenantIds: TestData.tenantIds} : {tenantId: TestData.tenantId};
+ isShardSplitPassthrough() ? {tenantIds: TestData.tenantIds} : {tenantId: TestData.tenantId};
const findRes = assert.commandWorked(
originalRunCommand.apply(conn, ["config", {find: collection, filter}, 0]));
const docs = findRes.cursor.firstBatch;
@@ -387,7 +445,7 @@ function getOperationStateDocument(conn) {
assert.eq(docs.length, 1, tojson(docs));
const result = docs[0];
- if (TestData.tenantIds) {
+ if (isShardSplitPassthrough()) {
result.recipientConnectionString =
convertServerConnectionStringToURI(result.recipientConnectionString);
}
@@ -400,7 +458,7 @@ function getOperationStateDocument(conn) {
* reroute commands by inserting a document for it into the testTenantMigration.rerouted collection.
*/
function recordRerouteDueToTenantMigration(conn, migrationStateDoc) {
- assert.neq(null, conn._conn);
+ assertRoutingConnection(conn);
while (true) {
try {
@@ -481,8 +539,9 @@ function runCommandRetryOnTenantMigrationErrors(
while (true) {
numAttempts++;
- let resObj = originalRunCommand.apply(getRoutingConnection(conn),
- [dbNameWithTenantId, cmdObjWithTenantId, options]);
+ const newConn = getRoutingConnection(conn);
+ let resObj =
+ originalRunCommand.apply(newConn, [dbNameWithTenantId, cmdObjWithTenantId, options]);
const migrationCommittedErr =
extractTenantMigrationError(resObj, ErrorCodes.TenantMigrationCommitted);
@@ -598,6 +657,10 @@ function runCommandRetryOnTenantMigrationErrors(
}
recordRerouteDueToTenantMigration(donorConnection, migrationStateDoc);
+
+ if (isShardSplitPassthrough()) {
+ closeRoutingConnection(donorConnection);
+ }
} else if (migrationAbortedErr) {
jsTestLog(`Got TenantMigrationAborted for command against database ${
dbNameWithTenantId} after trying ${numAttempts} times: ` +
@@ -654,9 +717,37 @@ Mongo.prototype.runCommand = function(dbName, cmdObj, options) {
return resObj;
};
-Mongo.prototype._markNodeAsFailed = function() {
- return originalMarkNodeAsFailed.apply(getRoutingConnection(this), arguments);
-};
+// Override all base methods on the Mongo prototype to try to proxy the call to the underlying
+// internal routing connection, if one exists.
+// NOTE: This list is derived from scripting/mozjs/mongo.cpp:62.
+['auth',
+ 'close',
+ 'compact',
+ 'cursorHandleFromId',
+ 'find',
+ 'generateDataKey',
+ 'getDataKeyCollection',
+ 'logout',
+ 'encrypt',
+ 'decrypt',
+ 'isReplicaSetConnection',
+ '_markNodeAsFailed',
+ 'getMinWireVersion',
+ 'getMaxWireVersion',
+ 'isReplicaSetMember',
+ 'isMongos',
+ 'isTLS',
+ 'getApiParameters',
+ '_startSession',
+ // Don't override this method, since it is never called directly in jstests. The expectation of is
+ // that it will be run on the connection `Mongo.prototype.runCommand` chose.
+ // '_runCommandImpl',
+].forEach(methodName => {
+ const $method = Mongo.prototype[methodName];
+ Mongo.prototype[methodName] = function() {
+ return $method.apply(getRoutingConnection(this), arguments);
+ };
+});
OverrideHelpers.prependOverrideInParallelShell(
"jstests/libs/override_methods/inject_tenant_prefix.js");