From d0822481b3ee0a0eb5cea108713bc660cc409699 Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 11 Oct 2024 14:44:38 +0200 Subject: [PATCH 01/18] WIP: test if shardDistribution works --- packages/shell-api/src/helpers.spec.ts | 12 ++- packages/shell-api/src/helpers.ts | 139 +++++++++++++++++++++---- 2 files changed, 126 insertions(+), 25 deletions(-) diff --git a/packages/shell-api/src/helpers.spec.ts b/packages/shell-api/src/helpers.spec.ts index b33cdae556..d239390540 100644 --- a/packages/shell-api/src/helpers.spec.ts +++ b/packages/shell-api/src/helpers.spec.ts @@ -214,7 +214,9 @@ describe('getPrintableShardStatus', function () { ]) { it(`does not show ${hiddenField} in shardingVersion`, async function () { const status = await getPrintableShardStatus(configDatabase, false); - expect(status.shardingVersion[hiddenField]).to.equal(undefined); + expect((status.shardingVersion as any)[hiddenField]).to.equal( + undefined + ); }); } }); @@ -235,8 +237,10 @@ describe('getPrintableShardStatus', function () { it('returns an object with verbose sharding information if requested', async function () { const status = await getPrintableShardStatus(configDatabase, true); - expect(status['most recently active mongoses'][0].up).to.be.a('number'); - expect(status['most recently active mongoses'][0].waiting).to.be.a( + expect((status['most recently active mongoses'][0] as any).up).to.be.a( + 'number' + ); + expect((status['most recently active mongoses'][0] as any).waiting).to.be.a( 'boolean' ); }); @@ -281,7 +285,7 @@ describe('getPrintableShardStatus', function () { status.balancer['Collections with active migrations'] ).to.have.lengthOf(1); expect( - status.balancer['Collections with active migrations'].join('') + status.balancer['Collections with active migrations']?.join('') ).to.include('asdf'); }); diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index 0b1affb428..bfe97185ba 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -26,7 +26,7 @@ import type { bson, } from '@mongosh/service-provider-core'; import type { ClientSideFieldLevelEncryptionOptions } from './field-level-encryption'; -import { type AutoEncryptionOptions } from 'mongodb'; +import type { AutoEncryptionOptions, Long, ObjectId, Timestamp } from 'mongodb'; import { shellApiType } from './enums'; import type { AbstractCursor } from './abstract-cursor'; import type ChangeStreamCursor from './change-stream-cursor'; @@ -224,10 +224,12 @@ export function processDigestPassword( * @param verbose */ export async function getPrintableShardStatus( - configDB: Database, + db: Database, verbose: boolean -): Promise { - const result = {} as any; +): Promise { + const result = {} as ShardingStatusResult; + + const configDB = await getConfigDB(db); // configDB is a DB object that contains the sharding metadata of interest. const mongosColl = configDB.getCollection('mongos'); @@ -259,9 +261,12 @@ export async function getPrintableShardStatus( ); } - result.shardingVersion = version; + result.shardingVersion = version as { + _id: number; + clusterId: ObjectId; + }; - result.shards = shards; + result.shards = shards as ShardingStatusResult['shards']; // (most recently) active mongoses const mongosActiveThresholdMs = 60000; @@ -280,9 +285,8 @@ export async function getPrintableShardStatus( } } - mongosAdjective = `${mongosAdjective} mongoses`; if (mostRecentMongosTime === null) { - result[mongosAdjective] = 'none'; + result[`${mongosAdjective} mongoses`] = 'none'; } else { const recentMongosQuery = { ping: { @@ -295,25 +299,27 @@ export async function getPrintableShardStatus( }; if (verbose) { - result[mongosAdjective] = await (await mongosColl.find(recentMongosQuery)) + result[`${mongosAdjective} mongoses`] = await ( + await mongosColl.find(recentMongosQuery) + ) .sort({ ping: -1 }) .toArray(); } else { - result[mongosAdjective] = ( + result[`${mongosAdjective} mongoses`] = ( (await ( await mongosColl.aggregate([ { $match: recentMongosQuery }, { $group: { _id: '$mongoVersion', num: { $sum: 1 } } }, { $sort: { num: -1 } }, ]) - ).toArray()) as any[] + ).toArray()) as { _id: string; num: number }[] ).map((z: { _id: string; num: number }) => { return { [z._id]: z.num }; }); } } - const balancerRes: Record = {}; + const balancerRes = {} as ShardingStatusResult['balancer']; await Promise.all([ (async (): Promise => { // Is autosplit currently enabled @@ -331,13 +337,13 @@ export async function getPrintableShardStatus( })(), (async (): Promise => { // Is the balancer currently active - let balancerRunning = 'unknown'; + let balancerRunning: 'yes' | 'no' | 'unknown' = 'unknown'; try { const balancerStatus = await configDB.adminCommand({ balancerStatus: 1, }); balancerRunning = balancerStatus.inBalancerRound ? 'yes' : 'no'; - } catch (err: any) { + } catch (err) { // pass, ignore all error messages } balancerRes['Currently running'] = balancerRunning; @@ -364,7 +370,9 @@ export async function getPrintableShardStatus( if (activeLocks?.length > 0) { balancerRes['Collections with active migrations'] = activeLocks.map( (lock) => { - return `${lock._id} started at ${lock.when}`; + // This type assertion is necessary for the string literal type check + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + return `${lock._id} started at ${lock.when}` as `${string} started at ${string}`; } ); } @@ -418,8 +426,23 @@ export async function getPrintableShardStatus( const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); + type MigrationResult = + | { + _id: 'Success'; + count: number; + from: never; + to: never; + } + // Failed migration + | { + _id: string; + count: number; + from: string; + to: string; + }; + // Successful migrations. - let migrations = await ( + let migrations = (await ( await changelogColl.aggregate([ { $match: { @@ -437,11 +460,11 @@ export async function getPrintableShardStatus( }, }, ]) - ).toArray(); + ).toArray()) as MigrationResult[]; // Failed migrations. migrations = migrations.concat( - await ( + (await ( await changelogColl.aggregate([ { $match: { @@ -472,11 +495,12 @@ export async function getPrintableShardStatus( }, }, ]) - ).toArray() + ).toArray()) as MigrationResult[] ); - const migrationsRes: Record = {}; - migrations.forEach((x: any) => { + const migrationsRes: ShardingStatusResult['balancer']['Migration Results for the last 24 hours'] = + {}; + migrations.forEach((x) => { if (x._id === 'Success') { migrationsRes[x.count] = x._id; } else { @@ -644,10 +668,83 @@ export async function getPrintableShardStatus( ) ).filter((dbEntry) => !!dbEntry); + let shardedDataDistribution: ShardedDataDistribution | undefined; + + try { + // Available since 6.0.3 + const adminDb = db.getSiblingDB('admin'); + shardedDataDistribution = (await ( + await adminDb.aggregate([{ $shardedDataDistribution: {} }]) + ).toArray()) as ShardedDataDistribution; + } catch (e) { + // Pass, most likely an older version. + console.error('shardedDataDistribution Errored:', e); + } + + result.shardedDataDistribution = shardedDataDistribution; + delete result.shardingVersion.currentVersion; return result; } +type ShardingStatusResult = { + shardingVersion: { + _id: number; + clusterId: ObjectId; + /** This gets deleted when it is returned from getPrintableShardStatus */ + currentVersion?: number; + }; + shards: { + _id: string; + host: string; + state: number; + topologyTime: Timestamp; + replSetConfigVersion: Long; + }[]; + [mongoses: `${string} mongoses`]: + | 'none' + | { + [version: string]: + | number + | { + up: number; + waiting: boolean; + }; + }[]; + autosplit: { + 'Currently enabled': 'yes' | 'no'; + }; + balancer: { + 'Currently enabled': 'yes' | 'no'; + 'Currently running': 'yes' | 'no' | 'unknown'; + 'Failed balancer rounds in last 5 attempts': number; + 'Migration Results for the last 24 hours': + | 'No recent migrations' + | { + [count: number]: + | 'Success' + | `Failed with error '${string}', from ${string} to ${string}`; + }; + 'Balancer active window is set between'?: `${string} and ${string} server local time`; + 'Last reported error'?: string; + 'Time of Reported error'?: string; + 'Collections with active migrations'?: `${string} started at ${string}`[]; + }; + shardedDataDistribution?: ShardedDataDistribution; + databases: { database: Document; collections: Document }[]; +}; + +type ShardedDataDistribution = { + ns: string; + shards: { + shardName: string; + numOrphanedDocs: number; + numOwnedDocuments: number; + orphanedSizeBytes: number; + ownedSizeBytes: number; + }; +}[]; + export async function getConfigDB(db: Database): Promise { const helloResult = await db._maybeCachedHello(); if (helloResult.msg !== 'isdbgrid') { From fc93cbd1a42012eb0d72dbcb71b9e61c6c14e025 Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 14 Oct 2024 11:33:22 +0200 Subject: [PATCH 02/18] Fix helper tests --- packages/shell-api/src/helpers.spec.ts | 29 ++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/shell-api/src/helpers.spec.ts b/packages/shell-api/src/helpers.spec.ts index d239390540..2661bea903 100644 --- a/packages/shell-api/src/helpers.spec.ts +++ b/packages/shell-api/src/helpers.spec.ts @@ -19,6 +19,7 @@ import sinon from 'ts-sinon'; import chai, { expect } from 'chai'; import { EventEmitter } from 'events'; import sinonChai from 'sinon-chai'; +import { stub } from 'sinon'; chai.use(sinonChai); const fakeConfigDb = makeFakeConfigDatabase( @@ -129,6 +130,7 @@ describe('getPrintableShardStatus', function () { const testServer = startSharedTestServer(); let mongo: Mongo; + let db: Database; let configDatabase: Database; let serviceProvider: ServiceProvider; let inBalancerRound = false; @@ -150,6 +152,11 @@ describe('getPrintableShardStatus', function () { configDatabase = new Database(mongo, 'config_test'); expect(configDatabase.getName()).to.equal('config_test'); + db = { + _maybeCachedHello: stub().returns({ msg: 'isdbgrid' }), + getSiblingDB: stub().withArgs('config').returns(configDatabase), + } as unknown as Database; + const origRunCommandWithCheck = serviceProvider.runCommandWithCheck; serviceProvider.runCommandWithCheck = async (db, cmd) => { if (cmd.hello) { @@ -186,7 +193,7 @@ describe('getPrintableShardStatus', function () { }); it('returns an object with sharding information', async function () { - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect(status.shardingVersion.clusterId).to.be.instanceOf(bson.ObjectId); expect(status.shards.map(({ host }: { host: string }) => host)).to.include( 'shard01/localhost:27018,localhost:27019,localhost:27020' @@ -213,7 +220,7 @@ describe('getPrintableShardStatus', function () { 'upgradeState', ]) { it(`does not show ${hiddenField} in shardingVersion`, async function () { - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect((status.shardingVersion as any)[hiddenField]).to.equal( undefined ); @@ -224,19 +231,19 @@ describe('getPrintableShardStatus', function () { it('returns whether the balancer is currently running', async function () { { inBalancerRound = true; - const status = await getPrintableShardStatus(configDatabase, true); + const status = await getPrintableShardStatus(db, true); expect(status.balancer['Currently running']).to.equal('yes'); } { inBalancerRound = false; - const status = await getPrintableShardStatus(configDatabase, true); + const status = await getPrintableShardStatus(db, true); expect(status.balancer['Currently running']).to.equal('no'); } }); it('returns an object with verbose sharding information if requested', async function () { - const status = await getPrintableShardStatus(configDatabase, true); + const status = await getPrintableShardStatus(db, true); expect((status['most recently active mongoses'][0] as any).up).to.be.a( 'number' ); @@ -250,7 +257,7 @@ describe('getPrintableShardStatus', function () { _id: 'balancer', activeWindow: { start: '00:00', stop: '23:59' }, }); - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect(status.balancer['Balancer active window is set between']).to.equal( '00:00 and 23:59 server local time' ); @@ -266,7 +273,7 @@ describe('getPrintableShardStatus', function () { what: 'balancer.round', ns: '', }); - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect( status.balancer['Failed balancer rounds in last 5 attempts'] ).to.equal(1); @@ -280,7 +287,7 @@ describe('getPrintableShardStatus', function () { ts: new bson.ObjectId('5fce116c579db766a198a176'), when: new Date('2020-12-07T11:26:36.803Z'), }); - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect( status.balancer['Collections with active migrations'] ).to.have.lengthOf(1); @@ -295,7 +302,7 @@ describe('getPrintableShardStatus', function () { what: 'moveChunk.from', details: { from: 'shard0', to: 'shard1', note: 'success' }, }); - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect( status.balancer['Migration Results for the last 24 hours'] ).to.deep.equal({ 1: 'Success' }); @@ -307,7 +314,7 @@ describe('getPrintableShardStatus', function () { what: 'moveChunk.from', details: { from: 'shard0', to: 'shard1', errmsg: 'oopsie' }, }); - const status = await getPrintableShardStatus(configDatabase, false); + const status = await getPrintableShardStatus(db, false); expect( status.balancer['Migration Results for the last 24 hours'] @@ -317,7 +324,7 @@ describe('getPrintableShardStatus', function () { it('fails when config.version is empty', async function () { await configDatabase.getCollection('version').drop(); try { - await getPrintableShardStatus(configDatabase, false); + await getPrintableShardStatus(db, false); } catch (err: any) { expect(err.name).to.equal('MongoshInvalidInputError'); return; From 6fc82ebef1d8f138c31ea623411c7256d0825eb4 Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 15 Oct 2024 11:18:50 +0200 Subject: [PATCH 03/18] Add unit test --- packages/shell-api/src/helpers.spec.ts | 33 +++++++++++++++++++++++++- packages/shell-api/src/helpers.ts | 4 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/shell-api/src/helpers.spec.ts b/packages/shell-api/src/helpers.spec.ts index 2661bea903..8ffa380dc3 100644 --- a/packages/shell-api/src/helpers.spec.ts +++ b/packages/shell-api/src/helpers.spec.ts @@ -1,3 +1,4 @@ +import type { ShardedDataDistribution } from './helpers'; import { assertArgsDefinedType, coerceToJSNumber, @@ -135,6 +136,21 @@ describe('getPrintableShardStatus', function () { let serviceProvider: ServiceProvider; let inBalancerRound = false; + const mockedShardedDataDistribution: ShardedDataDistribution = [ + { + ns: 'test.ns', + shards: [ + { + shardName: 'test', + numOrphanedDocs: 1, + numOwnedDocuments: 5, + orphanedSizeBytes: 20, + ownedSizeBytes: 80, + }, + ], + }, + ]; + beforeEach(async function () { serviceProvider = await CliServiceProvider.connect( await testServer.connectionString(), @@ -152,9 +168,20 @@ describe('getPrintableShardStatus', function () { configDatabase = new Database(mongo, 'config_test'); expect(configDatabase.getName()).to.equal('config_test'); + const mockedAdminDb = { + aggregate: stub() + .withArgs([{ $shardedDataDistribution: {} }]) + .resolves({ + toArray: stub().resolves(mockedShardedDataDistribution), + }), + }; + const getSiblingDB = stub(); + getSiblingDB.withArgs('admin').returns(mockedAdminDb); + getSiblingDB.withArgs('config').returns(configDatabase); + db = { _maybeCachedHello: stub().returns({ msg: 'isdbgrid' }), - getSiblingDB: stub().withArgs('config').returns(configDatabase), + getSiblingDB, } as unknown as Database; const origRunCommandWithCheck = serviceProvider.runCommandWithCheck; @@ -209,6 +236,10 @@ describe('getPrintableShardStatus', function () { ); expect(status.databases).to.have.lengthOf(1); expect(status.databases[0].database._id).to.equal('config'); + + expect(status.shardedDataDistribution).to.equal( + mockedShardedDataDistribution + ); }); describe('hides all internal deprecated fields in shardingVersion', function () { diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index bfe97185ba..4d9144d131 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -734,7 +734,7 @@ type ShardingStatusResult = { databases: { database: Document; collections: Document }[]; }; -type ShardedDataDistribution = { +export type ShardedDataDistribution = { ns: string; shards: { shardName: string; @@ -742,7 +742,7 @@ type ShardedDataDistribution = { numOwnedDocuments: number; orphanedSizeBytes: number; ownedSizeBytes: number; - }; + }[]; }[]; export async function getConfigDB(db: Database): Promise { From 9fb806dfd282a8037590b949ddf01225a03537ea Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 22 Oct 2024 15:15:54 +0200 Subject: [PATCH 04/18] Remove log --- packages/shell-api/src/helpers.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index 4d9144d131..70c4edfe27 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -678,7 +678,6 @@ export async function getPrintableShardStatus( ).toArray()) as ShardedDataDistribution; } catch (e) { // Pass, most likely an older version. - console.error('shardedDataDistribution Errored:', e); } result.shardedDataDistribution = shardedDataDistribution; From f71faa0222539ccb9d566d00ee714e78f42c52d2 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 23 Oct 2024 14:03:21 +0200 Subject: [PATCH 05/18] Add integration test --- packages/shell-api/src/helpers.ts | 5 +- packages/shell-api/src/shard.spec.ts | 100 ++++++++++++++++++++++++--- packages/shell-api/src/shard.ts | 4 +- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index 70c4edfe27..0617660245 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -671,7 +671,7 @@ export async function getPrintableShardStatus( let shardedDataDistribution: ShardedDataDistribution | undefined; try { - // Available since 6.0.3 + // Available since >= 6.0.3 const adminDb = db.getSiblingDB('admin'); shardedDataDistribution = (await ( await adminDb.aggregate([{ $shardedDataDistribution: {} }]) @@ -686,7 +686,7 @@ export async function getPrintableShardStatus( return result; } -type ShardingStatusResult = { +export type ShardingStatusResult = { shardingVersion: { _id: number; clusterId: ObjectId; @@ -697,6 +697,7 @@ type ShardingStatusResult = { _id: string; host: string; state: number; + tags: string[]; topologyTime: Timestamp; replSetConfigVersion: Long; }[]; diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index c8ec6a3cd1..9d8f73e99b 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -32,6 +32,7 @@ import { import Database from './database'; import { inspect } from 'util'; import { dummyOptions } from './helpers.spec'; +import type { ShardedDataDistribution } from './helpers'; describe('Shard', function () { skipIfApiStrict(); @@ -2121,7 +2122,7 @@ describe('Shard', function () { expect( (await sh.status()).value.databases.find( (d: Document) => d.database._id === 'test' - ).collections[ns].shardKey + )?.collections[ns].shardKey ).to.deep.equal({ key: 1 }); const db = instanceState.currentDb.getSiblingDB(dbName); @@ -2166,13 +2167,13 @@ describe('Shard', function () { describe('tags', function () { it('creates a zone', async function () { expect((await sh.addShardTag(`${shardId}-1`, 'zone1')).ok).to.equal(1); - expect((await sh.status()).value.shards[1].tags).to.deep.equal([ + expect((await sh.status()).value.shards[1]?.tags).to.deep.equal([ 'zone1', ]); expect((await sh.addShardToZone(`${shardId}-0`, 'zone0')).ok).to.equal( 1 ); - expect((await sh.status()).value.shards[0].tags).to.deep.equal([ + expect((await sh.status()).value.shards[0]?.tags).to.deep.equal([ 'zone0', ]); }); @@ -2241,7 +2242,7 @@ describe('Shard', function () { const tags = (await sh.status()).value.databases.find( (d: Document) => d.database._id === 'test' - ).collections[ns].tags; + )?.collections[ns].tags; expect(tags.length).to.equal(19); }); it('cuts a tag list when there are more than 20 tags', async function () { @@ -2251,7 +2252,7 @@ describe('Shard', function () { const tags = (await sh.status()).value.databases.find( (d: Document) => d.database._id === 'test' - ).collections[ns].tags; + )?.collections[ns].tags; expect(tags.length).to.equal(21); expect( tags.indexOf( @@ -2885,6 +2886,87 @@ describe('Shard', function () { }); }); }); + + describe('collection.status()', function () { + let db: Database; + + const dbName = 'shard-stats-test'; + const ns = `${dbName}.test`; + + beforeEach(async function () { + db = sh._database.getSiblingDB(dbName); + await db.getCollection('test').insertOne({ key: 1 }); + await db.getCollection('test').createIndex({ key: 1 }); + }); + afterEach(async function () { + await db.dropDatabase(); + }); + describe('unsharded collections', function () { + describe('with >= 6.0.3', function () { + skipIfServerVersion(mongos, '< 6.0.3'); + + it('returns shardedDataDistribution as an empty array', async function () { + const status = await sh.status(); + expect(status.value.shardedDataDistribution).deep.equals([]); + }); + }); + + describe('with < 6.0.3', function () { + skipIfServerVersion(mongos, '>= 6.0.3'); + + it('returns shardedDataDistribution as undefined', async function () { + const status = await sh.status(); + expect(status.value.shardedDataDistribution).equals(undefined); + }); + }); + }); + + describe('sharded collections', function () { + beforeEach(async function () { + expect((await sh.enableSharding(dbName)).ok).to.equal(1); + expect( + (await sh.shardCollection(ns, { key: 1 })).collectionsharded + ).to.equal(ns); + }); + + describe('with >= 6.0.3', function () { + skipIfServerVersion(mongos, '< 6.0.3'); + + it('returns correct shardedDataDistribution', async function () { + const expectedShardedDataDistribution: ShardedDataDistribution = [ + { + ns: 'shard-stats-test.test', + shards: [ + { + shardName: 'rs-shard0-0', + numOrphanedDocs: 0, + numOwnedDocuments: 1, + ownedSizeBytes: 31, + orphanedSizeBytes: 0, + }, + ], + }, + ]; + + const status = await sh.status(); + + expect(status.value.shardedDataDistribution).deep.equals( + expectedShardedDataDistribution + ); + }); + }); + + describe('with < 6.0.3', function () { + skipIfServerVersion(mongos, '>= 6.0.3'); + + it('returns shardedDataDistribution as undefined', async function () { + const status = await sh.status(); + expect(status.value.shardedDataDistribution).equals(undefined); + }); + }); + }); + }); + describe('collection.isCapped', function () { it('returns true for config.changelog', async function () { const ret = await sh._database @@ -2929,7 +3011,7 @@ describe('Shard', function () { (item: Document) => item.database._id === 'db' ); // Cannot get strict guarantees about the value of this field since SERVER-63983 - expect(databasesDbItem.database.partitioned).to.be.oneOf([ + expect(databasesDbItem?.database.partitioned).to.be.oneOf([ false, undefined, ]); @@ -2937,7 +3019,7 @@ describe('Shard', function () { (item: Document) => item.database._id === 'dbSh' ); // Cannot get strict guarantees about the value of this field since SERVER-60926 and SERVER-63983 - expect(databasesDbShItem.database.partitioned).to.be.oneOf([ + expect(databasesDbShItem?.database.partitioned).to.be.oneOf([ true, false, undefined, @@ -3051,7 +3133,7 @@ describe('Shard', function () { } const chunks = (await sh.status()).value.databases.find( (d: Document) => d.database._id === 'test' - ).collections[ns].chunks; + )?.collections[ns].chunks; expect(chunks.length).to.equal(20); }); @@ -3059,7 +3141,7 @@ describe('Shard', function () { await sh.splitAt(ns, { key: 20 }); const chunks = (await sh.status()).value.databases.find( (d: Document) => d.database._id === 'test' - ).collections[ns].chunks; + )?.collections[ns].chunks; expect(chunks.length).to.equal(21); expect( chunks.indexOf( diff --git a/packages/shell-api/src/shard.ts b/packages/shell-api/src/shard.ts index cd2882115b..9f5f65422d 100644 --- a/packages/shell-api/src/shard.ts +++ b/packages/shell-api/src/shard.ts @@ -12,10 +12,12 @@ import type { Document, CheckMetadataConsistencyOptions, } from '@mongosh/service-provider-core'; +import type { ShardingStatusResult } from './helpers'; import { assertArgsDefinedType, getConfigDB, getPrintableShardStatus, + ShardedDataDistribution, } from './helpers'; import { ServerVersions, asPrintable } from './enums'; import type { UpdateResult } from './result'; @@ -205,7 +207,7 @@ export default class Shard extends ShellApiWithMongoClass { async status( verbose = false, configDB?: Database - ): Promise> { + ): Promise> { const result = await getPrintableShardStatus( configDB ?? (await getConfigDB(this._database)), verbose From 80ac653485671716724b70e714295b4f5f91cb4c Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 23 Oct 2024 15:06:51 +0200 Subject: [PATCH 06/18] Clear previous shards --- packages/shell-api/src/shard.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 9d8f73e99b..e609f6c5ce 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -2893,6 +2893,10 @@ describe('Shard', function () { const dbName = 'shard-stats-test'; const ns = `${dbName}.test`; + before(async function () { + await db.dropDatabase(); + }); + beforeEach(async function () { db = sh._database.getSiblingDB(dbName); await db.getCollection('test').insertOne({ key: 1 }); From 05f6b330d285534a8375818b3008f6bbdf7ddb4e Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 23 Oct 2024 15:12:41 +0200 Subject: [PATCH 07/18] Use a different db name --- packages/shell-api/src/shard.spec.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index e609f6c5ce..78598774a5 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -2890,13 +2890,9 @@ describe('Shard', function () { describe('collection.status()', function () { let db: Database; - const dbName = 'shard-stats-test'; + const dbName = 'shard-status-test'; const ns = `${dbName}.test`; - before(async function () { - await db.dropDatabase(); - }); - beforeEach(async function () { db = sh._database.getSiblingDB(dbName); await db.getCollection('test').insertOne({ key: 1 }); From 2ef07dbd59e00bdf10d4d395b496fc56b650fb4d Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 23 Oct 2024 15:13:06 +0200 Subject: [PATCH 08/18] and correct ns --- packages/shell-api/src/shard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 78598774a5..6f0339ce4d 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -2935,7 +2935,7 @@ describe('Shard', function () { it('returns correct shardedDataDistribution', async function () { const expectedShardedDataDistribution: ShardedDataDistribution = [ { - ns: 'shard-stats-test.test', + ns, shards: [ { shardName: 'rs-shard0-0', From 631c92fe2b29dd0f1a3bb6a17c9c3e4a2fe03de8 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 23 Oct 2024 15:14:54 +0200 Subject: [PATCH 09/18] Remove unneeded import --- packages/shell-api/src/shard.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shell-api/src/shard.ts b/packages/shell-api/src/shard.ts index 9f5f65422d..293ca6fcab 100644 --- a/packages/shell-api/src/shard.ts +++ b/packages/shell-api/src/shard.ts @@ -17,7 +17,6 @@ import { assertArgsDefinedType, getConfigDB, getPrintableShardStatus, - ShardedDataDistribution, } from './helpers'; import { ServerVersions, asPrintable } from './enums'; import type { UpdateResult } from './result'; From 0083086053211596c7adfcd0574fa28cdfc46a75 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 09:57:48 +0200 Subject: [PATCH 10/18] Move test before others --- packages/shell-api/src/shard.spec.ts | 160 +++++++++++++-------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 6f0339ce4d..d4a683f388 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -2044,6 +2044,86 @@ describe('Shard', function () { return serviceProvider.close(true); }); + describe('collection.status()', function () { + let db: Database; + + const dbName = 'shard-status-test'; + const ns = `${dbName}.test`; + + beforeEach(async function () { + db = sh._database.getSiblingDB(dbName); + await db.getCollection('test').insertOne({ key: 1 }); + await db.getCollection('test').createIndex({ key: 1 }); + }); + afterEach(async function () { + await db.dropDatabase(); + }); + describe('unsharded collections', function () { + describe('with >= 6.0.3', function () { + skipIfServerVersion(mongos, '< 6.0.3'); + + it('returns shardedDataDistribution as an empty array', async function () { + const status = await sh.status(); + expect(status.value.shardedDataDistribution).deep.equals([]); + }); + }); + + describe('with < 6.0.3', function () { + skipIfServerVersion(mongos, '>= 6.0.3'); + + it('returns shardedDataDistribution as undefined', async function () { + const status = await sh.status(); + expect(status.value.shardedDataDistribution).equals(undefined); + }); + }); + }); + + describe('sharded collections', function () { + beforeEach(async function () { + expect((await sh.enableSharding(dbName)).ok).to.equal(1); + expect( + (await sh.shardCollection(ns, { key: 1 })).collectionsharded + ).to.equal(ns); + }); + + describe('with >= 6.0.3', function () { + skipIfServerVersion(mongos, '< 6.0.3'); + + it('returns correct shardedDataDistribution', async function () { + const expectedShardedDataDistribution: ShardedDataDistribution = [ + { + ns, + shards: [ + { + shardName: 'rs-shard0-0', + numOrphanedDocs: 0, + numOwnedDocuments: 1, + ownedSizeBytes: 31, + orphanedSizeBytes: 0, + }, + ], + }, + ]; + + const status = await sh.status(); + + expect(status.value.shardedDataDistribution).deep.equals( + expectedShardedDataDistribution + ); + }); + }); + + describe('with < 6.0.3', function () { + skipIfServerVersion(mongos, '>= 6.0.3'); + + it('returns shardedDataDistribution as undefined', async function () { + const status = await sh.status(); + expect(status.value.shardedDataDistribution).equals(undefined); + }); + }); + }); + }); + describe('sharding info', function () { it('returns the status', async function () { const result = await sh.status(); @@ -2887,86 +2967,6 @@ describe('Shard', function () { }); }); - describe('collection.status()', function () { - let db: Database; - - const dbName = 'shard-status-test'; - const ns = `${dbName}.test`; - - beforeEach(async function () { - db = sh._database.getSiblingDB(dbName); - await db.getCollection('test').insertOne({ key: 1 }); - await db.getCollection('test').createIndex({ key: 1 }); - }); - afterEach(async function () { - await db.dropDatabase(); - }); - describe('unsharded collections', function () { - describe('with >= 6.0.3', function () { - skipIfServerVersion(mongos, '< 6.0.3'); - - it('returns shardedDataDistribution as an empty array', async function () { - const status = await sh.status(); - expect(status.value.shardedDataDistribution).deep.equals([]); - }); - }); - - describe('with < 6.0.3', function () { - skipIfServerVersion(mongos, '>= 6.0.3'); - - it('returns shardedDataDistribution as undefined', async function () { - const status = await sh.status(); - expect(status.value.shardedDataDistribution).equals(undefined); - }); - }); - }); - - describe('sharded collections', function () { - beforeEach(async function () { - expect((await sh.enableSharding(dbName)).ok).to.equal(1); - expect( - (await sh.shardCollection(ns, { key: 1 })).collectionsharded - ).to.equal(ns); - }); - - describe('with >= 6.0.3', function () { - skipIfServerVersion(mongos, '< 6.0.3'); - - it('returns correct shardedDataDistribution', async function () { - const expectedShardedDataDistribution: ShardedDataDistribution = [ - { - ns, - shards: [ - { - shardName: 'rs-shard0-0', - numOrphanedDocs: 0, - numOwnedDocuments: 1, - ownedSizeBytes: 31, - orphanedSizeBytes: 0, - }, - ], - }, - ]; - - const status = await sh.status(); - - expect(status.value.shardedDataDistribution).deep.equals( - expectedShardedDataDistribution - ); - }); - }); - - describe('with < 6.0.3', function () { - skipIfServerVersion(mongos, '>= 6.0.3'); - - it('returns shardedDataDistribution as undefined', async function () { - const status = await sh.status(); - expect(status.value.shardedDataDistribution).equals(undefined); - }); - }); - }); - }); - describe('collection.isCapped', function () { it('returns true for config.changelog', async function () { const ret = await sh._database From 7c606db77af307856824bb9f3538a7369cd4dbb6 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 10:38:07 +0200 Subject: [PATCH 11/18] less strict check of shards --- packages/shell-api/src/shard.spec.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index d4a683f388..afdeb7d71a 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -2090,26 +2090,10 @@ describe('Shard', function () { skipIfServerVersion(mongos, '< 6.0.3'); it('returns correct shardedDataDistribution', async function () { - const expectedShardedDataDistribution: ShardedDataDistribution = [ - { - ns, - shards: [ - { - shardName: 'rs-shard0-0', - numOrphanedDocs: 0, - numOwnedDocuments: 1, - ownedSizeBytes: 31, - orphanedSizeBytes: 0, - }, - ], - }, - ]; - const status = await sh.status(); - expect(status.value.shardedDataDistribution).deep.equals( - expectedShardedDataDistribution - ); + expect(status.value.shardedDataDistribution?.length).equals(1); + expect(status.value.shardedDataDistribution?.[0].ns).equals(ns); }); }); From 46adbb20b00ab91f5b7d9d33c040506f650c1d22 Mon Sep 17 00:00:00 2001 From: Gagik Amaryan Date: Thu, 24 Oct 2024 11:20:25 +0200 Subject: [PATCH 12/18] Apply suggestions from code review Co-authored-by: Anna Henningsen --- packages/shell-api/src/helpers.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index 0617660245..2484c8d8c1 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -343,7 +343,7 @@ export async function getPrintableShardStatus( balancerStatus: 1, }); balancerRunning = balancerStatus.inBalancerRound ? 'yes' : 'no'; - } catch (err) { + } catch { // pass, ignore all error messages } balancerRes['Currently running'] = balancerRunning; @@ -370,9 +370,7 @@ export async function getPrintableShardStatus( if (activeLocks?.length > 0) { balancerRes['Collections with active migrations'] = activeLocks.map( (lock) => { - // This type assertion is necessary for the string literal type check - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - return `${lock._id} started at ${lock.when}` as `${string} started at ${string}`; + return `${lock._id} started at ${lock.when}` as const; } ); } From 118574c8e2bc2db6d2b598f65f6dea04b70573c7 Mon Sep 17 00:00:00 2001 From: Gagik Amaryan Date: Thu, 24 Oct 2024 11:42:26 +0200 Subject: [PATCH 13/18] Update packages/shell-api/src/helpers.ts Co-authored-by: Anna Henningsen --- packages/shell-api/src/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index 2484c8d8c1..e79ef11edd 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -674,7 +674,7 @@ export async function getPrintableShardStatus( shardedDataDistribution = (await ( await adminDb.aggregate([{ $shardedDataDistribution: {} }]) ).toArray()) as ShardedDataDistribution; - } catch (e) { + } catch { // Pass, most likely an older version. } From c6f5f86836b78a029e6032d724bb1574b63cf72c Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 12:01:01 +0200 Subject: [PATCH 14/18] Add changes --- .../src/change-stream-cursor.spec.ts | 3 +- packages/shell-api/src/helpers.spec.ts | 40 ++++++++++--------- packages/shell-api/src/helpers.ts | 35 ++++++++-------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/packages/shell-api/src/change-stream-cursor.spec.ts b/packages/shell-api/src/change-stream-cursor.spec.ts index 368f74c597..5a1e327975 100644 --- a/packages/shell-api/src/change-stream-cursor.spec.ts +++ b/packages/shell-api/src/change-stream-cursor.spec.ts @@ -13,7 +13,6 @@ import { } from './enums'; import type { ChangeStream, Document } from '@mongosh/service-provider-core'; import { startTestCluster } from '../../../testing/integration-testing-hooks'; -import { CliServiceProvider } from '../../service-provider-server/lib'; import ShellInstanceState from './shell-instance-state'; import Mongo from './mongo'; import { ensureMaster, ensureResult } from '../test/helpers'; @@ -22,6 +21,7 @@ import type Collection from './collection'; import { MongoshUnimplementedError } from '@mongosh/errors'; import { EventEmitter } from 'events'; import { dummyOptions } from './helpers.spec'; +import CliServiceProvider from '../../service-provider-server/src/cli-service-provider'; describe('ChangeStreamCursor', function () { describe('help', function () { @@ -342,7 +342,6 @@ describe('ChangeStreamCursor', function () { it('isExhausted fails', function () { try { cursor.isExhausted(); - expect.fail('missed exception'); } catch (err: any) { expect(err.name).to.equal('MongoshInvalidInputError'); } diff --git a/packages/shell-api/src/helpers.spec.ts b/packages/shell-api/src/helpers.spec.ts index 8ffa380dc3..cfec25be29 100644 --- a/packages/shell-api/src/helpers.spec.ts +++ b/packages/shell-api/src/helpers.spec.ts @@ -131,7 +131,6 @@ describe('getPrintableShardStatus', function () { const testServer = startSharedTestServer(); let mongo: Mongo; - let db: Database; let configDatabase: Database; let serviceProvider: ServiceProvider; let inBalancerRound = false; @@ -179,20 +178,23 @@ describe('getPrintableShardStatus', function () { getSiblingDB.withArgs('admin').returns(mockedAdminDb); getSiblingDB.withArgs('config').returns(configDatabase); - db = { - _maybeCachedHello: stub().returns({ msg: 'isdbgrid' }), - getSiblingDB, - } as unknown as Database; + configDatabase.getSiblingDB = getSiblingDB; + configDatabase._maybeCachedHello = stub().returns({ msg: 'isdbgrid' }); const origRunCommandWithCheck = serviceProvider.runCommandWithCheck; - serviceProvider.runCommandWithCheck = async (db, cmd) => { + serviceProvider.runCommandWithCheck = async (configDatabase, cmd) => { if (cmd.hello) { return { ok: 1, msg: 'isdbgrid' }; } - if (db === 'admin' && cmd.balancerStatus) { + if (configDatabase === 'admin' && cmd.balancerStatus) { return { ok: 1, inBalancerRound }; } - return origRunCommandWithCheck.call(serviceProvider, db, cmd, {}); + return origRunCommandWithCheck.call( + serviceProvider, + configDatabase, + cmd, + {} + ); }; await Promise.all( @@ -220,7 +222,7 @@ describe('getPrintableShardStatus', function () { }); it('returns an object with sharding information', async function () { - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect(status.shardingVersion.clusterId).to.be.instanceOf(bson.ObjectId); expect(status.shards.map(({ host }: { host: string }) => host)).to.include( 'shard01/localhost:27018,localhost:27019,localhost:27020' @@ -251,7 +253,7 @@ describe('getPrintableShardStatus', function () { 'upgradeState', ]) { it(`does not show ${hiddenField} in shardingVersion`, async function () { - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect((status.shardingVersion as any)[hiddenField]).to.equal( undefined ); @@ -262,19 +264,19 @@ describe('getPrintableShardStatus', function () { it('returns whether the balancer is currently running', async function () { { inBalancerRound = true; - const status = await getPrintableShardStatus(db, true); + const status = await getPrintableShardStatus(configDatabase, true); expect(status.balancer['Currently running']).to.equal('yes'); } { inBalancerRound = false; - const status = await getPrintableShardStatus(db, true); + const status = await getPrintableShardStatus(configDatabase, true); expect(status.balancer['Currently running']).to.equal('no'); } }); it('returns an object with verbose sharding information if requested', async function () { - const status = await getPrintableShardStatus(db, true); + const status = await getPrintableShardStatus(configDatabase, true); expect((status['most recently active mongoses'][0] as any).up).to.be.a( 'number' ); @@ -288,7 +290,7 @@ describe('getPrintableShardStatus', function () { _id: 'balancer', activeWindow: { start: '00:00', stop: '23:59' }, }); - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect(status.balancer['Balancer active window is set between']).to.equal( '00:00 and 23:59 server local time' ); @@ -304,7 +306,7 @@ describe('getPrintableShardStatus', function () { what: 'balancer.round', ns: '', }); - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect( status.balancer['Failed balancer rounds in last 5 attempts'] ).to.equal(1); @@ -318,7 +320,7 @@ describe('getPrintableShardStatus', function () { ts: new bson.ObjectId('5fce116c579db766a198a176'), when: new Date('2020-12-07T11:26:36.803Z'), }); - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect( status.balancer['Collections with active migrations'] ).to.have.lengthOf(1); @@ -333,7 +335,7 @@ describe('getPrintableShardStatus', function () { what: 'moveChunk.from', details: { from: 'shard0', to: 'shard1', note: 'success' }, }); - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect( status.balancer['Migration Results for the last 24 hours'] ).to.deep.equal({ 1: 'Success' }); @@ -345,7 +347,7 @@ describe('getPrintableShardStatus', function () { what: 'moveChunk.from', details: { from: 'shard0', to: 'shard1', errmsg: 'oopsie' }, }); - const status = await getPrintableShardStatus(db, false); + const status = await getPrintableShardStatus(configDatabase, false); expect( status.balancer['Migration Results for the last 24 hours'] @@ -355,7 +357,7 @@ describe('getPrintableShardStatus', function () { it('fails when config.version is empty', async function () { await configDatabase.getCollection('version').drop(); try { - await getPrintableShardStatus(db, false); + await getPrintableShardStatus(configDatabase, false); } catch (err: any) { expect(err.name).to.equal('MongoshInvalidInputError'); return; diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index e79ef11edd..180137a60f 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -224,13 +224,11 @@ export function processDigestPassword( * @param verbose */ export async function getPrintableShardStatus( - db: Database, + configDB: Database, verbose: boolean ): Promise { const result = {} as ShardingStatusResult; - const configDB = await getConfigDB(db); - // configDB is a DB object that contains the sharding metadata of interest. const mongosColl = configDB.getCollection('mongos'); const versionColl = configDB.getCollection('version'); @@ -522,7 +520,7 @@ export async function getPrintableShardStatus( // All databases in config.databases + those implicitly referenced // by a sharded collection in config.collections // (could become a single pipeline using $unionWith when we drop 4.2 server support) - const [databases, collections] = await Promise.all([ + const [databases, collections, shardedDataDistribution] = await Promise.all([ (async () => await (await configDB.getCollection('databases').find()) .sort({ _id: 1 }) @@ -535,7 +533,22 @@ export async function getPrintableShardStatus( ) .sort({ _id: 1 }) .toArray())(), + (async () => { + try { + // $shardedDataDistribution is available since >= 6.0.3 + const adminDB = configDB.getSiblingDB('admin'); + return (await ( + await adminDB.aggregate([{ $shardedDataDistribution: {} }]) + ).toArray()) as ShardedDataDistribution; + } catch { + // Pass, most likely an older version. + return undefined; + } + })(), ]); + + result.shardedDataDistribution = shardedDataDistribution; + // Special case the config db, since it doesn't have a record in config.databases. databases.push({ _id: 'config', primary: 'config', partitioned: true }); @@ -666,20 +679,6 @@ export async function getPrintableShardStatus( ) ).filter((dbEntry) => !!dbEntry); - let shardedDataDistribution: ShardedDataDistribution | undefined; - - try { - // Available since >= 6.0.3 - const adminDb = db.getSiblingDB('admin'); - shardedDataDistribution = (await ( - await adminDb.aggregate([{ $shardedDataDistribution: {} }]) - ).toArray()) as ShardedDataDistribution; - } catch { - // Pass, most likely an older version. - } - - result.shardedDataDistribution = shardedDataDistribution; - delete result.shardingVersion.currentVersion; return result; } From 9bf025111bf43ea709670a34d822aa2ea82efb1b Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 12:10:33 +0200 Subject: [PATCH 15/18] Align with main --- packages/shell-api/src/change-stream-cursor.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shell-api/src/change-stream-cursor.spec.ts b/packages/shell-api/src/change-stream-cursor.spec.ts index 03ceb1cb24..fa6d6e8304 100644 --- a/packages/shell-api/src/change-stream-cursor.spec.ts +++ b/packages/shell-api/src/change-stream-cursor.spec.ts @@ -22,7 +22,6 @@ import type Collection from './collection'; import { MongoshUnimplementedError } from '@mongosh/errors'; import { EventEmitter } from 'events'; import { dummyOptions } from './helpers.spec'; -import CliServiceProvider from '../../service-provider-server/src/cli-service-provider'; describe('ChangeStreamCursor', function () { describe('help', function () { From f25c329f61f2dd7230fab36c0f56711859cbc5b2 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 12:27:02 +0200 Subject: [PATCH 16/18] Fix accidental change --- packages/shell-api/src/change-stream-cursor.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shell-api/src/change-stream-cursor.spec.ts b/packages/shell-api/src/change-stream-cursor.spec.ts index fa6d6e8304..83fd198e08 100644 --- a/packages/shell-api/src/change-stream-cursor.spec.ts +++ b/packages/shell-api/src/change-stream-cursor.spec.ts @@ -342,6 +342,7 @@ describe('ChangeStreamCursor', function () { it('isExhausted fails', function () { try { cursor.isExhausted(); + expect.fail('missed exception'); } catch (err: any) { expect(err.name).to.equal('MongoshInvalidInputError'); } From c4f06b979df00d6dc5b256a368fb33f4d19dc124 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 13:33:01 +0200 Subject: [PATCH 17/18] Only mock for sharding information --- packages/shell-api/src/helpers.spec.ts | 39 +++++++++++--------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/shell-api/src/helpers.spec.ts b/packages/shell-api/src/helpers.spec.ts index 97367eb382..f39227fe02 100644 --- a/packages/shell-api/src/helpers.spec.ts +++ b/packages/shell-api/src/helpers.spec.ts @@ -167,34 +167,15 @@ describe('getPrintableShardStatus', function () { configDatabase = new Database(mongo, 'config_test'); expect(configDatabase.getName()).to.equal('config_test'); - const mockedAdminDb = { - aggregate: stub() - .withArgs([{ $shardedDataDistribution: {} }]) - .resolves({ - toArray: stub().resolves(mockedShardedDataDistribution), - }), - }; - const getSiblingDB = stub(); - getSiblingDB.withArgs('admin').returns(mockedAdminDb); - getSiblingDB.withArgs('config').returns(configDatabase); - - configDatabase.getSiblingDB = getSiblingDB; - configDatabase._maybeCachedHello = stub().returns({ msg: 'isdbgrid' }); - const origRunCommandWithCheck = serviceProvider.runCommandWithCheck; - serviceProvider.runCommandWithCheck = async (configDatabase, cmd) => { + serviceProvider.runCommandWithCheck = async (db, cmd) => { if (cmd.hello) { return { ok: 1, msg: 'isdbgrid' }; } - if (configDatabase === 'admin' && cmd.balancerStatus) { + if (db === 'admin' && cmd.balancerStatus) { return { ok: 1, inBalancerRound }; } - return origRunCommandWithCheck.call( - serviceProvider, - configDatabase, - cmd, - {} - ); + return origRunCommandWithCheck.call(serviceProvider, db, cmd, {}); }; await Promise.all( @@ -222,6 +203,20 @@ describe('getPrintableShardStatus', function () { }); it('returns an object with sharding information', async function () { + const mockedAdminDb = { + aggregate: stub() + .withArgs([{ $shardedDataDistribution: {} }]) + .resolves({ + toArray: stub().resolves(mockedShardedDataDistribution), + }), + }; + const getSiblingDB = stub(); + getSiblingDB.withArgs('admin').returns(mockedAdminDb); + getSiblingDB.withArgs('config').returns(configDatabase); + + configDatabase.getSiblingDB = getSiblingDB; + configDatabase._maybeCachedHello = stub().returns({ msg: 'isdbgrid' }); + const status = await getPrintableShardStatus(configDatabase, false); expect(status.shardingVersion.clusterId).to.be.instanceOf(bson.ObjectId); expect(status.shards.map(({ host }: { host: string }) => host)).to.include( From 178b616d0326e4fccf939530cdaeccacf505b788 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 24 Oct 2024 14:29:12 +0200 Subject: [PATCH 18/18] Fix lint --- packages/shell-api/src/shard.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 2375774691..ad2abce97e 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -32,7 +32,6 @@ import { import Database from './database'; import { inspect } from 'util'; import { dummyOptions } from './helpers.spec'; -import type { ShardedDataDistribution } from './helpers'; describe('Shard', function () { skipIfApiStrict();