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

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major - P3
    • Resolution: Won't Fix
    • Affects Version/s: 4.2.1, 4.3.2
    • Fix Version/s: None
    • Component/s: Storage
    • Labels:
      None
    • Operating System:
      ALL
    • Steps To Reproduce:
      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();
    • Sprint:
      Execution Team 2019-12-30, Execution Team 2020-05-04, Execution Team 2020-05-18, Execution Team 2020-06-01
    • Linked BF Score:
      28

      Description

      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.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              henrik.edin Henrik Edin
              Reporter:
              william.schultz William Schultz (Inactive)
              Participants:
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: