mirror of https://github.com/mongodb/mongo
182 lines
6.1 KiB
JavaScript
182 lines
6.1 KiB
JavaScript
/*
|
|
* Tests that a 'recoverFromOplogAsStandalone' with 'takeUnstableCheckpointOnShutdown' is
|
|
* idempotent.
|
|
*
|
|
* This test only makes sense for storage engines that support recover to stable timestamp.
|
|
* @tags: [requires_persistence, requires_replication,
|
|
* requires_majority_read_concern, uses_transactions, uses_prepare_transaction,
|
|
* # Restarting as a standalone is not supported in multiversion tests.
|
|
* multiversion_incompatible]
|
|
*/
|
|
|
|
import {PrepareHelpers} from "jstests/core/txns/libs/prepare_helpers.js";
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {restartServerReplication, stopServerReplication} from "jstests/libs/write_concern_util.js";
|
|
import {reconnect} from "jstests/replsets/rslib.js";
|
|
|
|
const name = jsTestName();
|
|
const dbName = name;
|
|
const collName1 = "srri_coll1";
|
|
const collName2 = "srri_coll2";
|
|
|
|
const logLevel = tojson({storage: {recovery: 2}});
|
|
|
|
const rst = new ReplSetTest({
|
|
nodes: 2,
|
|
});
|
|
|
|
function getColl1(conn) {
|
|
return conn.getDB(dbName)[collName1];
|
|
}
|
|
|
|
function getColl2(conn) {
|
|
return conn.getDB(dbName)[collName2];
|
|
}
|
|
|
|
function assertDocsInColl1(node, nums) {
|
|
let results = getColl1(node).find().sort({_id: 1}).toArray();
|
|
let expected = nums.map((i) => ({_id: i}));
|
|
if (!friendlyEqual(results, expected)) {
|
|
rst.dumpOplog(node, {}, 100);
|
|
}
|
|
assert.eq(results, expected, "actual (left) != expected (right)");
|
|
}
|
|
|
|
function assertPrepareConflictColl2(node, id) {
|
|
assert.sameMembers(getColl2(node).find().toArray(), [{_id: id}]);
|
|
assert.commandFailedWithCode(
|
|
node
|
|
.getDB(dbName)
|
|
.runCommand({update: collName2, updates: [{q: {_id: id}, u: {$inc: {x: 1}}}], maxTimeMS: 1000}),
|
|
ErrorCodes.MaxTimeMSExpired,
|
|
);
|
|
}
|
|
|
|
jsTestLog("Initiating as a replica set.");
|
|
let nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}});
|
|
let node = nodes[0];
|
|
let secondary = nodes[1];
|
|
rst.initiate(
|
|
{
|
|
_id: name,
|
|
members: [
|
|
{_id: 0, host: node.host},
|
|
{_id: 1, host: secondary.host, priority: 0},
|
|
],
|
|
},
|
|
null,
|
|
{initiateWithDefaultElectionTimeout: true},
|
|
);
|
|
|
|
// The default WC is majority and stopServerReplication will prevent satisfying any majority writes.
|
|
assert.commandWorked(
|
|
rst.getPrimary().adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}),
|
|
);
|
|
|
|
// Create two collections with w:majority and then perform a clean restart to ensure that
|
|
// the collections are in a stable checkpoint.
|
|
assert.commandWorked(getColl1(node).insert({_id: 3}, {writeConcern: {w: "majority"}}));
|
|
assert.commandWorked(getColl2(node).insert({_id: 1}, {writeConcern: {w: "majority"}}));
|
|
|
|
node = rst.restart(node, {"noReplSet": false});
|
|
reconnect(node);
|
|
assert.eq(rst.getPrimary(), node);
|
|
assertDocsInColl1(node, [3]);
|
|
assert.sameMembers(getColl2(node).find().toArray(), [{_id: 1}]);
|
|
|
|
// Keep node 0 the primary, but prevent it from committing any writes.
|
|
stopServerReplication(secondary);
|
|
assert.commandWorked(getColl1(node).insert({_id: 4}, {writeConcern: {w: 1, j: 1}}));
|
|
assertDocsInColl1(node, [3, 4]);
|
|
|
|
let session = node.startSession();
|
|
const sessionDB = session.getDatabase(dbName);
|
|
const sessionColl2 = sessionDB.getCollection(collName2);
|
|
session.startTransaction();
|
|
const txnNumber = session.getTxnNumber_forTesting();
|
|
assert.commandWorked(sessionColl2.update({_id: 1}, {_id: 1, a: 1}));
|
|
let prepareTimestamp = PrepareHelpers.prepareTransaction(session, {w: 1, j: 1});
|
|
jsTestLog("Prepared a transaction at " + tojson(prepareTimestamp));
|
|
assertPrepareConflictColl2(node, 1);
|
|
|
|
jsTestLog("Test that on restart with just 'recoverFromOplogAsStandalone' set we play recovery.");
|
|
node = rst.restart(node, {
|
|
noReplSet: true,
|
|
setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel},
|
|
});
|
|
reconnect(node);
|
|
assertDocsInColl1(node, [3, 4]);
|
|
assertPrepareConflictColl2(node, 1);
|
|
assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation);
|
|
|
|
jsTestLog("Test that on restart with just 'recoverFromOplogAsStandalone' we succeed" + " idempotently.");
|
|
node = rst.restart(node, {
|
|
noReplSet: true,
|
|
setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel},
|
|
});
|
|
reconnect(node);
|
|
assertDocsInColl1(node, [3, 4]);
|
|
assertPrepareConflictColl2(node, 1);
|
|
assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation);
|
|
|
|
jsTestLog("Test that on restart with both flags we succeed.");
|
|
node = rst.restart(node, {
|
|
noReplSet: true,
|
|
setParameter: {
|
|
recoverFromOplogAsStandalone: true,
|
|
takeUnstableCheckpointOnShutdown: true,
|
|
logComponentVerbosity: logLevel,
|
|
},
|
|
});
|
|
reconnect(node);
|
|
assertDocsInColl1(node, [3, 4]);
|
|
assertPrepareConflictColl2(node, 1);
|
|
assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation);
|
|
|
|
jsTestLog("Test that on restart with both flags we succeed idempotently.");
|
|
node = rst.restart(node, {
|
|
noReplSet: true,
|
|
setParameter: {
|
|
recoverFromOplogAsStandalone: true,
|
|
takeUnstableCheckpointOnShutdown: true,
|
|
logComponentVerbosity: logLevel,
|
|
},
|
|
});
|
|
reconnect(node);
|
|
assertDocsInColl1(node, [3, 4]);
|
|
assertPrepareConflictColl2(node, 1);
|
|
assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation);
|
|
|
|
jsTestLog("Restart as a replica set node so that we can commit the transaction");
|
|
node = rst.restart(node, {
|
|
noReplSet: false,
|
|
setParameter: {
|
|
recoverFromOplogAsStandalone: false,
|
|
takeUnstableCheckpointOnShutdown: false,
|
|
logComponentVerbosity: logLevel,
|
|
},
|
|
});
|
|
reconnect(node);
|
|
assert.eq(rst.getPrimary(), node);
|
|
assertDocsInColl1(node, [3, 4]);
|
|
assertPrepareConflictColl2(node, 1);
|
|
|
|
restartServerReplication(secondary);
|
|
PrepareHelpers.awaitMajorityCommitted(rst, prepareTimestamp);
|
|
|
|
assertDocsInColl1(node, [3, 4]);
|
|
assertPrepareConflictColl2(node, 1);
|
|
|
|
assert.commandWorked(
|
|
node.adminCommand({
|
|
commitTransaction: 1,
|
|
commitTimestamp: prepareTimestamp,
|
|
lsid: session.getSessionId(),
|
|
txnNumber: NumberLong(txnNumber),
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
assert.sameMembers(getColl2(node).find().toArray(), [{_id: 1, a: 1}]);
|
|
|
|
rst.stopSet();
|