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

Rollback doesn’t properly cancel out index drop/create operations when there are multiple indexes on the same collection

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 3.6.0-rc2
    • Affects Version/s: 3.5.12
    • Component/s: Replication
    • Fully Compatible
    • ALL
    • Hide
      load('jstests/replsets/libs/rollback_test.js');
      
      // Set up the cluster.
      const rollbackTest = new RollbackTest('repro');
      let testDb = rollbackTest.getPrimary().getDB('test');
      
      // Ensure collections are created.
      testDb.coll.insert({x: 1});
      
      // Force subsequent operations to be rolled back.
      rollbackTest.transitionToRollbackOperations();
      testDb = rollbackTest.getPrimary().getDB('test');
      
      // These are the operations that trigger the invariant failure.
      testDb.runCommand({createIndexes: 'coll', indexes: [{key: {x: 1}, name: 'x_1'}]});
      testDb.runCommand({createIndexes: 'coll', indexes: [{key: {y: 1}, name: 'y_1'}]});
      testDb.runCommand({dropIndexes: 'coll', index: 'x_1'});
      
      rollbackTest.transitionToSyncSourceOperations();
      
      // Allow the rollback to happen.
      rollbackTest.transitionToSteadyStateOperations({waitForRollback: true});
      rollbackTest.stop();
      
      Show
      load( 'jstests/replsets/libs/rollback_test.js' ); // Set up the cluster. const rollbackTest = new RollbackTest( 'repro' ); let testDb = rollbackTest.getPrimary().getDB( 'test' ); // Ensure collections are created. testDb.coll.insert({x: 1}); // Force subsequent operations to be rolled back. rollbackTest.transitionToRollbackOperations(); testDb = rollbackTest.getPrimary().getDB( 'test' ); // These are the operations that trigger the invariant failure. testDb.runCommand({createIndexes: 'coll' , indexes: [{key: {x: 1}, name: 'x_1' }]}); testDb.runCommand({createIndexes: 'coll' , indexes: [{key: {y: 1}, name: 'y_1' }]}); testDb.runCommand({dropIndexes: 'coll' , index: 'x_1' }); rollbackTest.transitionToSyncSourceOperations(); // Allow the rollback to happen. rollbackTest.transitionToSteadyStateOperations({waitForRollback: true }); rollbackTest.stop();
    • Repl 2017-11-13

      The behavior of rollback’s FixUpInfo::removeRedundantIndexCommands method is incorrect when there is a rollback of an operation sequence with multiple index creations on the same collection. Currently, it has the following invariant:

      invariant((*indexes).second.count(indexName) == 1)
      

      which asserts that the number of indexes that exist in indexesToCreate for the current collection UUID must be exactly 1. This, however, may not be true, if we are rolling back a createIndex operation that wasn’t dropped on the current collection (see Steps To Reproduce script). This invariant must be removed, since it is incorrect and may not hold in legitimate rollback scenarios.

      The presence of this erroneous invariant masks an additional issue. The removeRedundantIndexCommands doesn’t check if the given index name actually exists in indexesToCreate (for the given UUID), only if there is an entry with the same UUID. If it does not exist, we will try to erase it from indexesToCreate, which will do nothing, but then we still return true, which causes us to bypass the addition of the index to the indexesToDrop here. We need to change this behavior so that we check for the presence of the specific index, not just the index's UUID, and only return true if we actually found the right index.

            Assignee:
            william.schultz@mongodb.com Will Schultz
            Reporter:
            robert.guo@mongodb.com Robert Guo (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: