summaryrefslogtreecommitdiff
path: root/jstests/replsets/already_checked_out_session.js
blob: 3a4ae11840e9911082865f59526441cacf0ed9c0 (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
78
79
80
81
82
83
84
85
86
87
88
89
/**
 * Tests that checking out an already checked out session doesn't lead to a self-deadlock. This is a
 * regression test for SERVER-36007.
 *
 * @tags: [uses_transactions]
 */
(function() {
    "use strict";

    load("jstests/libs/parallelTester.js");

    const rst = new ReplSetTest({nodes: 1});
    rst.startSet();
    rst.initiate();

    const primary = rst.getPrimary();
    const db = primary.getDB("test");

    function doInsertWithSession(host, lsid, txnNumber) {
        try {
            const conn = new Mongo(host);
            const db = conn.getDB("test");
            assert.commandWorked(db.runCommand({
                insert: "mycoll",
                documents: [{_id: txnNumber}],
                lsid: {id: eval(lsid)},
                txnNumber: NumberLong(txnNumber),
            }));
            return {ok: 1};
        } catch (e) {
            print("doInsertWithSession failed with " + e.toString());
            return {ok: 0, error: e.toString(), stack: e.stack};
        }
    }

    let thread1;
    let thread2;

    // We fsyncLock the server so that a transaction operation will block waiting for a lock.
    assert.commandWorked(db.fsyncLock());
    try {
        // JavaScript objects backed by C++ objects (e.g. BSON values) do not serialize correctly
        // when passed through the ScopedThread constructor. To work around this behavior, we
        // instead pass a stringified form of the JavaScript object through the ScopedThread
        // constructor and use eval() to rehydrate it.
        const lsid = UUID();
        thread1 = new ScopedThread(doInsertWithSession, primary.host, tojson(lsid), 1);
        thread1.start();

        assert.soon(
            () => {
                const ops = db.currentOp({
                    "command.insert": "mycoll",
                    "command.txnNumber": {$eq: 1},
                    waitingForLock: true
                });
                return ops.inprog.length === 1;
            },
            () => {
                return "insert operation with txnNumber 1 was not found: " + tojson(db.currentOp());
            });

        thread2 = new ScopedThread(doInsertWithSession, primary.host, tojson(lsid), 2);
        thread2.start();

        // Run currentOp() again to ensure that thread2 has started its insert command.
        assert.soon(
            () => {
                const ops =
                    db.currentOp({"command.insert": "mycoll", "command.txnNumber": {$eq: 2}});
                return ops.inprog.length === 1;
            },
            () => {
                return "insert operation with txnNumber 2 was not found: " + tojson(db.currentOp());
            });
    } finally {
        // We run the fsyncUnlock command in a finally block to avoid leaving the server fsyncLock'd
        // if the test were to fail.
        assert.commandWorked(db.fsyncUnlock());
    }

    thread1.join();
    thread2.join();

    assert.commandWorked(thread1.returnData());
    assert.commandWorked(thread2.returnData());

    rst.stopSet();
})();