diff --git a/test/tools/sdam_viz b/test/tools/sdam_viz new file mode 100755 index 0000000000..ff751ea697 --- /dev/null +++ b/test/tools/sdam_viz @@ -0,0 +1,125 @@ +#!/usr/bin/env node +'use strict'; + +const { MongoClient } = require('../..'); +const arrayStrictEqual = require('../../lib/core/utils').arrayStrictEqual; +const chalk = require('chalk'); + +function print(msg) { + console.log(`${chalk.white(new Date().toISOString())} ${msg}`); +} + +const uri = process.argv[2]; +const client = new MongoClient(uri, { + useNewUrlParser: true, + useUnifiedTopology: true +}); + +async function run() { + print(`connecting to: ${uri}`); + client.on('serverHeartbeatSucceeded', event => + print( + `${chalk.yellow('heartbeat')} ${chalk.green('succeeded')} host: '${ + event.connectionId + }' ${chalk.gray(`(${event.duration} ms)`)}` + ) + ); + + client.on('serverHeartbeatFailed', event => + print( + `${chalk.yellow('heartbeat')} ${chalk.red('failed')} host: '${ + event.connectionId + }' ${chalk.gray(`(${event.duration} ms)`)}` + ) + ); + + // server information + client.on('serverOpening', event => { + print(`${chalk.cyan('server')} [${event.address}] added to topology#${event.topologyId}`); + }); + + client.on('serverClosed', event => { + print(`${chalk.cyan('server')} [${event.address}] removed from topology#${event.topologyId}`); + }); + + client.on('serverDescriptionChanged', event => { + print(`${chalk.cyan('server')} [${event.address}] changed:`); + console.log(serverDescriptionDiff(event.previousDescription, event.newDescription)); + }); + + // topology information + client.on('topologyOpening', event => { + print(`${chalk.magenta('topology')} adding topology#${event.topologyId}`); + }); + + client.on('topologyClosed', event => { + print(`${chalk.magenta('topology')} removing topology#${event.topologyId}`); + }); + + client.on('topologyDescriptionChanged', event => { + const diff = topologyDescriptionDiff(event.previousDescription, event.newDescription); + if (diff !== '') { + print(`${chalk.magenta('topology')} [topology#${event.topologyId}] changed:`); + console.log(diff); + } + }); + + await client.connect(); +} + +function diff(lhs, rhs, fields, comparator) { + return fields.reduce((diff, field) => { + if (lhs[field] == null || rhs[field] == null) { + return diff; + } + + if (!comparator(lhs[field], rhs[field])) { + diff.push( + ` ${field}: ${chalk.green(`[${lhs[field]}]`)} => ${chalk.green(`[${rhs[field]}]`)}` + ); + } + + return diff; + }, []); +} + +function serverDescriptionDiff(lhs, rhs) { + const objectIdFields = ['electionId']; + const arrayFields = ['hosts', 'tags']; + const simpleFields = [ + 'type', + 'minWireVersion', + 'me', + 'setName', + 'setVersion', + 'electionId', + 'primary', + 'logicalSessionTimeoutMinutes' + ]; + + return diff(lhs, rhs, simpleFields, (x, y) => x === y) + .concat(diff(lhs, rhs, arrayFields, (x, y) => arrayStrictEqual(x, y))) + .concat(diff(lhs, rhs, objectIdFields, (x, y) => x.equals(y))) + .join(',\n'); +} + +function topologyDescriptionDiff(lhs, rhs) { + const simpleFields = [ + 'type', + 'setName', + 'maxSetVersion', + 'stale', + 'compatible', + 'compatibilityError', + 'logicalSessionTimeoutMinutes', + 'error', + 'commonWireVersion' + ]; + + return diff(lhs, rhs, simpleFields, (x, y) => x === y).join(',\n'); +} + +run().catch(error => console.log('Caught', error)); +process.on('SIGINT', async function() { + await client.close(); +});