Skip to content

Commit d082248

Browse files
committed
WIP: test if shardDistribution works
1 parent 249fd54 commit d082248

File tree

2 files changed

+126
-25
lines changed

2 files changed

+126
-25
lines changed

packages/shell-api/src/helpers.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,9 @@ describe('getPrintableShardStatus', function () {
214214
]) {
215215
it(`does not show ${hiddenField} in shardingVersion`, async function () {
216216
const status = await getPrintableShardStatus(configDatabase, false);
217-
expect(status.shardingVersion[hiddenField]).to.equal(undefined);
217+
expect((status.shardingVersion as any)[hiddenField]).to.equal(
218+
undefined
219+
);
218220
});
219221
}
220222
});
@@ -235,8 +237,10 @@ describe('getPrintableShardStatus', function () {
235237

236238
it('returns an object with verbose sharding information if requested', async function () {
237239
const status = await getPrintableShardStatus(configDatabase, true);
238-
expect(status['most recently active mongoses'][0].up).to.be.a('number');
239-
expect(status['most recently active mongoses'][0].waiting).to.be.a(
240+
expect((status['most recently active mongoses'][0] as any).up).to.be.a(
241+
'number'
242+
);
243+
expect((status['most recently active mongoses'][0] as any).waiting).to.be.a(
240244
'boolean'
241245
);
242246
});
@@ -281,7 +285,7 @@ describe('getPrintableShardStatus', function () {
281285
status.balancer['Collections with active migrations']
282286
).to.have.lengthOf(1);
283287
expect(
284-
status.balancer['Collections with active migrations'].join('')
288+
status.balancer['Collections with active migrations']?.join('')
285289
).to.include('asdf');
286290
});
287291

packages/shell-api/src/helpers.ts

Lines changed: 118 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type {
2626
bson,
2727
} from '@mongosh/service-provider-core';
2828
import type { ClientSideFieldLevelEncryptionOptions } from './field-level-encryption';
29-
import { type AutoEncryptionOptions } from 'mongodb';
29+
import type { AutoEncryptionOptions, Long, ObjectId, Timestamp } from 'mongodb';
3030
import { shellApiType } from './enums';
3131
import type { AbstractCursor } from './abstract-cursor';
3232
import type ChangeStreamCursor from './change-stream-cursor';
@@ -224,10 +224,12 @@ export function processDigestPassword(
224224
* @param verbose
225225
*/
226226
export async function getPrintableShardStatus(
227-
configDB: Database,
227+
db: Database,
228228
verbose: boolean
229-
): Promise<Document> {
230-
const result = {} as any;
229+
): Promise<ShardingStatusResult> {
230+
const result = {} as ShardingStatusResult;
231+
232+
const configDB = await getConfigDB(db);
231233

232234
// configDB is a DB object that contains the sharding metadata of interest.
233235
const mongosColl = configDB.getCollection('mongos');
@@ -259,9 +261,12 @@ export async function getPrintableShardStatus(
259261
);
260262
}
261263

262-
result.shardingVersion = version;
264+
result.shardingVersion = version as {
265+
_id: number;
266+
clusterId: ObjectId;
267+
};
263268

264-
result.shards = shards;
269+
result.shards = shards as ShardingStatusResult['shards'];
265270

266271
// (most recently) active mongoses
267272
const mongosActiveThresholdMs = 60000;
@@ -280,9 +285,8 @@ export async function getPrintableShardStatus(
280285
}
281286
}
282287

283-
mongosAdjective = `${mongosAdjective} mongoses`;
284288
if (mostRecentMongosTime === null) {
285-
result[mongosAdjective] = 'none';
289+
result[`${mongosAdjective} mongoses`] = 'none';
286290
} else {
287291
const recentMongosQuery = {
288292
ping: {
@@ -295,25 +299,27 @@ export async function getPrintableShardStatus(
295299
};
296300

297301
if (verbose) {
298-
result[mongosAdjective] = await (await mongosColl.find(recentMongosQuery))
302+
result[`${mongosAdjective} mongoses`] = await (
303+
await mongosColl.find(recentMongosQuery)
304+
)
299305
.sort({ ping: -1 })
300306
.toArray();
301307
} else {
302-
result[mongosAdjective] = (
308+
result[`${mongosAdjective} mongoses`] = (
303309
(await (
304310
await mongosColl.aggregate([
305311
{ $match: recentMongosQuery },
306312
{ $group: { _id: '$mongoVersion', num: { $sum: 1 } } },
307313
{ $sort: { num: -1 } },
308314
])
309-
).toArray()) as any[]
315+
).toArray()) as { _id: string; num: number }[]
310316
).map((z: { _id: string; num: number }) => {
311317
return { [z._id]: z.num };
312318
});
313319
}
314320
}
315321

316-
const balancerRes: Record<string, any> = {};
322+
const balancerRes = {} as ShardingStatusResult['balancer'];
317323
await Promise.all([
318324
(async (): Promise<void> => {
319325
// Is autosplit currently enabled
@@ -331,13 +337,13 @@ export async function getPrintableShardStatus(
331337
})(),
332338
(async (): Promise<void> => {
333339
// Is the balancer currently active
334-
let balancerRunning = 'unknown';
340+
let balancerRunning: 'yes' | 'no' | 'unknown' = 'unknown';
335341
try {
336342
const balancerStatus = await configDB.adminCommand({
337343
balancerStatus: 1,
338344
});
339345
balancerRunning = balancerStatus.inBalancerRound ? 'yes' : 'no';
340-
} catch (err: any) {
346+
} catch (err) {
341347
// pass, ignore all error messages
342348
}
343349
balancerRes['Currently running'] = balancerRunning;
@@ -364,7 +370,9 @@ export async function getPrintableShardStatus(
364370
if (activeLocks?.length > 0) {
365371
balancerRes['Collections with active migrations'] = activeLocks.map(
366372
(lock) => {
367-
return `${lock._id} started at ${lock.when}`;
373+
// This type assertion is necessary for the string literal type check
374+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
375+
return `${lock._id} started at ${lock.when}` as `${string} started at ${string}`;
368376
}
369377
);
370378
}
@@ -418,8 +426,23 @@ export async function getPrintableShardStatus(
418426
const yesterday = new Date();
419427
yesterday.setDate(yesterday.getDate() - 1);
420428

429+
type MigrationResult =
430+
| {
431+
_id: 'Success';
432+
count: number;
433+
from: never;
434+
to: never;
435+
}
436+
// Failed migration
437+
| {
438+
_id: string;
439+
count: number;
440+
from: string;
441+
to: string;
442+
};
443+
421444
// Successful migrations.
422-
let migrations = await (
445+
let migrations = (await (
423446
await changelogColl.aggregate([
424447
{
425448
$match: {
@@ -437,11 +460,11 @@ export async function getPrintableShardStatus(
437460
},
438461
},
439462
])
440-
).toArray();
463+
).toArray()) as MigrationResult[];
441464

442465
// Failed migrations.
443466
migrations = migrations.concat(
444-
await (
467+
(await (
445468
await changelogColl.aggregate([
446469
{
447470
$match: {
@@ -472,11 +495,12 @@ export async function getPrintableShardStatus(
472495
},
473496
},
474497
])
475-
).toArray()
498+
).toArray()) as MigrationResult[]
476499
);
477500

478-
const migrationsRes: Record<number, string> = {};
479-
migrations.forEach((x: any) => {
501+
const migrationsRes: ShardingStatusResult['balancer']['Migration Results for the last 24 hours'] =
502+
{};
503+
migrations.forEach((x) => {
480504
if (x._id === 'Success') {
481505
migrationsRes[x.count] = x._id;
482506
} else {
@@ -644,10 +668,83 @@ export async function getPrintableShardStatus(
644668
)
645669
).filter((dbEntry) => !!dbEntry);
646670

671+
let shardedDataDistribution: ShardedDataDistribution | undefined;
672+
673+
try {
674+
// Available since 6.0.3
675+
const adminDb = db.getSiblingDB('admin');
676+
shardedDataDistribution = (await (
677+
await adminDb.aggregate([{ $shardedDataDistribution: {} }])
678+
).toArray()) as ShardedDataDistribution;
679+
} catch (e) {
680+
// Pass, most likely an older version.
681+
console.error('shardedDataDistribution Errored:', e);
682+
}
683+
684+
result.shardedDataDistribution = shardedDataDistribution;
685+
647686
delete result.shardingVersion.currentVersion;
648687
return result;
649688
}
650689

690+
type ShardingStatusResult = {
691+
shardingVersion: {
692+
_id: number;
693+
clusterId: ObjectId;
694+
/** This gets deleted when it is returned from getPrintableShardStatus */
695+
currentVersion?: number;
696+
};
697+
shards: {
698+
_id: string;
699+
host: string;
700+
state: number;
701+
topologyTime: Timestamp;
702+
replSetConfigVersion: Long;
703+
}[];
704+
[mongoses: `${string} mongoses`]:
705+
| 'none'
706+
| {
707+
[version: string]:
708+
| number
709+
| {
710+
up: number;
711+
waiting: boolean;
712+
};
713+
}[];
714+
autosplit: {
715+
'Currently enabled': 'yes' | 'no';
716+
};
717+
balancer: {
718+
'Currently enabled': 'yes' | 'no';
719+
'Currently running': 'yes' | 'no' | 'unknown';
720+
'Failed balancer rounds in last 5 attempts': number;
721+
'Migration Results for the last 24 hours':
722+
| 'No recent migrations'
723+
| {
724+
[count: number]:
725+
| 'Success'
726+
| `Failed with error '${string}', from ${string} to ${string}`;
727+
};
728+
'Balancer active window is set between'?: `${string} and ${string} server local time`;
729+
'Last reported error'?: string;
730+
'Time of Reported error'?: string;
731+
'Collections with active migrations'?: `${string} started at ${string}`[];
732+
};
733+
shardedDataDistribution?: ShardedDataDistribution;
734+
databases: { database: Document; collections: Document }[];
735+
};
736+
737+
type ShardedDataDistribution = {
738+
ns: string;
739+
shards: {
740+
shardName: string;
741+
numOrphanedDocs: number;
742+
numOwnedDocuments: number;
743+
orphanedSizeBytes: number;
744+
ownedSizeBytes: number;
745+
};
746+
}[];
747+
651748
export async function getConfigDB(db: Database): Promise<Database> {
652749
const helloResult = await db._maybeCachedHello();
653750
if (helloResult.msg !== 'isdbgrid') {

0 commit comments

Comments
 (0)