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

Concurrent collection drops can return NamespaceNotFound instead of CollectionUUIDMismatch

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

      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@mongodb.com Fausto Leyva (Inactive)
            Reporter:
            gregory.noma@mongodb.com Gregory Noma
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              Resolved: