Files
mongo/jstests/multiVersion/shell_retryable_writes_downgrade.js

125 lines
4.6 KiB
JavaScript

/**
* Tests that the mongo shell doesn't attempt to retry its write operations after downgrading from
* 3.6 to 3.4.
*/
(function() {
"use strict";
load("jstests/libs/retryable_writes_util.js");
if (!RetryableWritesUtil.storageEngineSupportsRetryableWrites(jsTest.options().storageEngine)) {
jsTestLog("Retryable writes are not supported, skipping test");
return;
}
load("jstests/replsets/rslib.js");
const rst = new ReplSetTest({nodes: 1});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
const db = primary.startSession({retryWrites: true}).getDatabase("test");
const coll = db.shell_retryable_writes_downgrade;
function testCommandCanBeRetried(func, {
expectedLogicalSessionId: expectedLogicalSessionId = true,
expectedTransactionNumber: expectedTransactionNumber = true
} = {}) {
const mongoRunCommandOriginal = Mongo.prototype.runCommand;
const sentinel = {};
let cmdObjSeen = sentinel;
Mongo.prototype.runCommand = function runCommandSpy(dbName, cmdObj, options) {
cmdObjSeen = cmdObj;
return mongoRunCommandOriginal.apply(this, arguments);
};
try {
assert.doesNotThrow(func);
} finally {
Mongo.prototype.runCommand = mongoRunCommandOriginal;
}
if (cmdObjSeen === sentinel) {
throw new Error("Mongo.prototype.runCommand() was never called: " + func.toString());
}
let cmdName = Object.keys(cmdObjSeen)[0];
// If the command is in a wrapped form, then we look for the actual command object inside
// the query/$query object.
if (cmdName === "query" || cmdName === "$query") {
cmdObjSeen = cmdObjSeen[cmdName];
cmdName = Object.keys(cmdObjSeen)[0];
}
if (expectedLogicalSessionId) {
assert(cmdObjSeen.hasOwnProperty("lsid"),
"Expected operation " + tojson(cmdObjSeen) + " to have a logical session id: " +
func.toString());
} else {
assert(!cmdObjSeen.hasOwnProperty("lsid"),
"Expected operation " + tojson(cmdObjSeen) +
" to not have a logical session id: " + func.toString());
}
if (expectedTransactionNumber) {
assert(cmdObjSeen.hasOwnProperty("txnNumber"),
"Expected operation " + tojson(cmdObjSeen) +
" to be assigned a transaction number since it can be retried: " +
func.toString());
} else {
assert(!cmdObjSeen.hasOwnProperty("txnNumber"),
"Expected operation " + tojson(cmdObjSeen) +
" to not be assigned a transaction number since it cannot be retried: " +
func.toString());
}
}
testCommandCanBeRetried(function() {
assert.writeOK(coll.insert({_id: "while fCV=3.6"}));
});
assert.commandWorked(db.adminCommand({setFeatureCompatibilityVersion: "3.4"}));
// The server errors on lsid and txnNumber while in featureCompatibilityVersion=3.4.
testCommandCanBeRetried(function() {
assert.writeError(coll.insert({_id: "while fCV=3.4"}));
});
rst.restart(primary, {binVersion: "3.4"});
rst.waitForMaster();
assert(db.getSession().getOptions().shouldRetryWrites(),
"Re-establishing the connection shouldn't change the state of the SessionOptions");
// After downgrading to MongoDB 3.4, the mongo shell shouldn't attempt to automatically retry
// write operations.
assert.throws(function() {
coll.insert({_id: "while in binVersion=3.4 and disconnected"});
});
// After downgrading to MongoDB 3.4, the mongo shell shouldn't inject an lsid or assign a
// transaction number to its write requests.
testCommandCanBeRetried(function() {
assert.writeOK(coll.insert({_id: "while binVersion=3.4 and reconnected"}));
}, {expectedLogicalSessionId: false, expectedTransactionNumber: false});
rst.restart(primary, {binVersion: "latest", noReplSet: true});
rst.waitForMaster();
reconnect(primary);
// When upgrading to MongoDB 3.6 but running as a stand-alone server, the mongo shell should
// still assign a transaction number to its write requests (per the Driver's specification).
testCommandCanBeRetried(function() {
assert.writeError(
coll.insert({_id: "while binVersion=3.6 as stand-alone and reconnected"}));
});
db.getSession().endSession();
rst.stopSet();
})();