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

Collection validation can cause inaccurate fast count when run against a node with a prepared transaction

    • Type: Icon: Bug Bug
    • Resolution: Won't Fix
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 4.2.1, 4.3.2
    • Component/s: Storage
    • None
    • ALL
    • Hide
      load("jstests/core/txns/libs/prepare_helpers.js");
      
      const name = "prepare_counts";
      const rst = new ReplSetTest({nodes: 1});
      const nodes = rst.startSet();
      rst.initiate();
      const dbName = "test";
      const collName = name;
      
      const primary = rst.getPrimary();
      const secondary = rst.getSecondary();
      const testDB = primary.getDB(dbName);
      
      assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}}));
      
      const session = primary.startSession({causalConsistency: false});
      const sessionDB = session.getDatabase(dbName);
      const sessionColl = sessionDB.getCollection(collName);
      
      jsTestLog("Inserting 9 documents");
      for(var i=0;i<9;i++){
          assert.commandWorked(primary.getDB(dbName).getCollection(collName).insert({x:i}));
      }
      
      jsTestLog("Starting transaction.");
      session.startTransaction();
      var txnNum = session.getTxnNumber_forTesting();
      assert.commandWorked(sessionColl.insert({_id:1}));
      assert.commandWorked(sessionColl.insert({_id:2}));
      jsTestLog("Preparing transaction.");
      PrepareHelpers.prepareTransaction(session);
      
      jsTestLog("Validating collection.");
      assert.commandWorked(primary.adminCommand({replSetStepDown: 5, force: true}));
      assert.commandWorked(testDB.runCommand({validate: collName, full: true}));
      
      jsTestLog("Done validating collections");
      var session1 = PrepareHelpers.createSessionWithGivenId(rst.getPrimary(), session.getSessionId());
      var session1DB = session1.getDatabase(dbName);
      
      jsTestLog("Aborting transaction.");
      assert.commandWorked(session1DB.adminCommand(
          {abortTransaction: 1, txnNumber: txnNum, stmtid: NumberInt(3), autocommit: false}));
      
      rst.checkCollectionCounts();
      rst.stopSet();
      
      Show
      load( "jstests/core/txns/libs/prepare_helpers.js" ); const name = "prepare_counts" ; const rst = new ReplSetTest({nodes: 1}); const nodes = rst.startSet(); rst.initiate(); const dbName = "test" ; const collName = name; const primary = rst.getPrimary(); const secondary = rst.getSecondary(); const testDB = primary.getDB(dbName); assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority" }})); const session = primary.startSession({causalConsistency: false }); const sessionDB = session.getDatabase(dbName); const sessionColl = sessionDB.getCollection(collName); jsTestLog( "Inserting 9 documents" ); for ( var i=0;i<9;i++){ assert.commandWorked(primary.getDB(dbName).getCollection(collName).insert({x:i})); } jsTestLog( "Starting transaction." ); session.startTransaction(); var txnNum = session.getTxnNumber_forTesting(); assert.commandWorked(sessionColl.insert({_id:1})); assert.commandWorked(sessionColl.insert({_id:2})); jsTestLog( "Preparing transaction." ); PrepareHelpers.prepareTransaction(session); jsTestLog( "Validating collection." ); assert.commandWorked(primary.adminCommand({replSetStepDown: 5, force: true })); assert.commandWorked(testDB.runCommand({validate: collName, full: true })); jsTestLog( "Done validating collections" ); var session1 = PrepareHelpers.createSessionWithGivenId(rst.getPrimary(), session.getSessionId()); var session1DB = session1.getDatabase(dbName); jsTestLog( "Aborting transaction." ); assert.commandWorked(session1DB.adminCommand( {abortTransaction: 1, txnNumber: txnNum, stmtid: NumberInt(3), autocommit: false })); rst.checkCollectionCounts(); rst.stopSet();
    • Execution Team 2019-12-30, Execution Team 2020-05-04, Execution Team 2020-05-18, Execution Team 2020-06-01
    • 0

      When we run collection validation we will update the "fast count" value for a collection, based on how many documents we observe when scanning the collection. If we run validation against a node that currently has an open, prepared transaction that has done operations against the collection we are validating, we may incorrectly update the fast count value since validation cannot observe the effects of the uncommitted transaction. For example, if we have done 10 inserts against collection A, a prepared transaction has done 10 inserts against collection A, and then we run validate against A, we will update the fast count to 10, since we do not see the effects of the transaction yet. If the transaction later aborts, though, we will decrement the fast count by 10, leaving us with a fast count of 0 indefinitely, even though there are 10 documents in the collection.

            Assignee:
            henrik.edin@mongodb.com Henrik Edin
            Reporter:
            william.schultz@mongodb.com Will Schultz
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: