diff --git a/packages/data-service/src/connect.spec.ts b/packages/data-service/src/connect.spec.ts index 717cc6ece4c..ca689472dab 100644 --- a/packages/data-service/src/connect.spec.ts +++ b/packages/data-service/src/connect.spec.ts @@ -483,6 +483,63 @@ describe('connect', function () { }); }); + it('connects to sharded with readPreferenceTags', async function () { + const options = envs.getConnectionOptions('sharded'); + /* + See ticket COMPASS-9111 + + This test is using readPreference=nearest because this cluster has node with the tag + ANALYTICS. readPreference=secondary would more closely mirror the + original ticket, but this cluster also has no secondaries so that would + fail regardless of readPreferenceTags. + + Ideally people would use readPreference=secondaryPreferred, but that works + regardless so isn't a good test and if it was the case that people used + that in the first place we'd never need this ticket. + + readPreference=nearest tries to find one that matches the criteria and + since the config server doesn't know about tags the following operations + would hang unless we remove the tags. You can confirm this manually by + hacking maybeOverrideReadPreference in data-service.ts. + */ + const connectionString = + options.connectionString + + '&readPreference=nearest&readPreferenceTags=nodeType:ANALYTICS'; + const connectionOptions = { + ...options, + connectionString, + }; + await testConnection(connectionOptions, { + authenticatedUserRoles: [{ db: 'admin', role: 'root' }], + authenticatedUsers: [{ db: 'admin', user: 'root' }], + }); + + const dataService = await connect({ + connectionOptions, + }); + + /* + Without us removing the read preference tags these operations would fail. + + Normal database operations like find or aggregate will still fail + regardless because the cluster does not have a node with the ANALYTICS + tag, but this test never executes any of those + */ + try { + const databases = await dataService.listDatabases(); + const databaseNames = databases + .map((d) => d.name) + .filter((name) => !['local'].includes(name)); + for (const databaseName of databaseNames) { + // don't really care what's in there, just that the calls succeed + await dataService.listCollections(databaseName); + await dataService.databaseStats(databaseName); + } + } finally { + await dataService.disconnect(); + } + }); + describe('ssh', function () { it('connects with ssh (sshPassword)', async function () { await testConnection(envs.getConnectionOptions('sshPassword'), { diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index 5fc4d1a2498..856a404f436 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -53,6 +53,7 @@ import type { SearchIndexDescription, ReadPreferenceMode, } from 'mongodb'; +import { ReadPreference } from 'mongodb'; import ConnectionStringUrl from 'mongodb-connection-string-url'; import parseNamespace from 'mongodb-ns'; import type { @@ -133,6 +134,25 @@ function isReadPreferenceSet(connectionString: string): boolean { ); } +function readPreferenceWithoutTags(readPreference: ReadPreference) { + return new ReadPreference(readPreference.mode, undefined, readPreference); +} + +function maybeOverrideReadPreference( + isMongos: boolean, + readPreference?: ReadPreference +): { + readPreference?: ReadPreference; +} { + // see ticket COMPASS-9111 for why this is necessary + if (isMongos && readPreference?.tags) { + const newReadPreference = readPreferenceWithoutTags(readPreference); + return { readPreference: newReadPreference }; + } + + return {}; +} + let id = 0; type ClientType = 'CRUD' | 'META'; @@ -1264,7 +1284,13 @@ class DataServiceImpl extends WithLogContext implements DataService { try { const cursor = this._database(databaseName, 'CRUD').listCollections( filter, - { nameOnly } + { + nameOnly, + ...maybeOverrideReadPreference( + this.isMongos(), + this._crudClient?.readPreference + ), + } ); // Iterate instead of using .toArray() so we can emit // collection info update events as they come in. @@ -1399,7 +1425,13 @@ class DataServiceImpl extends WithLogContext implements DataService { } as { listDatabases: 1; }, - { enableUtf8Validation: false } + { + enableUtf8Validation: false, + ...maybeOverrideReadPreference( + this.isMongos(), + this._crudClient?.readPreference + ), + } ); return databases.map((x) => ({ ...x, @@ -2542,7 +2574,13 @@ class DataServiceImpl extends WithLogContext implements DataService { const stats = await runCommand( db, { dbStats: 1 }, - { enableUtf8Validation: false } + { + enableUtf8Validation: false, + ...maybeOverrideReadPreference( + this.isMongos(), + this._crudClient?.readPreference + ), + } ); const normalized = adaptDatabaseInfo(stats); return { name, ...normalized };