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

Ban transaction snapshot reads on capped collections

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 4.7.0
    • Affects Version/s: None
    • Component/s: Replication
    • Labels:
      None
    • Minor Change
    • Hide
      /* Tests capped collection reads with readConcern level snapshot.
       *
       * @tags: [requires_majority_read_concern, requires_fcv_46]
       */
      (function() {
      "use strict";
      
      const replSet = new ReplSetTest({nodes: 1});
      
      replSet.startSet();
      replSet.initiate();
      
      const collName = "coll";
      const primary = replSet.getPrimary();
      const primaryDB = primary.getDB('test');
      
      assert.commandWorked(primaryDB.createCollection(collName, {capped: true, size: 32, max: 1}));
      
      const insertTimestamp =
          assert
              .commandWorked(primaryDB.runCommand(
                  {insert: collName, documents: [{_id: 0}], writeConcern: {w: "majority"}}))
              .operationTime;
      
      // Perform an extra insert outside of the transaction to trigger capped collection cleanup.
      assert.commandWorked(
          primaryDB.runCommand({insert: collName, documents: [{_id: 1}], writeConcern: {w: "majority"}}));
      
      // Perform a local read outside of the transaction and it should only see {_id: 1}.
      let cmdRes = assert.commandWorked(primaryDB.runCommand({find: collName}));
      assert.sameMembers([{_id: 1}], cmdRes.cursor.firstBatch, tojson(cmdRes));
      
      // TODO(SERVER-47824): Also ban transaction snapshot reads on capped collections because reading
      // from a capped collection within a transaction with an atClusterTime could give wrong result.
      session = primary.startSession({causalConsistency: false});
      sessionDB = session.getDatabase('test');
      session.startTransaction({readConcern: {level: 'snapshot', atClusterTime: insertTimestamp}});
      cmdRes = assert.commandWorked(sessionDB.runCommand({find: collName}));
      assert.sameMembers([{_id: 0}], cmdRes.cursor.firstBatch, tojson(cmdRes));;  // This would FAIL!
      
      assert.commandWorked(session.abortTransaction_forTesting());
      
      replSet.stopSet();
      })();
      
      Show
      /* Tests capped collection reads with readConcern level snapshot. * * @tags: [requires_majority_read_concern, requires_fcv_46] */ (function() { "use strict"; const replSet = new ReplSetTest({nodes: 1}); replSet.startSet(); replSet.initiate(); const collName = "coll"; const primary = replSet.getPrimary(); const primaryDB = primary.getDB('test'); assert.commandWorked(primaryDB.createCollection(collName, {capped: true, size: 32, max: 1})); const insertTimestamp = assert .commandWorked(primaryDB.runCommand( {insert: collName, documents: [{_id: 0}], writeConcern: {w: "majority"}})) .operationTime; // Perform an extra insert outside of the transaction to trigger capped collection cleanup. assert.commandWorked( primaryDB.runCommand({insert: collName, documents: [{_id: 1}], writeConcern: {w: "majority"}})); // Perform a local read outside of the transaction and it should only see {_id: 1}. let cmdRes = assert.commandWorked(primaryDB.runCommand({find: collName})); assert.sameMembers([{_id: 1}], cmdRes.cursor.firstBatch, tojson(cmdRes)); // TODO(SERVER-47824): Also ban transaction snapshot reads on capped collections because reading // from a capped collection within a transaction with an atClusterTime could give wrong result. session = primary.startSession({causalConsistency: false}); sessionDB = session.getDatabase('test'); session.startTransaction({readConcern: {level: 'snapshot', atClusterTime: insertTimestamp}}); cmdRes = assert.commandWorked(sessionDB.runCommand({find: collName})); assert.sameMembers([{_id: 0}], cmdRes.cursor.firstBatch, tojson(cmdRes));; // This would FAIL! assert.commandWorked(session.abortTransaction_forTesting()); replSet.stopSet(); })();
    • Repl 2020-06-15, Repl 2020-07-27
    • 0

      Capped collections typically (always?) delete records with an unreplicated/untimestamped write. Those deletions will always become visible in a newer transaction. So if a transaction uses snapshot with an atClusterTime (e.g. coming from mongos), it may not see some deleted documents even if they existed at the point in time requested.

      Snapshot transactions without atClusterTime might still work if the snapshot is open before the deletions happen probably because they hold onto the WT txn and the snapshot. But I am not 100% sure. Either way, it seems that we will still have to ban transaction snapshot reads on capped collections unless we choose to timestamp the capped collection deletions.

            Assignee:
            ali.mir@mongodb.com Ali Mir
            Reporter:
            lingzhi.deng@mongodb.com Lingzhi Deng
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: