diff options
author | jinichu <jinnybyun@gmail.com> | 2018-08-16 16:18:00 -0400 |
---|---|---|
committer | jinichu <jinnybyun@gmail.com> | 2018-08-16 16:18:00 -0400 |
commit | beb1cebf667ea63b3d899500e6d32c2b90aff0f2 (patch) | |
tree | 78ae2badb1f1c6dbd44ff442038d7f379ebe7dbf | |
parent | 36c1546add7887aeacb8228d289feac1b2d74c08 (diff) | |
download | mongo-beb1cebf667ea63b3d899500e6d32c2b90aff0f2.tar.gz |
SERVER-36007 Add JS test to test that checking out an already checked out session doesn't lead to a self-deadlock
(cherry picked from commit 1a32566e3173a309b109f1b53d5fe7dfe6cc704b)
-rw-r--r-- | jstests/replsets/already_checked_out_session.js | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/jstests/replsets/already_checked_out_session.js b/jstests/replsets/already_checked_out_session.js new file mode 100644 index 00000000000..af4c894c450 --- /dev/null +++ b/jstests/replsets/already_checked_out_session.js @@ -0,0 +1,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(testData, host, lsid, txnNumber) { + try { + TestData = testData; + 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) { + 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, TestData, 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, TestData, 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(); +})(); |