summaryrefslogtreecommitdiff
path: root/jstests/core/txns/kill_sessions_kills_transaction.js
blob: bd03a1246243abea3e2fbe45a5e024e7ff20f7a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// Tests that killSessions kills inactive transactions.
// @tags: [uses_transactions]
(function() {
"use strict";

const dbName = "test";
const collName = "kill_sessions_kills_transaction";
const testDB = db.getSiblingDB(dbName);
const adminDB = db.getSiblingDB("admin");
const testColl = testDB[collName];
const sessionOptions = {
    causalConsistency: false
};

testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});

const bulk = testColl.initializeUnorderedBulkOp();
for (let i = 0; i < 4; ++i) {
    bulk.insert({_id: i});
}
assert.commandWorked(bulk.execute({w: "majority"}));

jsTest.log("Test that killing a session kills an inactive transaction.");
let session = db.getMongo().startSession(sessionOptions);
let sessionDb = session.getDatabase(dbName);
let sessionColl = sessionDb[collName];

session.startTransaction();
assert.commandWorked(sessionColl.insert({_id: 5}));
assert.commandWorked(testDB.runCommand({killSessions: [session.getSessionId()]}));
assert.commandFailedWithCode(session.commitTransaction_forTesting(), ErrorCodes.NoSuchTransaction);

session.endSession();

jsTest.log("killSessions must not block on locks held by a transaction it plans to kill.");
session = db.getMongo().startSession(sessionOptions);
sessionDb = session.getDatabase(dbName);
sessionColl = sessionDb[collName];

session.startTransaction();
// Open a cursor on the collection.
assert.commandWorked(sessionDb.runCommand({find: collName, batchSize: 2}));

// Start a drop, which will hang.
let awaitDrop = startParallelShell(function() {
    db.getSiblingDB("test")["kill_sessions_kills_transaction"].drop(
        {writeConcern: {w: "majority"}});
});

// Wait for the drop to have a pending MODE_X lock on the database.
assert.soon(
    function() {
        return adminDB
                   .aggregate([
                       {$currentOp: {}},
                       {$match: {"command.drop": collName, waitingForLock: true}}
                   ])
                   .itcount() === 1;
    },
    function() {
        return "Failed to find drop in currentOp output: " +
            tojson(adminDB.aggregate([{$currentOp: {}}]).toArray());
    });

// killSessions needs to acquire a MODE_IS lock on the collection in order to kill the open
// cursor. However, the transaction is holding a MODE_IX lock on the collection, which will
// block the drop from obtaining a MODE_X lock on the database, which will block the
// killSessions from taking a MODE_IS lock on the collection. In order to avoid hanging,
// killSessions must first kill the transaction, so that it releases its MODE_IX collection
// lock. This allows the drop to proceed and obtain and release the MODE_X lock. Finally,
// killSessions can obtain a MODE_IS collection lock and kill the cursor.
assert.commandWorked(testDB.runCommand({killSessions: [session.getSessionId()]}));
awaitDrop();
assert.commandFailedWithCode(session.commitTransaction_forTesting(), ErrorCodes.NoSuchTransaction);

session.endSession();
}());