summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjinichu <jinnybyun@gmail.com>2018-08-16 16:18:00 -0400
committerjinichu <jinnybyun@gmail.com>2018-08-16 16:18:00 -0400
commitbeb1cebf667ea63b3d899500e6d32c2b90aff0f2 (patch)
tree78ae2badb1f1c6dbd44ff442038d7f379ebe7dbf
parent36c1546add7887aeacb8228d289feac1b2d74c08 (diff)
downloadmongo-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.js89
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();
+})();