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

recoverFromOplogAsStandalone doesn't work with prepared transactions

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 4.2.0-rc3, 4.3.1
    • Affects Version/s: None
    • Component/s: Replication
    • Labels:
      None
    • Fully Compatible
    • ALL
    • v4.2
    • Hide

      A modified version of standalone_replication_recovery.js

      /*
       * Tests that a standalone succeeds when passed the 'recoverFromOplogAsStandalone' parameter.
       *
       * This test only makes sense for storage engines that support recover to stable timestamp.
       * @tags: [requires_wiredtiger, requires_persistence, requires_journaling, requires_replication,
       * requires_majority_read_concern]
       */
      
      (function() {
          "use strict";
          load("jstests/replsets/rslib.js");
          load("jstests/libs/write_concern_util.js");
          load("jstests/core/txns/libs/prepare_helpers.js");
      
          const name = 'standalone_replication_recovery';
          const dbName = name;
          const collName = 'srr_coll';
          const logLevel = tojson({storage: {recovery: 3}});
      
          const rst = new ReplSetTest({
              nodes: 2,
          });
      
          function getColl(conn) {
              return conn.getDB(dbName)[collName];
          }
      
          function assertDocsInColl(node, nums) {
              let results = getColl(node).find().sort({_id: 1}).toArray();
              let expected = nums.map((i) => ({_id: i}));
              if (!friendlyEqual(results, expected)) {
                  rst.dumpOplog(node, {}, 100);
              }
              assert.eq(results, expected, "actual (left) != expected (right)");
          }
      
          jsTestLog("Initiating as a replica set.");
          // Restart as a replica set node without the flag so we can add operations to the oplog.
          let nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}});
          let node = nodes[0];
          let secondary = nodes[1];
          rst.initiate({
              _id: name,
              members: [{_id: 0, host: node.host}, {_id: 2, host: secondary.host, priority: 0}]
          });
      
          // Create the collection with w:majority and then perform a clean restart to ensure that
          // the collection is in a stable checkpoint.
          assert.commandWorked(node.getDB(dbName).runCommand({
              create: collName,
              writeConcern: {w: "majority", wtimeout: ReplSetTest.kDefaultTimeoutMS}
          }));
          assertDocsInColl(node, []);
          node = rst.restart(node, {"noReplSet": false});
          reconnect(node);
          assert.eq(rst.getPrimary(), node);
      
          const session = node.startSession();
          const sessionDb = session.getDatabase(dbName);
          const sessionColl = sessionDb.getCollection(collName + "2");
          assert.commandWorked(sessionColl.insert({_id: 1}));
          session.startTransaction({readConcern: {level: "snapshot"}, writeConcern: {w: 1, j: 1}});
          assert.commandWorked(sessionColl.insert({_id: 123457}));
          assert.commandWorked(sessionColl.insert({_id: 123458}));
          let prepareTimestamp = PrepareHelpers.prepareTransaction(session);
      
          // Keep node 0 the primary, but prevent it from committing any writes.
          stopServerReplication(secondary);
      
          assert.commandWorked(getColl(node).insert({_id: 3}, {writeConcern: {w: 1, j: 1}}));
          assert.commandWorked(getColl(node).insert({_id: 4}, {writeConcern: {w: 1, j: 1}}));
          assert.commandWorked(getColl(node).insert({_id: 5}, {writeConcern: {w: 1, j: 1}}));
          assertDocsInColl(node, [3, 4, 5]);
      
          assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp));
      
          jsTestLog("Test that on restart with the flag set we play recovery.");
          node = rst.restart(node, {
              noReplSet: true,
              setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}
          });
          reconnect(node);
          assertDocsInColl(node, [3, 4, 5]);
      
          restartServerReplication(secondary);
      
          // Skip checking db hashes since we do a write as a standalone.
          TestData.skipCheckDBHashes = true;
          rst.stopSet();
      })();
      
      Show
      A modified version of standalone_replication_recovery.js /* * Tests that a standalone succeeds when passed the 'recoverFromOplogAsStandalone' parameter. * * This test only makes sense for storage engines that support recover to stable timestamp. * @tags: [requires_wiredtiger, requires_persistence, requires_journaling, requires_replication, * requires_majority_read_concern] */ ( function () { "use strict" ; load( "jstests/replsets/rslib.js" ); load( "jstests/libs/write_concern_util.js" ); load( "jstests/core/txns/libs/prepare_helpers.js" ); const name = 'standalone_replication_recovery' ; const dbName = name; const collName = 'srr_coll' ; const logLevel = tojson({storage: {recovery: 3}}); const rst = new ReplSetTest({ nodes: 2, }); function getColl(conn) { return conn.getDB(dbName)[collName]; } function assertDocsInColl(node, nums) { let results = getColl(node).find().sort({_id: 1}).toArray(); let expected = nums.map((i) => ({_id: i})); if (!friendlyEqual(results, expected)) { rst.dumpOplog(node, {}, 100); } assert.eq(results, expected, "actual (left) != expected (right)" ); } jsTestLog( "Initiating as a replica set." ); // Restart as a replica set node without the flag so we can add operations to the oplog. let nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}}); let node = nodes[0]; let secondary = nodes[1]; rst.initiate({ _id: name, members: [{_id: 0, host: node.host}, {_id: 2, host: secondary.host, priority: 0}] }); // Create the collection with w:majority and then perform a clean restart to ensure that // the collection is in a stable checkpoint. assert.commandWorked(node.getDB(dbName).runCommand({ create: collName, writeConcern: {w: "majority" , wtimeout: ReplSetTest.kDefaultTimeoutMS} })); assertDocsInColl(node, []); node = rst.restart(node, { "noReplSet" : false }); reconnect(node); assert.eq(rst.getPrimary(), node); const session = node.startSession(); const sessionDb = session.getDatabase(dbName); const sessionColl = sessionDb.getCollection(collName + "2" ); assert.commandWorked(sessionColl.insert({_id: 1})); session.startTransaction({readConcern: {level: "snapshot" }, writeConcern: {w: 1, j: 1}}); assert.commandWorked(sessionColl.insert({_id: 123457})); assert.commandWorked(sessionColl.insert({_id: 123458})); let prepareTimestamp = PrepareHelpers.prepareTransaction(session); // Keep node 0 the primary, but prevent it from committing any writes. stopServerReplication(secondary); assert.commandWorked(getColl(node).insert({_id: 3}, {writeConcern: {w: 1, j: 1}})); assert.commandWorked(getColl(node).insert({_id: 4}, {writeConcern: {w: 1, j: 1}})); assert.commandWorked(getColl(node).insert({_id: 5}, {writeConcern: {w: 1, j: 1}})); assertDocsInColl(node, [3, 4, 5]); assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp)); jsTestLog( "Test that on restart with the flag set we play recovery." ); node = rst.restart(node, { noReplSet: true , setParameter: {recoverFromOplogAsStandalone: true , logComponentVerbosity: logLevel} }); reconnect(node); assertDocsInColl(node, [3, 4, 5]); restartServerReplication(secondary); // Skip checking db hashes since we do a write as a standalone. TestData.skipCheckDBHashes = true ; rst.stopSet(); })();
    • Repl 2019-06-03, Repl 2019-06-17, Repl 2019-07-01

      It invariants expecting a prepared transaction's inner ops to have a timestamp field. I'm not sure why this isn't a problem with transactions that commit without going through prepare.

            Assignee:
            vesselina.ratcheva@mongodb.com Vesselina Ratcheva (Inactive)
            Reporter:
            judah.schvimer@mongodb.com Judah Schvimer
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: