Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-33671

Bumping txnNumber does not clear stashed transaction resources

    • Type: Icon: Bug Bug
    • Resolution: Duplicate
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Storage
    • None
    • ALL
    • Hide
      (function() {
          const rst = new ReplSetTest({nodes: 1});
          rst.startSet();
          rst.initiate();
          const session = rst.getPrimary().getDB("test").getMongo().startSession();
          const db1 = session.getDatabase("test1");
          const db2 = session.getDatabase("test2");
      
          // Populate two collections in different databases.
          for (let i = 0; i < 4; i++) {
              assert.commandWorked(db1.coll.insert({_id: i}, {writeConcern: {w: "majority"}}));
              assert.commandWorked(db2.coll.insert({_id: i}, {writeConcern: {w: "majority"}}));
          }
      
          // Open a snapshot read cursor on db1.coll.
          let res = assert.commandWorked(db1.runCommand(
              {find: "coll", batchSize: 2, readConcern: {level: "snapshot"}, txnNumber: NumberLong(0)}));
      
          // Open a snapshot read cursor on db2.coll.
          res = assert.commandWorked(db2.runCommand(
              {find: "coll", batchSize: 2, readConcern: {level: "snapshot"}, txnNumber: NumberLong(1)}));
      
          // db2.coll is not locked, so this drop succeeds, when it should fail.
          db2.coll.drop();
          rst.stopSet();
      }());
      
      Show
      (function() { const rst = new ReplSetTest({nodes: 1}); rst.startSet(); rst.initiate(); const session = rst.getPrimary().getDB( "test" ).getMongo().startSession(); const db1 = session.getDatabase( "test1" ); const db2 = session.getDatabase( "test2" ); // Populate two collections in different databases. for (let i = 0; i < 4; i++) { assert .commandWorked(db1.coll.insert({_id: i}, {writeConcern: {w: "majority" }})); assert .commandWorked(db2.coll.insert({_id: i}, {writeConcern: {w: "majority" }})); } // Open a snapshot read cursor on db1.coll. let res = assert .commandWorked(db1.runCommand( {find: "coll" , batchSize: 2, readConcern: {level: "snapshot" }, txnNumber: NumberLong(0)})); // Open a snapshot read cursor on db2.coll. res = assert .commandWorked(db2.runCommand( {find: "coll" , batchSize: 2, readConcern: {level: "snapshot" }, txnNumber: NumberLong(1)})); // db2.coll is not locked, so this drop succeeds, when it should fail. db2.coll.drop(); rst.stopSet(); }());
    • Storage NYC 2018-03-26

      This is what happens in the repro. First we open a snapshot cursor on db1.coll. The next operation has a higher txnNumber, so we set _isSnapshotTxn to false. Then we unstash resources from the previous operation onto our OperationContext, since we have not cleared stashed resources. Then at the end of the operation, since _isSnapshotTxn is false, we do not stash resources. This allows us to drop db2.coll.

      I did not demonstrate this in the repro, but the second read is also holding the locks from the first read while it is running.

            Assignee:
            tess.avitabile@mongodb.com Tess Avitabile (Inactive)
            Reporter:
            tess.avitabile@mongodb.com Tess Avitabile (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: