Skip to content

Commit 01e92b0

Browse files
authored
chore(data-service, connections): make sure that instance info is only fetched once on initial connection COMPASS-9549 (#7089)
* chore(data-service, connections): make sure that instance info is only fetched once on initial connection * chore(connections): separate should show and show methods * chore(app-stores): handle ipv6 hosts when building data for instance model; pass instance info to constructor instead of extra .set call * chore(connections): no need for promise.all
1 parent f4a4dbb commit 01e92b0

File tree

3 files changed

+141
-80
lines changed

3 files changed

+141
-80
lines changed

packages/compass-app-stores/src/stores/instance-store.ts

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { MongoDBInstanceProps } from 'mongodb-instance-model';
22
import { MongoDBInstance } from 'mongodb-instance-model';
33
import toNS from 'mongodb-ns';
44
import type {
5+
ConnectionInfo,
56
ConnectionsService,
67
DataService,
78
} from '@mongodb-js/compass-connections/provider';
@@ -14,6 +15,8 @@ import { openToast } from '@mongodb-js/compass-components';
1415
import { MongoDBInstancesManager } from '../instances-manager';
1516
import type { PreferencesAccess } from 'compass-preferences-model';
1617

18+
type InstanceDetails = Awaited<ReturnType<DataService['instance']>>;
19+
1720
function serversArray(
1821
serversMap: NonNullable<
1922
ReturnType<DataService['getLastSeenTopology']>
@@ -305,55 +308,78 @@ export function createInstancesStore(
305308
instancesManager.removeMongoDBInstanceForConnection(connectionInfoId);
306309
});
307310

308-
on(connections, 'connected', function (instanceConnectionId: string) {
309-
const dataService =
310-
connections.getDataServiceForConnection(instanceConnectionId);
311-
const connectionString = dataService.getConnectionString();
312-
const firstHost = connectionString.hosts[0] || '';
313-
const [hostname, port] = firstHost.split(':');
314-
315-
const initialInstanceProps: Partial<MongoDBInstanceProps> = {
316-
_id: firstHost,
317-
hostname: hostname,
318-
port: port ? +port : undefined,
319-
topologyDescription: getTopologyDescription(
320-
dataService.getLastSeenTopology()
321-
),
322-
preferences,
323-
};
324-
const instance = instancesManager.createMongoDBInstanceForConnection(
325-
instanceConnectionId,
326-
initialInstanceProps as MongoDBInstanceProps
327-
);
311+
on(
312+
connections,
313+
'connected',
314+
function (
315+
instanceConnectionId: string,
316+
_connectionInfo: ConnectionInfo,
317+
instanceInfo: InstanceDetails
318+
) {
319+
const dataService =
320+
connections.getDataServiceForConnection(instanceConnectionId);
321+
const connectionString = dataService.getConnectionString();
322+
const firstHost = connectionString.hosts[0] || '';
323+
const [hostname, port] = (() => {
324+
if (firstHost.startsWith('[')) {
325+
return firstHost.slice(1).split(']'); // IPv6
326+
}
327+
return firstHost.split(':');
328+
})();
329+
330+
const initialInstanceProps: Partial<MongoDBInstanceProps> = {
331+
// We pre-fetched instance info and so can right away construct it in a
332+
// "ready" state
333+
...(instanceInfo as Partial<MongoDBInstanceProps>),
334+
status: 'ready',
335+
statusError: null,
336+
337+
// Required initial values that are not returned with instance info
338+
_id: firstHost,
339+
hostname: hostname,
340+
port: port ? +port : undefined,
341+
topologyDescription: getTopologyDescription(
342+
dataService.getLastSeenTopology()
343+
),
344+
345+
// Service injection for preferences (currently only controls namespace
346+
// stats fetching)
347+
preferences,
348+
};
349+
const instance = instancesManager.createMongoDBInstanceForConnection(
350+
instanceConnectionId,
351+
initialInstanceProps as MongoDBInstanceProps
352+
);
328353

329-
addCleanup(() => {
330-
instance.removeAllListeners();
331-
});
354+
addCleanup(() => {
355+
instance.removeAllListeners();
356+
});
332357

333-
void refreshInstance(
334-
{
335-
fetchDatabases: true,
336-
fetchDbStats: true,
337-
},
338-
{
339-
connectionId: instanceConnectionId,
340-
}
341-
);
358+
void refreshInstance(
359+
{
360+
fetchDatabases: true,
361+
fetchDbStats: true,
362+
},
363+
{
364+
connectionId: instanceConnectionId,
365+
}
366+
);
342367

343-
on(
344-
dataService,
345-
'topologyDescriptionChanged',
346-
({
347-
newDescription,
348-
}: {
349-
newDescription: ReturnType<DataService['getLastSeenTopology']>;
350-
}) => {
351-
instance.set({
352-
topologyDescription: getTopologyDescription(newDescription),
353-
});
354-
}
355-
);
356-
});
368+
on(
369+
dataService,
370+
'topologyDescriptionChanged',
371+
({
372+
newDescription,
373+
}: {
374+
newDescription: ReturnType<DataService['getLastSeenTopology']>;
375+
}) => {
376+
instance.set({
377+
topologyDescription: getTopologyDescription(newDescription),
378+
});
379+
}
380+
);
381+
}
382+
);
357383

358384
on(
359385
globalAppRegistry,

packages/compass-connections/src/stores/connections-store-redux.ts

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
ConnectionAttempt,
1717
ConnectionOptions,
1818
DataService,
19+
InstanceDetails,
1920
} from 'mongodb-data-service';
2021
import { createConnectionAttempt } from 'mongodb-data-service';
2122
import { UUID } from 'bson';
@@ -43,7 +44,8 @@ import type { ImportConnectionOptions } from '@mongodb-js/connection-storage/pro
4344
export type ConnectionsEventMap = {
4445
connected: (
4546
connectionId: ConnectionId,
46-
connectionInfo: ConnectionInfo
47+
connectionInfo: ConnectionInfo,
48+
instanceInfo: InstanceDetails
4749
) => void;
4850
disconnected: (
4951
connectionId: ConnectionId,
@@ -1658,6 +1660,14 @@ const connectWithOptions = (
16581660
return;
16591661
}
16601662

1663+
// We're trying to optimise the initial Compass loading times here: to
1664+
// make sure that the driver connection pool doesn't immediately get
1665+
// overwhelmed with requests, we fetch instance info only once and then
1666+
// pass it down to telemetry and instance model. This is a relatively
1667+
// expensive dataService operation so we're trying to keep the usage
1668+
// very limited
1669+
const instanceInfo = await dataService.instance();
1670+
16611671
let showedNonRetryableErrorToast = false;
16621672
// Listen for non-retry-able errors on failed server heartbeats.
16631673
// These can happen on compass web when:
@@ -1762,13 +1772,17 @@ const connectWithOptions = (
17621772
track(
17631773
'New Connection',
17641774
async () => {
1765-
const [
1766-
{ dataLake, genuineMongoDB, host, build, isAtlas, isLocalAtlas },
1767-
[extraInfo, resolvedHostname],
1768-
] = await Promise.all([
1769-
dataService.instance(),
1770-
getExtraConnectionData(connectionInfo),
1771-
]);
1775+
const {
1776+
dataLake,
1777+
genuineMongoDB,
1778+
host,
1779+
build,
1780+
isAtlas,
1781+
isLocalAtlas,
1782+
} = instanceInfo;
1783+
const [extraInfo, resolvedHostname] = await getExtraConnectionData(
1784+
connectionInfo
1785+
);
17721786

17731787
const connections = getState().connections;
17741788
// Counting all connections, we need to filter out any connections currently being created
@@ -1811,44 +1825,33 @@ const connectWithOptions = (
18111825
connectionsEventEmitter.emit(
18121826
'connected',
18131827
connectionInfo.id,
1814-
connectionInfo
1828+
connectionInfo,
1829+
instanceInfo
18151830
);
18161831

18171832
dispatch({
18181833
type: ActionTypes.ConnectionAttemptSuccess,
18191834
connectionId: connectionInfo.id,
18201835
});
18211836

1822-
const { networkTraffic, showEndOfLifeConnectionModal } =
1823-
preferences.getPreferences();
1824-
18251837
if (
18261838
getGenuineMongoDB(connectionInfo.connectionOptions.connectionString)
18271839
.isGenuine === false
18281840
) {
18291841
dispatch(showNonGenuineMongoDBWarningModal(connectionInfo.id));
1830-
} else if (showEndOfLifeConnectionModal) {
1831-
void dataService
1832-
.instance()
1833-
.then(async (instance) => {
1834-
const { version } = instance.build;
1835-
const latestEndOfLifeServerVersion =
1836-
await getLatestEndOfLifeServerVersion(networkTraffic);
1837-
if (isEndOfLifeVersion(version, latestEndOfLifeServerVersion)) {
1838-
dispatch(
1839-
showEndOfLifeMongoDBWarningModal(
1840-
connectionInfo.id,
1841-
instance.build.version
1842-
)
1843-
);
1844-
}
1845-
})
1846-
.catch((err) => {
1847-
debug(
1848-
'failed to get instance details to determine if the server version is end-of-life',
1849-
err
1850-
);
1851-
});
1842+
} else if (
1843+
await shouldShowEndOfLifeWarning(
1844+
instanceInfo.build.version,
1845+
preferences,
1846+
debug
1847+
)
1848+
) {
1849+
dispatch(
1850+
showEndOfLifeMongoDBWarningModal(
1851+
connectionInfo.id,
1852+
instanceInfo.build.version
1853+
)
1854+
);
18521855
}
18531856
} catch (err) {
18541857
dispatch(connectionAttemptError(connectionInfo, err));
@@ -2173,6 +2176,30 @@ export const showNonGenuineMongoDBWarningModal = (
21732176
};
21742177
};
21752178

2179+
async function shouldShowEndOfLifeWarning(
2180+
serverVersion: string,
2181+
preferences: PreferencesAccess,
2182+
debug: Logger['debug']
2183+
) {
2184+
try {
2185+
const { showEndOfLifeConnectionModal, networkTraffic } =
2186+
preferences.getPreferences();
2187+
if (!showEndOfLifeConnectionModal) {
2188+
return;
2189+
}
2190+
const latestEndOfLifeServerVersion = await getLatestEndOfLifeServerVersion(
2191+
networkTraffic
2192+
);
2193+
return isEndOfLifeVersion(serverVersion, latestEndOfLifeServerVersion);
2194+
} catch (err) {
2195+
debug(
2196+
'failed to get instance details to determine if the server version is end-of-life',
2197+
err
2198+
);
2199+
return false;
2200+
}
2201+
}
2202+
21762203
export const showEndOfLifeMongoDBWarningModal = (
21772204
connectionId: string,
21782205
version: string

packages/data-service/src/data-service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ export interface DataService {
310310

311311
/**
312312
* Get the current instance details.
313+
*
314+
* @deprecated avoid using `instance` directly and use `InstanceModel` instead
313315
*/
314316
instance(): Promise<InstanceDetails>;
315317

@@ -340,6 +342,9 @@ export interface DataService {
340342

341343
/**
342344
* List all collections for a database.
345+
*
346+
* @deprecated avoid using `listCollections` directly and use
347+
* `CollectionModel` instead
343348
*/
344349
listCollections(
345350
databaseName: string,
@@ -448,6 +453,9 @@ export interface DataService {
448453

449454
/**
450455
* List all databases on the currently connected instance.
456+
*
457+
* @deprecated avoid using `listDatabases` directly and use `DatabaseModel`
458+
* instead
451459
*/
452460
listDatabases(options?: {
453461
nameOnly?: true;

0 commit comments

Comments
 (0)