/** * Tests for basic functionality of the unshard collection feature. * * @tags: [ * requires_fcv_80, * featureFlagUnshardCollection, * assumes_balancer_off, * ] */ import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js"; import {ShardingTest} from "jstests/libs/shardingtest.js"; (function () { "use strict"; let st = new ShardingTest({mongos: 1, shards: 2}); const dbName = "db"; const collName = "foo"; const ns = dbName + "." + collName; let mongos = st.s0; let shard0 = st.shard0.shardName; let shard1 = st.shard1.shardName; let cmdObj = {unshardCollection: ns, toShard: shard1}; // Fail if unsharded collection. assert.commandFailedWithCode(mongos.adminCommand(cmdObj), ErrorCodes.NamespaceNotFound); assert.commandWorked(mongos.adminCommand({enableSharding: dbName, primaryShard: shard0})); let coll = mongos.getDB(dbName)[collName]; assert.commandWorked(coll.insert({oldKey: 50})); const unshardedCollName = "foo_unsharded"; const unshardedCollNS = dbName + "." + unshardedCollName; assert.commandWorked(st.s.getDB(dbName).runCommand({create: unshardedCollName})); let res = mongos.adminCommand({unshardCollection: unshardedCollNS}); assert.commandFailedWithCode(res, [ErrorCodes.NamespaceNotFound, ErrorCodes.NamespaceNotSharded]); assert.commandWorked(coll.createIndex({oldKey: 1})); assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {oldKey: 1}})); assert.commandWorked(mongos.adminCommand({split: ns, middle: {oldKey: 0}})); assert.commandWorked(mongos.adminCommand({moveChunk: ns, find: {oldKey: -1}, to: shard0})); assert.commandWorked(mongos.adminCommand({moveChunk: ns, find: {oldKey: 10}, to: shard1})); coll = mongos.getDB(dbName)[collName]; for (let i = -25; i < 25; ++i) { assert.commandWorked(coll.insert({oldKey: i})); } assert.eq(26, st.rs1.getPrimary().getCollection(ns).countDocuments({})); // Unshard collection should succeed with toShard option. assert.commandWorked(mongos.adminCommand({unshardCollection: ns, toShard: shard1})); // Should have unsplittable set to true let configDb = mongos.getDB("config"); let unshardedColl = configDb.collections.findOne({_id: ns}); assert.eq(unshardedColl.unsplittable, true); assert.eq(51, st.rs1.getPrimary().getCollection(ns).countDocuments({})); assert.eq(0, st.rs0.getPrimary().getCollection(ns).countDocuments({})); let unshardedChunk = configDb.chunks.find({uuid: unshardedColl.uuid}).toArray(); assert.eq(1, unshardedChunk.length); const collInfo = mongos.getDB(dbName).getCollectionInfos({name: coll.getName()})[0]; const prevCollUUID = collInfo.info.uuid; // Unshard collection should no-op with the same toShard option. assert.commandWorked(mongos.adminCommand({unshardCollection: ns, toShard: shard1})); assert.eq(mongos.getDB(dbName).getCollectionInfos({name: coll.getName()})[0].info.uuid, prevCollUUID); // Unshard collection should fail with the different toShard option. assert.commandFailedWithCode( mongos.adminCommand({unshardCollection: ns, toShard: shard0}), ErrorCodes.NamespaceNotSharded, ); const newCollName = "foo1"; const newCollNs = dbName + "." + newCollName; assert.commandWorked(mongos.adminCommand({shardCollection: newCollNs, key: {oldKey: 1}})); assert.commandWorked(mongos.adminCommand({split: newCollNs, middle: {oldKey: 0}})); assert.commandWorked(mongos.adminCommand({moveChunk: newCollNs, find: {oldKey: -1}, to: shard0})); assert.commandWorked(mongos.adminCommand({moveChunk: newCollNs, find: {oldKey: 10}, to: shard1})); coll = mongos.getDB(dbName)[newCollName]; for (let i = -30; i < 30; ++i) { assert.commandWorked(coll.insert({oldKey: i})); } // Unshard collection should succeed without toShard option. assert.commandWorked(mongos.adminCommand({unshardCollection: newCollNs})); assert( st.rs1.getPrimary().getCollection(newCollNs).countDocuments({}) == 60 || st.rs0.getPrimary().getCollection(newCollNs).countDocuments({}) == 60, ); // Fail if command called on shard. assert.commandFailedWithCode(st.shard0.adminCommand(cmdObj), ErrorCodes.CommandNotFound); assert.commandWorked(mongos.adminCommand({shardCollection: newCollNs, key: {_id: 1}})); assert.commandWorked(mongos.adminCommand({split: newCollNs, middle: {_id: 0}})); assert.commandWorked(mongos.adminCommand({moveChunk: newCollNs, find: {_id: -1}, to: shard0})); assert.commandWorked(mongos.adminCommand({moveChunk: newCollNs, find: {_id: 10}, to: shard1})); coll = mongos.getDB(dbName)[newCollName]; for (let i = -30; i < 30; ++i) { assert.commandWorked(coll.insert({_id: i})); } assert(st.rs0.getPrimary().getCollection(newCollNs).countDocuments({}) == 30); // Unshard collection should succeed when collection's original shard key is _id. assert.commandWorked(mongos.adminCommand({unshardCollection: newCollNs})); assert( st.rs1.getPrimary().getCollection(newCollNs).countDocuments({}) == 120 || st.rs0.getPrimary().getCollection(newCollNs).countDocuments({}) == 120, ); const metrics = st.config0.getDB("admin").serverStatus({}).shardingStatistics.unshardCollection; assert.eq(metrics.countStarted, 3); assert.eq(metrics.countSucceeded, 3); assert.eq(metrics.countFailed, 0); assert.eq(metrics.countCanceled, 0); st.stop(); })();