mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-30 00:56:44 +01:00
103 lines
4.1 KiB
JavaScript
103 lines
4.1 KiB
JavaScript
/**
|
|
* Tests that prepared transactions are correctly rolled-back.
|
|
*
|
|
* @tags: [uses_transactions, uses_prepare_transaction, requires_fcv_44]
|
|
*/
|
|
(function() {
|
|
"use strict";
|
|
|
|
load("jstests/core/txns/libs/prepare_helpers.js");
|
|
load("jstests/replsets/libs/rollback_test.js");
|
|
load("jstests/replsets/libs/rollback_files.js");
|
|
load("jstests/libs/uuid_util.js");
|
|
|
|
const rollbackTest = new RollbackTest();
|
|
const rollbackNode = rollbackTest.getPrimary();
|
|
|
|
const testDB = rollbackNode.getDB("test");
|
|
const collName = "rollback_prepare_transaction";
|
|
const testColl = testDB.getCollection(collName);
|
|
|
|
// We perform some operations on the collection aside from starting and preparing a transaction
|
|
// in order to cause the count diff computed by replication to be non-zero.
|
|
assert.commandWorked(testColl.insert({_id: "a"}));
|
|
|
|
// Start two separate sessions for running transactions. On 'session1', we will run a prepared
|
|
// transaction whose commit operation gets rolled back, and on 'session2', we will run a
|
|
// prepared transaction whose prepare operation gets rolled back.
|
|
const session1 = rollbackNode.startSession();
|
|
const session1DB = session1.getDatabase(testDB.getName());
|
|
const session1Coll = session1DB.getCollection(collName);
|
|
|
|
const session2 = rollbackNode.startSession();
|
|
const session2DB = session2.getDatabase(testDB.getName());
|
|
const session2Coll = session2DB.getCollection(collName);
|
|
|
|
// Prepare a transaction whose commit operation will be rolled back.
|
|
session1.startTransaction();
|
|
assert.commandWorked(session1Coll.insert({_id: "t2_a"}));
|
|
assert.commandWorked(session1Coll.insert({_id: "t2_b"}));
|
|
assert.commandWorked(session1Coll.insert({_id: "t2_c"}));
|
|
let prepareTs = PrepareHelpers.prepareTransaction(session1);
|
|
|
|
rollbackTest.transitionToRollbackOperations();
|
|
|
|
// The following operations will be rolled-back.
|
|
assert.commandWorked(testColl.insert({_id: "b"}));
|
|
|
|
session2.startTransaction();
|
|
assert.commandWorked(session2Coll.insert({_id: "t1"}));
|
|
|
|
// Use w: 1 to simulate a prepare that will not become majority-committed.
|
|
PrepareHelpers.prepareTransaction(session2, {w: 1});
|
|
|
|
// Commit the transaction that was prepared before the common point.
|
|
PrepareHelpers.commitTransaction(session1, prepareTs);
|
|
|
|
// This is not exactly correct, but characterizes the current behavior of fastcount, which
|
|
// includes the prepared but uncommitted transaction in the collection count.
|
|
assert.eq(6, testColl.count());
|
|
|
|
// Check the visible documents.
|
|
arrayEq([{_id: "a"}, {_id: "b"}, {_id: "t2_a"}, {_id: "t2_b"}, {_id: "t2_c"}],
|
|
testColl.find().toArray());
|
|
|
|
rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
|
|
rollbackTest.transitionToSyncSourceOperationsDuringRollback();
|
|
// Skip consistency checks so they don't conflict with the prepared transaction.
|
|
rollbackTest.transitionToSteadyStateOperations({skipDataConsistencyChecks: true});
|
|
|
|
// Both the regular insert and prepared insert should be rolled-back.
|
|
assert.sameMembers([{_id: "a"}], testColl.find().toArray());
|
|
|
|
// Confirm that the rollback wrote deleted documents to a file.
|
|
const replTest = rollbackTest.getTestFixture();
|
|
const expectedDocs = [{_id: "b"}, {_id: "t2_a"}, {_id: "t2_b"}, {_id: "t2_c"}];
|
|
|
|
const uuid = getUUIDFromListCollections(testDB, collName);
|
|
checkRollbackFiles(replTest.getDbPath(rollbackNode), testColl.getFullName(), uuid, expectedDocs);
|
|
|
|
let adminDB = rollbackTest.getPrimary().getDB("admin");
|
|
|
|
// Since we rolled back the prepared transaction on session2, retrying the prepareTransaction
|
|
// command on this session should fail with a NoSuchTransaction error.
|
|
assert.commandFailedWithCode(adminDB.adminCommand({
|
|
prepareTransaction: 1,
|
|
lsid: session2.getSessionId(),
|
|
txnNumber: session2.getTxnNumber_forTesting(),
|
|
autocommit: false
|
|
}),
|
|
ErrorCodes.NoSuchTransaction);
|
|
|
|
// Allow the test to complete by aborting the left over prepared transaction.
|
|
jsTestLog("Aborting the prepared transaction on session " + tojson(session1.getSessionId()));
|
|
assert.commandWorked(adminDB.adminCommand({
|
|
abortTransaction: 1,
|
|
lsid: session1.getSessionId(),
|
|
txnNumber: session1.getTxnNumber_forTesting(),
|
|
autocommit: false
|
|
}));
|
|
|
|
rollbackTest.stop();
|
|
})();
|