Concurrent collection drops can return NamespaceNotFound instead of CollectionUUIDMismatch

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Major - P3
    • 6.0.2, 6.1.0-rc0
    • Affects Version/s: None
    • Component/s: None
    • None
    • Fully Compatible
    • ALL
    • v6.0
    • Execution Team 2022-08-08, Execution Team 2022-08-22
    • None
    • 3
    • None
    • None
    • None
    • None
    • None
    • None

      Because the dropCollection command releases and reacquires locks, after reacquiring locks it has to perform some checks again including whether the collection still exists. If there are multiple concurrent commands trying to drop the same collection, one thread can actually do the drop while another has yielded its locks. If the collectionUUID parameter was specified, this should result in a CollectionUUIDMismatch error rather than NamespaceNotFound. The following test reproduces this issue.

      /**
       * Tests that CollectionUUIDMismatch is properly returned with concurrent collection drops.
       */
      (function() {
      'use strict';
      
      load('jstests/libs/parallel_shell_helpers.js');
      load('jstests/libs/wait_for_command.js');
      
      const testDB = db.getSiblingDB(jsTestName());
      testDB.dropDatabase();
      
      const coll = testDB.coll;
      assert.commandWorked(testDB.createCollection(coll.getName()));
      
      const uuid = assert.commandWorked(testDB.runCommand({listCollections: 1}))
                       .cursor.firstBatch.find(c => c.name === coll.getName())
                       .info.uuid;
      
      const sleepCommand = startParallelShell(
          funWithArgs(function(ns) {
              assert.commandFailedWithCode(
                  db.adminCommand(
                      {sleep: 1, secs: 18000, lockTarget: ns, lock: 'iw', $comment: jsTestName()}),
                  ErrorCodes.Interrupted);
          }, coll.getFullName()), testDB.getMongo().port);
      const sleepID =
          waitForCommand('sleepCmd',
                         op => (op['ns'] == 'admin.$cmd' && op['command']['$comment'] == jsTestName()),
                         testDB.getSiblingDB('admin'));
      
      const firstDrop = startParallelShell(
          funWithArgs(function(dbName, collName, uuid) {
              assert.commandWorked(
                  db.getSiblingDB(dbName).runCommand({drop: collName, collectionUUID: uuid}));
          }, testDB.getName(), coll.getName(), uuid), testDB.getMongo().port);
      assert.soon(() => {
          return testDB
                     .currentOp({
                         'command.drop': coll.getName(),
                         'command.collectionUUID': uuid,
                         'locks.Collection': 'W',
                         waitingForLock: true,
                     })
                     .inprog.length === 1;
      });
      
      const secondDrop = startParallelShell(
          funWithArgs(function(dbName, collName, uuid) {
              const res = assert.commandFailedWithCode(
                  db.getSiblingDB(dbName).runCommand({drop: collName, collectionUUID: uuid}),
                  ErrorCodes.CollectionUUIDMismatch);
              assert.eq(res.db, dbName);
              assert.eq(res.collectionUUID, uuid);
              assert.eq(res.expectedCollection, collName);
              assert.eq(res.actualCollection, null);
          }, testDB.getName(), coll.getName(), uuid), testDB.getMongo().port);
      assert.soon(() => {
          return testDB
                     .currentOp({
                         'command.drop': coll.getName(),
                         'command.collectionUUID': uuid,
                         'locks.Collection': 'w',
                         waitingForLock: true,
                     })
                     .inprog.length === 1;
      });
      
      assert.commandWorked(testDB.getSiblingDB('admin').killOp(sleepID));
      sleepCommand();
      firstDrop();
      secondDrop();
      })();
      

            Assignee:
            Fausto Leyva (Inactive)
            Reporter:
            Gregory Noma
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              Resolved: