mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
138 lines
4.9 KiB
JavaScript
138 lines
4.9 KiB
JavaScript
/**
|
|
* Tests that using the "failNonIntentLocksIfWaitNeeded" failpoint allows us to immediately
|
|
* fail DDL operations blocked behind prepare, as we know they will not be able to acquire locks
|
|
* anyway. The DDL ops will fail here because they won't be able to get a MODE_X lock on the
|
|
* global or database resources.
|
|
*
|
|
* @tags: [uses_transactions, uses_prepare_transaction]
|
|
*/
|
|
|
|
(function() {
|
|
"use strict";
|
|
load("jstests/core/txns/libs/prepare_helpers.js");
|
|
load("jstests/libs/get_index_helpers.js");
|
|
|
|
const rst = new ReplSetTest({nodes: 1});
|
|
rst.startSet();
|
|
rst.initiate();
|
|
|
|
const dbName = "test";
|
|
const collName = "ddl_ops_after_prepare_lock_failpoint";
|
|
const indexName = "test_index";
|
|
|
|
const primary = rst.getPrimary();
|
|
const testDB = primary.getDB(dbName);
|
|
const testColl = testDB.getCollection(collName);
|
|
|
|
// Create the collection we will be working with.
|
|
assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}}));
|
|
|
|
// Also build an index (on the same collection) which we will later attempt to drop.
|
|
assert.commandWorked(
|
|
testDB.runCommand({createIndexes: collName, indexes: [{key: {"num": 1}, name: indexName}]}));
|
|
|
|
const session = primary.startSession({causalConsistency: false});
|
|
const sessionDB = session.getDatabase(dbName);
|
|
const sessionColl = sessionDB.getCollection(collName);
|
|
|
|
session.startTransaction();
|
|
assert.commandWorked(sessionColl.insert({_id: 42}));
|
|
|
|
PrepareHelpers.prepareTransaction(session);
|
|
|
|
assert.commandWorked(
|
|
primary.adminCommand({configureFailPoint: "failNonIntentLocksIfWaitNeeded", mode: "alwaysOn"}));
|
|
|
|
/**
|
|
* Tests that conflicting DDL ops fail immediately.
|
|
*/
|
|
|
|
// Collection names for DDL ops that will fail.
|
|
const collToDrop = collName;
|
|
const collToRenameFrom = collName;
|
|
const collToRenameTo = "rename_collection_to_fail";
|
|
const indexToCreate = "create_index_to_fail";
|
|
const indexToDrop = indexName;
|
|
|
|
let testDDLOps = () => {
|
|
// Also attempt to delete our original collection (it is in conflict anyway, but should
|
|
// fail to acquire the db lock in the first place).
|
|
assert.throws(function() {
|
|
testDB.getCollection(collToDrop).drop();
|
|
});
|
|
assert(testDB.getCollectionNames().includes(collToDrop));
|
|
|
|
// Same goes for trying to rename it.
|
|
assert.commandFailedWithCode(
|
|
testDB.getCollection(collToRenameFrom).renameCollection(collToRenameTo),
|
|
ErrorCodes.LockTimeout);
|
|
assert(testDB.getCollectionNames().includes(collToRenameFrom));
|
|
assert(!testDB.getCollectionNames().includes(collToRenameTo));
|
|
|
|
assert.commandFailedWithCode(testDB.adminCommand({
|
|
renameCollection: testDB.getCollection(collToRenameFrom).getFullName(),
|
|
to: testDB.getSiblingDB('test2').getCollection(collToRenameTo).getFullName(),
|
|
}),
|
|
ErrorCodes.LockTimeout);
|
|
|
|
// Attempt to add a new index to that collection.
|
|
assert.commandFailedWithCode(
|
|
testDB.runCommand(
|
|
{createIndexes: collName, indexes: [{key: {"b": 1}, name: indexToCreate}]}),
|
|
ErrorCodes.LockTimeout);
|
|
assert.eq(null, GetIndexHelpers.findByName(testColl.getIndexes(), indexToCreate));
|
|
|
|
// Try dropping the index we created originally. This should also fail.
|
|
assert.commandFailedWithCode(testDB.runCommand({dropIndexes: collName, index: indexToDrop}),
|
|
ErrorCodes.LockTimeout);
|
|
assert.neq(null, GetIndexHelpers.findByName(testColl.getIndexes(), indexToDrop));
|
|
};
|
|
|
|
/**
|
|
* Tests that CRUD operations on the same collection succeed.
|
|
*/
|
|
|
|
const docToInsert = {
|
|
num: 100
|
|
};
|
|
const docToUpdateFrom = docToInsert;
|
|
const docToUpdateTo = {
|
|
num: 101
|
|
};
|
|
const docToRemove = docToUpdateTo;
|
|
|
|
let testCRUDOps = (collConn) => {
|
|
// TODO: SERVER-40167 Having an extra document in the collection is necessary to avoid
|
|
// prepare conflicts when deleting documents.
|
|
assert.commandWorked(collConn.insert({num: 1}));
|
|
|
|
assert.commandWorked(collConn.insert(docToInsert));
|
|
assert.eq(100, collConn.findOne(docToInsert).num);
|
|
|
|
// This will not encounter a prepare conflict because there is an index on "num" that
|
|
// eliminates the need for using a collection scan.
|
|
assert.commandWorked(collConn.update(docToUpdateFrom, docToUpdateTo));
|
|
assert.eq(101, collConn.findOne(docToUpdateTo).num);
|
|
|
|
assert.commandWorked(collConn.remove(docToRemove));
|
|
assert.eq(null, collConn.findOne(docToUpdateFrom));
|
|
assert.eq(null, collConn.findOne(docToUpdateTo));
|
|
};
|
|
|
|
// First test DDL ops (should fail).
|
|
testDDLOps();
|
|
|
|
// Then test operations outside of transactions (should succeed).
|
|
testCRUDOps(testColl);
|
|
|
|
// Also test operations as part of a transaction (should succeed).
|
|
testCRUDOps(
|
|
primary.startSession({causalConsistency: false}).getDatabase(dbName).getCollection(collName));
|
|
|
|
assert.commandWorked(
|
|
primary.adminCommand({configureFailPoint: "failNonIntentLocksIfWaitNeeded", mode: "off"}));
|
|
|
|
assert.commandWorked(session.abortTransaction_forTesting());
|
|
rst.stopSet();
|
|
})();
|