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(); })();