diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index be5f74736..2e60644f4 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -556,6 +556,7 @@ export async function getPrintableShardStatus( { noBalance: coll.noBalance }, ]; } + const chunksRes = []; const chunksCollMatch = buildConfigChunksCollectionMatch(coll); const chunks = await ( @@ -566,21 +567,20 @@ export async function getPrintableShardStatus( { $sort: { shard: 1 } }, ]) ).toArray(); - let totalChunks = 0; + collRes.chunkMetadata = []; + chunks.forEach((z: any) => { - totalChunks += z.nChunks; collRes.chunkMetadata.push({ shard: z.shard, nChunks: z.nChunks, }); }); - // NOTE: this will return the chunk info as a string, and will print ugly BSON - if (totalChunks < 20 || verbose) { - for await (const chunk of ( - await chunksColl.find(chunksCollMatch) - ).sort({ min: 1 })) { + for await (const chunk of ( + await chunksColl.find(chunksCollMatch) + ).sort({ min: 1 })) { + if (chunksRes.length < 20 || verbose) { const c = { min: chunk.min, max: chunk.max, @@ -607,11 +607,12 @@ export async function getPrintableShardStatus( ); if (chunk.jumbo) c.jumbo = 'yes'; chunksRes.push(c); + } else if (chunksRes.length === 20 && !verbose) { + chunksRes.push( + 'too many chunks to print, use verbose if you want to force print' + ); + break; } - } else { - chunksRes.push( - 'too many chunks to print, use verbose if you want to force print' - ); } const tagsRes: any[] = []; @@ -620,11 +621,19 @@ export async function getPrintableShardStatus( ns: coll._id, }) ).sort({ min: 1 })) { - tagsRes.push({ - tag: tag.tag, - min: tag.min, - max: tag.max, - }); + if (tagsRes.length < 20 || verbose) { + tagsRes.push({ + tag: tag.tag, + min: tag.min, + max: tag.max, + }); + } + if (tagsRes.length === 20 && !verbose) { + tagsRes.push( + 'too many tags to print, use verbose if you want to force print' + ); + break; + } } collRes.chunks = chunksRes; collRes.tags = tagsRes; diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 498a2a0ac..239114901 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -2032,6 +2032,53 @@ describe('Shard', function () { ); expect((await sh.status()).value.shards[0].tags).to.deep.equal([]); }); + it('shows a full tag list when there are 20 or less tags', async function () { + const db = instanceState.currentDb.getSiblingDB(dbName); + for (let i = 0; i < 19; i++) { + await db.getCollection('coll').insertOne({ key: 'A', value: i * 10 }); + await sh.addShardToZone(`${shardId}-0`, `zone${i}`); + await sh.updateZoneKeyRange( + ns, + { key: i * 10 }, + { key: i * 10 + 10 }, + `zone${i}` + ); + await sh.addShardTag(`${shardId}-0`, `zone${i}`); + } + + const tags = (await sh.status()).value.databases.find( + (d) => d.database._id === 'test' + ).collections[ns].tags; + expect(tags.length).to.equal(19); + }); + it('cuts a tag list when there are more than 20 tags', async function () { + await sh.addShardToZone(`${shardId}-0`, 'zone19'); + await sh.updateZoneKeyRange(ns, { key: 190 }, { key: 200 }, 'zone19'); + await sh.addShardTag(`${shardId}-0`, 'zone19'); + + const tags = (await sh.status()).value.databases.find( + (d) => d.database._id === 'test' + ).collections[ns].tags; + expect(tags.length).to.equal(21); + expect( + tags.indexOf( + 'too many tags to print, use verbose if you want to force print' + ) + ).to.equal(20); + + // Cleanup. + const db = instanceState.currentDb.getSiblingDB(dbName); + await db.getCollection('coll').deleteMany({}); + for (let i = 0; i < 20; i++) { + await sh.removeRangeFromZone( + ns, + { key: i * 10 }, + { key: i * 10 + 10 } + ); + await sh.removeShardTag(`${shardId}-0`, `zone${i}`); + await sh.removeShardFromZone(`${shardId}-0`, `zone${i}`); + } + }); }); describe('balancer', function () { it('reports balancer state', async function () { @@ -2588,7 +2635,6 @@ describe('Shard', function () { ]); }); }); - describe('checkMetadataConsistency', function () { skipIfServerVersion(mongos, '< 7.0'); let db; @@ -2629,4 +2675,88 @@ describe('Shard', function () { }); }); }); + + describe('integration chunks', function () { + let serviceProvider: CliServiceProvider; + let instanceState: ShellInstanceState; + let sh: Shard; + const dbName = 'test'; + const ns = `${dbName}.coll`; + const shardId = 'rs-shard1'; + + const [mongos, rs0, rs1] = startTestCluster( + 'shard', + // shards: 0 creates a setup without any initial shards + { topology: 'sharded', shards: 0 }, + { + topology: 'replset', + args: ['--replSet', `${shardId}-0`, '--shardsvr'], + }, + { topology: 'replset', args: ['--replSet', `${shardId}-1`, '--shardsvr'] } + ); + + before(async function () { + serviceProvider = await CliServiceProvider.connect( + await mongos.connectionString(), + dummyOptions, + {}, + new EventEmitter() + ); + instanceState = new ShellInstanceState(serviceProvider); + sh = new Shard(instanceState.currentDb); + + // check replset uninitialized + let members = await ( + await sh._database.getSiblingDB('config').getCollection('shards').find() + ) + .sort({ _id: 1 }) + .toArray(); + expect(members.length).to.equal(0); + + // add new shards + expect( + (await sh.addShard(`${shardId}-0/${await rs0.hostport()}`)).shardAdded + ).to.equal(`${shardId}-0`); + expect( + (await sh.addShard(`${shardId}-1/${await rs1.hostport()}`)).shardAdded + ).to.equal(`${shardId}-1`); + members = await ( + await sh._database.getSiblingDB('config').getCollection('shards').find() + ) + .sort({ _id: 1 }) + .toArray(); + expect(members.length).to.equal(2); + await sh._database.getSiblingDB(dbName).dropDatabase(); + await sh._database.getSiblingDB(dbName).createCollection('unsharded'); + await sh.enableSharding(dbName); + await sh.shardCollection(ns, { key: 1 }); + }); + + after(function () { + return serviceProvider.close(true); + }); + + it('shows a full chunk list when there are 20 or less chunks', async function () { + for (let i = 0; i < 19; i++) { + await sh.splitAt(ns, { key: i + 1 }); + } + const chunks = (await sh.status()).value.databases.find( + (d) => d.database._id === 'test' + ).collections[ns].chunks; + expect(chunks.length).to.equal(20); + }); + + it('cuts a chunk list when there are more than 20 chunks', async function () { + await sh.splitAt(ns, { key: 20 }); + const chunks = (await sh.status()).value.databases.find( + (d) => d.database._id === 'test' + ).collections[ns].chunks; + expect(chunks.length).to.equal(21); + expect( + chunks.indexOf( + 'too many chunks to print, use verbose if you want to force print' + ) + ).to.equal(20); + }); + }); });