mirror of https://github.com/mongodb/mongo
114 lines
4.2 KiB
JavaScript
114 lines
4.2 KiB
JavaScript
import {PrepareHelpers} from "jstests/core/txns/libs/prepare_helpers.js";
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {stopServerReplication} from "jstests/libs/write_concern_util.js";
|
|
import {reconnect} from "jstests/replsets/rslib.js";
|
|
|
|
/*
|
|
* Library used to test that we can recover prepared transactions using the
|
|
* 'recoverFromOplogAsStandalone' flag. This can be used to test with both prepared transactions
|
|
* that have been committed and ones that are still in the prepared state.
|
|
*/
|
|
|
|
export var testPrepareRecoverFromOplogAsStandalone = function (name, commitBeforeRecovery) {
|
|
const dbName = "test";
|
|
const txnCollName = "txn_coll";
|
|
const nonTxnCollName = "non_txn_coll";
|
|
const logLevel = tojson({storage: {recovery: 3}});
|
|
|
|
const rst = new ReplSetTest({
|
|
name: name,
|
|
nodes: 2,
|
|
});
|
|
|
|
function getColl(conn, collName = nonTxnCollName) {
|
|
return conn.getDB(dbName)[collName];
|
|
}
|
|
|
|
function assertDocsInColl(conn, nums, collName) {
|
|
const results = getColl(conn, collName).find().sort({_id: 1}).toArray();
|
|
const expected = nums.map((i) => ({_id: i}));
|
|
if (!friendlyEqual(results, expected)) {
|
|
rst.dumpOplog(node, {}, 100);
|
|
}
|
|
assert.eq(results, expected, "actual (left) != expected (right)");
|
|
}
|
|
|
|
jsTestLog("Initiating as a replica set.");
|
|
// Start up the nodes as a replica set so we can add operations to the oplog.
|
|
const nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}});
|
|
let node = nodes[0];
|
|
const secondary = nodes[1];
|
|
rst.initiate({
|
|
_id: name,
|
|
members: [
|
|
{_id: 0, host: node.host},
|
|
{_id: 2, host: secondary.host, priority: 0},
|
|
],
|
|
});
|
|
|
|
assert.eq(rst.getPrimary(), node);
|
|
|
|
// The default WC is majority and stopServerReplication will prevent satisfying any majority
|
|
// writes.
|
|
assert.commandWorked(
|
|
node.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}),
|
|
);
|
|
// Create both collections with {w: majority}.
|
|
assert.commandWorked(
|
|
node.getDB(dbName).runCommand({
|
|
create: nonTxnCollName,
|
|
writeConcern: {w: "majority", wtimeout: ReplSetTest.kDefaultTimeoutMS},
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
node.getDB(dbName).runCommand({
|
|
create: txnCollName,
|
|
writeConcern: {w: "majority", wtimeout: ReplSetTest.kDefaultTimeoutMS},
|
|
}),
|
|
);
|
|
|
|
jsTestLog("Beginning a transaction.");
|
|
const session = node.startSession();
|
|
const sessionDb = session.getDatabase(dbName);
|
|
const sessionColl = sessionDb.getCollection(txnCollName);
|
|
|
|
// Start the transaction and insert some documents.
|
|
session.startTransaction();
|
|
assert.commandWorked(sessionColl.insert({_id: 1234}));
|
|
assert.commandWorked(sessionColl.insert({_id: 5678}));
|
|
|
|
// Prepare the transaction.
|
|
let prepareTimestamp = PrepareHelpers.prepareTransaction(session);
|
|
|
|
// Keep node 0 the primary, but prevent it from committing any writes.
|
|
stopServerReplication(secondary);
|
|
|
|
// Issue a few non-transactional writes that will not be majority-committed.
|
|
assert.commandWorked(getColl(node).insert({_id: 3}));
|
|
assert.commandWorked(getColl(node).insert({_id: 4}));
|
|
assert.commandWorked(getColl(node).insert({_id: 5}));
|
|
assertDocsInColl(node, [3, 4, 5]);
|
|
|
|
if (commitBeforeRecovery) {
|
|
// Also commit the prepared transaction, but only on the primary.
|
|
jsTestLog("Committing the transaction (before recovery).");
|
|
assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp));
|
|
}
|
|
|
|
jsTestLog("Testing that on restart with the flag set we play recovery.");
|
|
node = rst.restart(node, {
|
|
noReplSet: true,
|
|
setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel},
|
|
});
|
|
|
|
reconnect(node);
|
|
assertDocsInColl(node, [3, 4, 5]);
|
|
|
|
// Verify that we can only see the contents of the transaction if it was committed.
|
|
const expectedDocs = commitBeforeRecovery ? [1234, 5678] : [];
|
|
assertDocsInColl(node, expectedDocs, txnCollName);
|
|
|
|
rst.stop(node);
|
|
rst.stop(secondary);
|
|
};
|