Skip to content

Commit efa6c45

Browse files
authored
ref(core): Prevent redundant setup work (#3862)
This fixes two places where we do work unnecessarily: 1) When creating a new `Hub` instance we call `bindClient`, even if there's no client to bind. 2) When calling `Client.setupIntegrations()` (as we do when binding a client to a hub), we run through the whole integration-setup process, even if the client has already had its integrations set up. (The latter situation comes up, among other places, any time we create a domain: each new domain gets its own hub, which then is bound to the current client, meaning up until now we've been calling `setupIntegrations()` for every incoming request handled by either our Express integration or our nextjs SDK.) To prevent this, we now (as of this change) check a) if there's a client to bind to the new hub, and b) if the client having its integrations set up has already had that done.
1 parent 7c538ab commit efa6c45

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

packages/core/src/baseclient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
207207
* Sets up the integrations
208208
*/
209209
public setupIntegrations(): void {
210-
if (this._isEnabled()) {
210+
if (this._isEnabled() && !this._integrations.initialized) {
211211
this._integrations = setupIntegrations(this._options);
212212
}
213213
}

packages/core/src/integration.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { logger } from '@sentry/utils';
55
export const installedIntegrations: string[] = [];
66

77
/** Map of integrations assigned to a client */
8-
export interface IntegrationIndex {
8+
export type IntegrationIndex = {
99
[key: string]: Integration;
10-
}
10+
} & { initialized?: boolean };
1111

1212
/**
1313
* @private
@@ -74,5 +74,9 @@ export function setupIntegrations<O extends Options>(options: O): IntegrationInd
7474
integrations[integration.name] = integration;
7575
setupIntegration(integration);
7676
});
77+
// set the `initialized` flag so we don't run through the process again unecessarily; use `Object.defineProperty`
78+
// because by default it creates a property which is nonenumerable, which we want since `initialized` shouldn't be
79+
// considered a member of the index the way the actual integrations are
80+
Object.defineProperty(integrations, 'initialized', { value: true });
7781
return integrations;
7882
}

packages/core/test/lib/base.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Hub, Scope, Session } from '@sentry/hub';
22
import { Event, Severity, Span } from '@sentry/types';
33
import { logger, SentryError, SyncPromise } from '@sentry/utils';
44

5+
import * as integrationModule from '../../src/integration';
56
import { TestBackend } from '../mocks/backend';
67
import { TestClient } from '../mocks/client';
78
import { TestIntegration } from '../mocks/integration';
@@ -872,6 +873,26 @@ describe('BaseClient', () => {
872873
expect(Object.keys((client as any)._integrations).length).toBe(0);
873874
expect(client.getIntegration(TestIntegration)).toBeFalsy();
874875
});
876+
877+
test('skips installation if integrations are already installed', () => {
878+
expect.assertions(4);
879+
const client = new TestClient({
880+
dsn: PUBLIC_DSN,
881+
integrations: [new TestIntegration()],
882+
});
883+
// note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls
884+
const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations');
885+
886+
// it should install the first time, because integrations aren't yet installed...
887+
client.setupIntegrations();
888+
expect(Object.keys((client as any)._integrations).length).toBe(1);
889+
expect(client.getIntegration(TestIntegration)).toBeTruthy();
890+
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);
891+
892+
// ...but it shouldn't try to install a second time
893+
client.setupIntegrations();
894+
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);
895+
});
875896
});
876897

877898
describe('flush/close', () => {

packages/hub/src/hub.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ export class Hub implements HubInterface {
103103
*/
104104
public constructor(client?: Client, scope: Scope = new Scope(), private readonly _version: number = API_VERSION) {
105105
this.getStackTop().scope = scope;
106-
this.bindClient(client);
106+
if (client) {
107+
this.bindClient(client);
108+
}
107109
}
108110

109111
/**

0 commit comments

Comments
 (0)