From 92eee57af61b300246aabe70e7a5ea192af5c447 Mon Sep 17 00:00:00 2001 From: Gregory Wlodarek Date: Mon, 28 Feb 2022 17:33:25 +0000 Subject: SERVER-62613 Implement _configsvrRunRestore to remove documents in the config server for collections not restored --- jstests/core/views/views_all_commands.js | 1 + .../db_reads_while_recovering_all_commands.js | 1 + .../read_write_concern_defaults_application.js | 1 + jstests/sharding/run_restore.js | 162 +++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 jstests/sharding/run_restore.js (limited to 'jstests') diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js index 8996ec0243d..d55c4f07f1f 100644 --- a/jstests/core/views/views_all_commands.js +++ b/jstests/core/views/views_all_commands.js @@ -114,6 +114,7 @@ let viewsCommandTests = { _configsvrRemoveTags: {skip: isAnInternalCommand}, _configsvrRepairShardedCollectionChunksHistory: {skip: isAnInternalCommand}, _configsvrReshardCollection: {skip: isAnInternalCommand}, + _configsvrRunRestore: {skip: isAnInternalCommand}, _configsvrSetAllowMigrations: {skip: isAnInternalCommand}, _configsvrSetClusterParameter: {skip: isAnInternalCommand}, _configsvrSetUserWriteBlockMode: {skip: isAnInternalCommand}, diff --git a/jstests/replsets/db_reads_while_recovering_all_commands.js b/jstests/replsets/db_reads_while_recovering_all_commands.js index eca3081b7b9..8c884d6e9c5 100644 --- a/jstests/replsets/db_reads_while_recovering_all_commands.js +++ b/jstests/replsets/db_reads_while_recovering_all_commands.js @@ -51,6 +51,7 @@ const allCommands = { _configsvrRepairShardedCollectionChunksHistory: {skip: isPrimaryOnly}, _configsvrRenameCollectionMetadata: {skip: isPrimaryOnly}, _configsvrReshardCollection: {skip: isPrimaryOnly}, + _configsvrRunRestore: {skip: isPrimaryOnly}, _configsvrSetAllowMigrations: {skip: isPrimaryOnly}, _configsvrSetClusterParameter: {skip: isPrimaryOnly}, _configsvrSetUserWriteBlockMode: {skip: isPrimaryOnly}, diff --git a/jstests/sharding/read_write_concern_defaults_application.js b/jstests/sharding/read_write_concern_defaults_application.js index 97ab87c0de1..b43c17181c4 100644 --- a/jstests/sharding/read_write_concern_defaults_application.js +++ b/jstests/sharding/read_write_concern_defaults_application.js @@ -117,6 +117,7 @@ let testCases = { _configsvrRenameCollectionMetadata: {skip: "internal command"}, _configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"}, _configsvrReshardCollection: {skip: "internal command"}, + _configsvrRunRestore: {skip: "internal command"}, _configsvrSetAllowMigrations: {skip: "internal command"}, _configsvrSetClusterParameter: {skip: "internal command"}, _configsvrSetUserWriteBlockMode: {skip: "internal command"}, diff --git a/jstests/sharding/run_restore.js b/jstests/sharding/run_restore.js new file mode 100644 index 00000000000..a314c2bca58 --- /dev/null +++ b/jstests/sharding/run_restore.js @@ -0,0 +1,162 @@ +/** + * Tests that the "_configsvrRunRestore" command removes documents in config collections not + * referenced in the "local.system.collections_to_restore" collection. + */ +(function() { +"use strict"; + +load("jstests/libs/feature_flag_util.js"); + +const s = + new ShardingTest({name: "runRestore", shards: 2, mongos: 1, config: 1, other: {chunkSize: 1}}); + +let mongos = s.s0; +let db = s.getDB("test"); +if (!FeatureFlagUtil.isEnabled(db, "SelectiveBackup")) { + jsTestLog("Skipping as featureFlagSelectiveBackup is not enabled"); + s.stop(); + return; +} + +s.adminCommand({enablesharding: "test"}); +s.ensurePrimaryShard("test", s.shard1.shardName); +s.adminCommand({shardcollection: "test.a", key: {x: 1}}); +s.adminCommand({shardcollection: "test.b", key: {x: 1}}); + +s.adminCommand({enablesharding: "unusedDB"}); +s.ensurePrimaryShard("unusedDB", s.shard0.shardName); + +let primary = s.getPrimaryShard("test").getDB("test"); +let primaryName = s.getPrimaryShard("test").shardName; + +let secondary = s.getOther(primary).getDB("test"); +let secondaryName = s.getOther(primary).shardName; + +for (let i = 0; i < 6; i++) { + assert.commandWorked(db.getCollection("a").insert({x: i})); + assert.commandWorked(db.getCollection("b").insert({x: i})); + + // Split chunks we just inserted. + assert.commandWorked(mongos.adminCommand({split: "test.a", middle: {x: i}})); + assert.commandWorked(mongos.adminCommand({split: "test.b", middle: {x: i}})); +} + +const aCollUUID = + mongos.getDB("config").getCollection("collections").find({_id: "test.a"}).toArray()[0].uuid; +const bCollUUID = + mongos.getDB("config").getCollection("collections").find({_id: "test.b"}).toArray()[0].uuid; + +for (const uuid of [aCollUUID, bCollUUID]) { + assert.eq(7, + mongos.getDB("config") + .getCollection("chunks") + .find({uuid: uuid, shard: primaryName}) + .count()); + assert.eq(0, + mongos.getDB("config") + .getCollection("chunks") + .find({uuid: uuid, shard: secondaryName}) + .count()); +} + +// Move chunks between shards. +for (const x of [0, 2, 4]) { + assert.commandWorked(s.s0.adminCommand( + {moveChunk: "test.a", find: {x: x}, to: secondary.getMongo().name, _waitForDelete: true})); + assert.commandWorked(s.s0.adminCommand( + {moveChunk: "test.b", find: {x: x}, to: secondary.getMongo().name, _waitForDelete: true})); +} + +// Check config collection counts. +for (const uuid of [aCollUUID, bCollUUID]) { + assert.eq(4, + mongos.getDB("config") + .getCollection("chunks") + .find({uuid: uuid, shard: primaryName}) + .count()); + assert.eq(3, + mongos.getDB("config") + .getCollection("chunks") + .find({uuid: uuid, shard: secondaryName}) + .count()); +} + +assert.eq(1, mongos.getDB("config").getCollection("collections").find({_id: "test.a"}).count()); +assert.eq(1, mongos.getDB("config").getCollection("collections").find({_id: "test.b"}).count()); + +assert.eq(1, mongos.getDB("config").getCollection("locks").find({_id: "test"}).count()); +assert.eq(1, mongos.getDB("config").getCollection("locks").find({_id: "test.a"}).count()); +assert.eq(1, mongos.getDB("config").getCollection("locks").find({_id: "test.b"}).count()); +assert.eq(1, mongos.getDB("config").getCollection("locks").find({_id: "unusedDB"}).count()); + +assert.eq(1, mongos.getDB("config").getCollection("databases").find({_id: "test"}).count()); +assert.eq(1, mongos.getDB("config").getCollection("databases").find({_id: "unusedDB"}).count()); + +s.stop({noCleanData: true}); + +const configDbPath = s.c0.dbpath; + +// Start the config server in standalone mode. +let conn = MongoRunner.runMongod({noCleanData: true, dbpath: configDbPath}); +assert(conn); + +// Can't run the "_configsvrRunRestore" command without --restore. +assert.commandFailedWithCode(conn.getDB("admin").runCommand({_configsvrRunRestore: 1}), + ErrorCodes.CommandFailed); + +MongoRunner.stopMongod(conn); + +// Start the config server in standalone restore mode. +conn = MongoRunner.runMongod({noCleanData: true, dbpath: configDbPath, restore: ""}); +assert(conn); + +assert.commandWorked(conn.getDB("admin").runCommand({setParameter: 1, logLevel: 1})); + +// Can't run if the "local.system.collections_to_restore" collection is missing. +assert.commandFailedWithCode(conn.getDB("admin").runCommand({_configsvrRunRestore: 1}), + ErrorCodes.NamespaceNotFound); + +// Create the "local.system.collections_to_restore" collection and insert "test.a". +assert.commandWorked(conn.getDB("local").createCollection("system.collections_to_restore")); +assert.commandWorked(conn.getDB("local").getCollection("system.collections_to_restore").insert({ + ns: "test.a", + uuid: aCollUUID +})); + +assert.commandWorked(conn.getDB("admin").runCommand({_configsvrRunRestore: 1})); + +assert.eq(4, + conn.getDB("config") + .getCollection("chunks") + .find({uuid: aCollUUID, shard: primaryName}) + .count()); +assert.eq(3, + conn.getDB("config") + .getCollection("chunks") + .find({uuid: aCollUUID, shard: secondaryName}) + .count()); + +assert.eq(0, + conn.getDB("config") + .getCollection("chunks") + .find({uuid: bCollUUID, shard: primaryName}) + .count()); +assert.eq(0, + conn.getDB("config") + .getCollection("chunks") + .find({uuid: bCollUUID, shard: secondaryName}) + .count()); + +assert.eq(1, conn.getDB("config").getCollection("collections").find({_id: "test.a"}).count()); +assert.eq(0, conn.getDB("config").getCollection("collections").find({_id: "test.b"}).count()); + +assert.eq(1, conn.getDB("config").getCollection("locks").find({_id: "test"}).count()); +assert.eq(1, conn.getDB("config").getCollection("locks").find({_id: "test.a"}).count()); +assert.eq(0, conn.getDB("config").getCollection("locks").find({_id: "test.b"}).count()); +assert.eq(0, conn.getDB("config").getCollection("locks").find({_id: "unusedDB"}).count()); + +assert.eq(1, conn.getDB("config").getCollection("databases").find({_id: "test"}).count()); +assert.eq(0, conn.getDB("config").getCollection("databases").find({_id: "unusedDB"}).count()); + +MongoRunner.stopMongod(conn); +}()); -- cgit v1.2.1