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

Ensure transaction metrics are valid even if prepareTransaction fails midway

    XMLWordPrintableJSON

Details

    • Fully Compatible
    • ALL
    • Repl 2019-04-08

    Description

      Currently, if prepareTransaction fails midway and enters the abort guard, abortActiveTransaction is called. At this point, it is possible that the transaction has already transitioned into the kPrepared state, but the transactions metrics observer has not yet been called to increment the current and total number of prepared transactions. At this point, abortActiveTransaction will decrement the currentPrepared while incrementing the TotalPreparedThenAborted metrics. Therefore, it is possible to leave the metrics in a bad state if preparing a transactions fails.

      Example test that will pass (incorrectly):

      /**
       * Tests that failing while preparing a transaction leaves the server transaction metrics in an illegal state.
       *
       */
      (function() {
          "use strict";
          load("jstests/core/txns/libs/prepare_helpers.js");
       
          const dbName = "test";
          const collName = "fail_prepare_transaction";
          const testDB = db.getSiblingDB(dbName);
          const testColl = testDB.getCollection(collName);
       
          testColl.drop({writeConcern: {w: "majority"}});
          assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}}));
       
          const session = testDB.getMongo().startSession({causalConsistency: false});
          const sessionDB = session.getDatabase(dbName);
          const sessionColl = sessionDB.getCollection(collName);
       
          const doc1 = {_id: 1, x: 1};
       
          session.startTransaction({readConcern: {level: 'snapshot'}});
          assert.commandWorked(sessionColl.insert(doc1));
          // Assuming prepareTransaction was to fail and enter the abortGuard.   
          assert.commandFailed(session.getDatabase('admin').adminCommand({prepareTransaction: 1}));
          let newStatus = assert.commandWorked(testDB.adminCommand({serverStatus: 1}));
       
          // This breaks the invariant that metrics cannot be negative.
          assert.eq(newStatus.transactions["currentPrepared"], -1);
          assert.eq(newStatus.transactions["totalPreparedThenAborted"], 1);
          assert.eq(newStatus.transactions["totalPreparedThenCommitted"], 0);
          assert.eq(newStatus.transactions["totalPrepared"], 0);
          session.endSession();
      }());
      

      We should either delay transitioning to kPrepared until the abortGuard is dismissed or increment the current and total prepared metrics earlier.

      Attachments

        Activity

          People

            lingzhi.deng@mongodb.com Lingzhi Deng
            jason.chan@mongodb.com Jason Chan
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: