/** * Test dropDatabase command in a sharded cluster. */ import {ShardingTest} from "jstests/libs/shardingtest.js"; let st = new ShardingTest({shards: 2}); let configDB = st.s.getDB("config"); const dbNamePrefix = "testDropDB"; let dbCounter = 0; function getNewDb() { return st.s.getDB(dbNamePrefix + "_" + dbCounter++); } function getPrefixRegExp(prefix) { return RegExp("^" + RegExp.escape(prefix) + ".*"); } function getDbPrefixRegExp(dbName) { return getPrefixRegExp(dbName + "."); } function listDatabases(options) { return assert.commandWorked(st.s.adminCommand(Object.assign({listDatabases: 1}, options))).databases; } function assertDatabaseExists(dbName) { // Check that listDatabase return the db assert.gte(1, listDatabases({nameOnly: true, filter: {name: dbName}}).length); // Database entry exists assert.eq(1, configDB.databases.countDocuments({_id: dbName})); } function assertDatabaseDropped(dbName) { // Check that listDatabase doesn't return the db assert.eq(0, listDatabases({nameOnly: true, filter: {name: dbName}}).length); // No more database entry assert.eq(0, configDB.databases.countDocuments({_id: dbName})); // No more tags for this database assert.eq(0, configDB.tags.countDocuments({ns: getDbPrefixRegExp(dbName)})); // Check dropped collections. assert.eq(0, configDB.collections.countDocuments({_id: getDbPrefixRegExp(dbName)})); } jsTest.log("Test that dropping admin/config DB is illegal"); { assert.commandFailedWithCode(st.s.getDB("admin").dropDatabase(), ErrorCodes.IllegalOperation); assert.commandFailedWithCode(st.s.getDB("config").dropDatabase(), ErrorCodes.IllegalOperation); } jsTest.log("Test dropping unexistent database"); { const db = getNewDb(); // Dropping a database that doesn't exist will result in an info field in the response. const res = assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } jsTest.log("Test dropping unsharded database"); { const db = getNewDb(); // Create the database assert.commandWorked(db.foo.insert({})); assertDatabaseExists(db.getName()); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); // Test drop database idempotency assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } jsTest.log("Test dropping unsharded database with multiple collections"); { const db = getNewDb(); // Create 3 unsharded collection const colls = Array.from({length: 3}, (_, i) => db["unshardedColl_" + i]); colls.forEach((coll) => coll.insert({})); // Create the database assertDatabaseExists(db.getName()); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } jsTest.log("Test dropping sharded database"); { const db = getNewDb(); // Create the database st.s.adminCommand({enableSharding: db.getName()}); assertDatabaseExists(db.getName()); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); // Test drop database idempotency assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } jsTest.log("Test dropping database that contains regex characters"); // Original bugs [SERVER-4954, SERVER-4955] { const db = st.s.getDB("onlysmallcaseletters"); assert.commandWorked(st.s.adminCommand({enablesharding: db.getName(), primaryShard: st.shard1.shardName})); st.shardColl(db["data"], {num: 1}); assertDatabaseExists(db.getName()); const specialDB = st.s.getDB("[a-z]+"); const specialColl = db["special"]; assert.commandWorked(st.s.adminCommand({enablesharding: specialDB.getName(), primaryShard: st.shard0.shardName})); assertDatabaseExists(specialDB.getName()); st.shardColl(specialColl, {num: 1}); assert(specialColl.exists()); // Drop special database assert.commandWorked(specialDB.dropDatabase()); assertDatabaseDropped(specialDB.getName()); // The normal database still exists assertDatabaseExists(db.getName()); // Drop also the normal database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } jsTest.log("Test dropping sharded database with multiple collections"); { const db = getNewDb(); const unshardedColls = Array.from({length: 3}, (_, i) => db["unshardedColl_" + i]); unshardedColls.forEach((coll) => coll.insert({})); const shardedColls = Array.from({length: 3}, (_, i) => db["shardedColl_" + i]); shardedColls.forEach((coll) => st.shardColl(coll, {_id: 1})); // Create the database st.s.adminCommand({enableSharding: db.getName()}); assertDatabaseExists(db.getName()); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } jsTest.log("Tests that dropping a database also removes the zones associated with the collections in the database."); { const db = getNewDb(); const coll = db["sharededColl"]; const zoneName = "zone"; assert.commandWorked(st.s.adminCommand({addShardToZone: st.shard0.shardName, zone: zoneName})); st.shardColl(coll, {x: 1}); assertDatabaseExists(db.getName()); assert.commandWorked( st.s.adminCommand({updateZoneKeyRange: coll.getFullName(), min: {x: 0}, max: {x: 10}, zone: zoneName}), ); assert.eq(1, configDB.tags.countDocuments({ns: getDbPrefixRegExp(db.getName())})); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); } { const db = getNewDb(); const conn = st.rs0.getPrimary().getDB(db.getName()); const fcvDoc = conn.adminCommand({getParameter: 1, featureCompatibilityVersion: 1}); if (MongoRunner.compareBinVersions(fcvDoc.featureCompatibilityVersion.version, "6.3") >= 0) { jsTest.log( "Tests that dropping a database also removes all associated zones for nonexisting and " + "unsharded collections.", ); { const coll = db["shardedColl"]; const unshardedColl = db["unshardedColl"]; const zoneName = "zone"; st.shardColl(coll, {x: 1}); assertDatabaseExists(db.getName()); // Create an unsharded collection assert.commandWorked(unshardedColl.insert({x: 3})); // Append zones for a sharded collection, unsharded collection, a nonexisting collection // and a collection from another database. st.addShardTag(st.shard0.shardName, zoneName); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: coll.getFullName(), min: {x: 0}, max: {x: 10}, zone: zoneName, }), ); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: unshardedColl.getFullName(), min: {x: 10}, max: {x: 15}, zone: zoneName, }), ); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: db.getName() + ".nonexisting", min: {x: 15}, max: {x: 20}, zone: zoneName, }), ); assert.commandWorked( st.s.adminCommand({updateZoneKeyRange: "otherDb.coll", min: {x: 20}, max: {x: 25}, zone: zoneName}), ); // Assert that has been added some entries on 'config.tags' assert.eq(3, configDB.tags.countDocuments({ns: getDbPrefixRegExp(db.getName())})); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); // Assert that there are no zones left for database assert.eq(0, configDB.tags.countDocuments({ns: getDbPrefixRegExp(db.getName())})); // Assert that there is one zone from another database that has not been deleted. assert.eq(1, configDB.tags.countDocuments({ns: getDbPrefixRegExp("otherDb")})); } } } { const db = st.s.getDB("db[a-z]+"); const conn = st.rs0.getPrimary().getDB(db.getName()); const fcvDoc = conn.adminCommand({getParameter: 1, featureCompatibilityVersion: 1}); if (MongoRunner.compareBinVersions(fcvDoc.featureCompatibilityVersion.version, "6.3") >= 0) { jsTest.log( "Tests that dropping a database with regex characters also removes all associated " + "zones for nonexisting and unsharded collections.", ); { const coll = db["shardedColl"]; const unshardedColl = db["unshardedColl"]; const zoneName = "zone"; st.shardColl(coll, {x: 1}); assertDatabaseExists(db.getName()); // Create an unsharded collection assert.commandWorked(unshardedColl.insert({x: 3})); // Append zones for a sharded collection, unsharded collection, a nonexisting collection // and a collection from another database. st.addShardTag(st.shard0.shardName, zoneName); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: coll.getFullName(), min: {x: 0}, max: {x: 10}, zone: zoneName, }), ); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: unshardedColl.getFullName(), min: {x: 10}, max: {x: 15}, zone: zoneName, }), ); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: db.getName() + ".nonexisting", min: {x: 15}, max: {x: 20}, zone: zoneName, }), ); assert.commandWorked( st.s.adminCommand({ updateZoneKeyRange: "otherDbRegex.coll", min: {x: 20}, max: {x: 25}, zone: zoneName, }), ); // Assert that has been added some entries on 'config.tags' assert.eq(3, configDB.tags.countDocuments({ns: getDbPrefixRegExp(db.getName())})); // Drop the database assert.commandWorked(db.dropDatabase()); assertDatabaseDropped(db.getName()); // Assert that there are no zones left for database assert.eq(0, configDB.tags.countDocuments({ns: getDbPrefixRegExp(db.getName())})); // Assert that there is one zone from another database that has not been deleted. assert.eq(1, configDB.tags.countDocuments({ns: getDbPrefixRegExp("otherDbRegex")})); } } } jsTest.log("Tests that dropping a database doesn't affects other database with the same prefix."); // Original bug SERVER-3471 { // Create 3 DBs // - SomePrefix // - SomePrefix_A // - SomePrefix_b const dbPrefix = dbNamePrefix + "Prefix"; const databases = [st.s.getDB(dbPrefix), st.s.getDB(dbPrefix + "_A"), st.s.getDB(dbPrefix + "_B")]; // Assert all database have the same prefix assert.containsPrefix( dbNamePrefix, databases.map((db) => db.getName()), ); const collPrefix = "coll_"; const numColls = 3; const numDocs = 3; // Create 3 colls with 3 documents on each database databases.forEach((db) => { for (let collID = 0; collID < numColls; collID++) { let coll = db[collPrefix + collID]; // Create 3 documents for each collection for (let docID = 0; docID < numDocs; docID++) { coll.insert({_id: docID}); } // shard the collection st.shardColl(coll, {_id: 1}); } assertDatabaseExists(db.getName()); }); // Insert a document to an unsharded collection and make sure that the document is there. const unshardedColl = databases[0]["unshardedColl"]; assert.commandWorked(unshardedColl.insert({dummy: 1})); assert.eq(1, unshardedColl.countDocuments({dummy: 1})); // Drop the non-suffixed db assert.commandWorked(databases[0].dropDatabase()); assertDatabaseDropped(databases[0].getName()); assert.eq(0, unshardedColl.countDocuments({})); // Ensure that the others databases exists and still contains all the collections databases.slice(1).forEach((db) => { assertDatabaseExists(db.getName()); assert.eq(numColls, configDB.collections.countDocuments({_id: getDbPrefixRegExp(db.getName())})); // Assert that all the collections still have all the documents for (let collID = 0; collID < numColls; collID++) { assert.eq(numDocs, db[collPrefix + collID].countDocuments({})); } }); } jsTest.log("Test that dropping a non-sharded database, relevant events are properly logged on CSRS"); { // Create a non-sharded database const db = getNewDb(); assert.commandWorked(db.foo.insert({})); // Drop the database assert.commandWorked(db.dropDatabase()); // Verify that the drop database start event has been logged const startLogCount = configDB.changelog.countDocuments({what: "dropDatabase.start", ns: db.getName()}); assert.gte(startLogCount, 1, "dropDatabase start event not found in changelog"); // Verify that the drop database end event has been logged const endLogCount = configDB.changelog.countDocuments({what: "dropDatabase", ns: db.getName()}); assert.gte(endLogCount, 1, "dropDatabase end event not found in changelog"); } jsTest.log("Test that dropping a sharded database, relevant events are properly logged on CSRS"); { // Create a sharded database const db = getNewDb(); st.s.adminCommand({enableSharding: db.getName()}); // Drop the database assert.commandWorked(db.dropDatabase()); // Verify that the drop database start event has been logged const startLogCount = configDB.changelog.countDocuments({what: "dropDatabase.start", ns: db.getName()}); assert.gte(startLogCount, 1, "dropDatabase start event not found in changelog"); // Verify that the drop database end event has been logged const endLogCount = configDB.changelog.countDocuments({what: "dropDatabase", ns: db.getName()}); assert.gte(endLogCount, 1, "dropDatabase end event not found in changelog"); } st.stop();