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

Creating collection with specific UUID using applyOps can roll back a pending collection drop

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major - P3
    • Resolution: Duplicate
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Replication
    • Labels:
    • Operating System:
      ALL
    • Steps To Reproduce:
      Hide

       
      load("jstests/libs/check_log.js");  // For 'checkLog'.
       
      // Pause oplog application on a specified node.
      function pauseOplogApplication(node) {
          assert.commandWorked(
              node.adminCommand({configureFailPoint: "rsSyncApplyStop", mode: "alwaysOn"}));
          checkLog.contains(node, "rsSyncApplyStop fail point enabled");
      }
       
      // Resume oplog application on a specified node.
      function resumeOplogApplication(node) {
          assert.commandWorked(node.adminCommand({configureFailPoint: "rsSyncApplyStop", mode: "off"}));
      }
       
      var replTest = new ReplSetTest({name: "applyOpsUUID", nodes: 2});
       
      replTest.startSet();
      replTest.initiate();
      replTest.awaitReplication();
       
      // Pause application on secondary so collection drop doesn't commit.
      pauseOplogApplication(replTest.getSecondary());
       
      // Get connections and collection.
      let primary = replTest.getPrimary();
      let pdb = primary.getDB("test");
      let collName = "coll";
       
      // Create collection.
      assert.commandWorked(pdb.createCollection(collName));
       
      // Get the UUID for 'coll'.
      let uuid = pdb.getCollectionInfos()[0].info.uuid;
       
      jsTestLog("Dropping collection.");
      assert.commandWorked(pdb.runCommand({drop: collName}));
      assert.commandWorked(pdb.createCollection("otherColl"));
       
      // Create collection with UUID via applyOps.
      let ops = [{
          "op": "c",
          "ns": "test.$cmd",
          "ui": uuid,
          "o": {
              "create": collName,
              "idIndex": {"v": 2, "key": {"_id": 1}, "name": "_id_", "ns": "test.coll"}
          }
      }];
      jsTestLog("Doing 'applyOps' command.");
      assert.commandWorked(pdb.adminCommand({applyOps: ops}));
       
      // Dump oplog of primary.
      jsTestLog("Primary oplog.");
      let oplog = primary.getDB("local")["oplog.rs"].find().toArray();
      jsTestLog(tojson(oplog));
       
      jsTestLog("Resuming oplog application.");
      resumeOplogApplication(replTest.getSecondary());
       
      // Check data consistency.
      replTest.awaitReplication();
      replTest.checkReplicatedDataHashes();
      

      Show
        load( "jstests/libs/check_log.js" ); // For 'checkLog'.   // Pause oplog application on a specified node. function pauseOplogApplication(node) { assert.commandWorked( node.adminCommand({configureFailPoint: "rsSyncApplyStop" , mode: "alwaysOn" })); checkLog.contains(node, "rsSyncApplyStop fail point enabled" ); }   // Resume oplog application on a specified node. function resumeOplogApplication(node) { assert.commandWorked(node.adminCommand({configureFailPoint: "rsSyncApplyStop" , mode: "off" })); }   var replTest = new ReplSetTest({name: "applyOpsUUID" , nodes: 2});   replTest.startSet(); replTest.initiate(); replTest.awaitReplication();   // Pause application on secondary so collection drop doesn't commit. pauseOplogApplication(replTest.getSecondary());   // Get connections and collection. let primary = replTest.getPrimary(); let pdb = primary.getDB( "test" ); let collName = "coll" ;   // Create collection. assert.commandWorked(pdb.createCollection(collName));   // Get the UUID for 'coll'. let uuid = pdb.getCollectionInfos()[0].info.uuid;   jsTestLog( "Dropping collection." ); assert.commandWorked(pdb.runCommand({drop: collName})); assert.commandWorked(pdb.createCollection( "otherColl" ));   // Create collection with UUID via applyOps. let ops = [{ "op" : "c" , "ns" : "test.$cmd" , "ui" : uuid, "o" : { "create" : collName, "idIndex" : { "v" : 2, "key" : { "_id" : 1}, "name" : "_id_" , "ns" : "test.coll" } } }]; jsTestLog( "Doing 'applyOps' command." ); assert.commandWorked(pdb.adminCommand({applyOps: ops}));   // Dump oplog of primary. jsTestLog( "Primary oplog." ); let oplog = primary.getDB( "local" )[ "oplog.rs" ].find().toArray(); jsTestLog(tojson(oplog));   jsTestLog( "Resuming oplog application." ); resumeOplogApplication(replTest.getSecondary());   // Check data consistency. replTest.awaitReplication(); replTest.checkReplicatedDataHashes();
    • Linked BF Score:
      0

      Description

      If a collection on a primary is in "drop-pending" state, by executing a create collection command via the applyOps command using the original UUID of that collection it is possible to erroneously "roll back" that collection i.e. remove it from the "drop-pending" state, and make it a normal collection again. Presumably, the correct thing to do would be to disallow collection creation operations via applyOps on collection UUIDs that are currently in drop-pending state.

      This issue can lead to a db hash mismatch between a primary and secondary, since the drop-pending "roll back" gets logged in the oplog as a renameCollection operation. When a secondary applies the initial collection drop (the drop prepare), it might physically drop the collection before it applies the later renameCollection operation from the primary. If it tries to then apply the renameCollection operation on a collection that has been physically dropped, it will presumably fail silently, leaving it permanently missing a collection that the primary still has. See repro script.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              backlog-server-repl Backlog - Replication Team
              Reporter:
              william.schultz William Schultz (Inactive)
              Participants:
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: