From ea04b0562f8455aa90ec637634d90681497c3630 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 9 Feb 2026 21:19:38 +0100 Subject: [PATCH] Prevent SDK from disposing withit debounce period (#18775) --- .../services/sdk/default-sdk.service.spec.ts | 35 +++++++++++++++++-- .../services/sdk/default-sdk.service.ts | 8 ++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts index fb9c1fae77e..2a1a3497887 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts @@ -143,17 +143,44 @@ describe("DefaultSdkService", () => { }); it("destroys the internal SDK client when all subscriptions are closed", async () => { + jest.useFakeTimers(); const subject_1 = new BehaviorSubject | undefined>(undefined); const subject_2 = new BehaviorSubject | undefined>(undefined); const subscription_1 = service.userClient$(userId).subscribe(subject_1); const subscription_2 = service.userClient$(userId).subscribe(subject_2); - await new Promise(process.nextTick); + await jest.advanceTimersByTimeAsync(0); subscription_1.unsubscribe(); subscription_2.unsubscribe(); - await new Promise(process.nextTick); + await jest.advanceTimersByTimeAsync(0); + expect(mockClient.free).not.toHaveBeenCalled(); + + await jest.advanceTimersByTimeAsync(1000); expect(mockClient.free).toHaveBeenCalledTimes(1); + jest.useRealTimers(); + }); + + it("does not destroy the internal SDK client if resubscribed within 1 second", async () => { + jest.useFakeTimers(); + const subject_1 = new BehaviorSubject | undefined>(undefined); + const subscription_1 = service.userClient$(userId).subscribe(subject_1); + await jest.advanceTimersByTimeAsync(0); + + subscription_1.unsubscribe(); + await jest.advanceTimersByTimeAsync(500); + expect(mockClient.free).not.toHaveBeenCalled(); + + // Resubscribe before the 1 second delay + const subject_2 = new BehaviorSubject | undefined>(undefined); + const subscription_2 = service.userClient$(userId).subscribe(subject_2); + await jest.advanceTimersByTimeAsync(1000); + + // Client should not be freed since we resubscribed + expect(mockClient.free).not.toHaveBeenCalled(); + expect(sdkClientFactory.createSdkClient).toHaveBeenCalledTimes(1); + subscription_2.unsubscribe(); + jest.useRealTimers(); }); it("destroys the internal SDK client when the userKey is unset (i.e. lock or logout)", async () => { @@ -218,6 +245,7 @@ describe("DefaultSdkService", () => { }); it("destroys the internal client when an override is set", async () => { + jest.useFakeTimers(); const mockInternalClient = createMockClient(); const mockOverrideClient = createMockClient(); sdkClientFactory.createSdkClient.mockResolvedValue(mockInternalClient); @@ -227,7 +255,10 @@ describe("DefaultSdkService", () => { service.setClient(userId, mockOverrideClient); await userClientTracker.pauseUntilReceived(2); + expect(mockInternalClient.free).not.toHaveBeenCalled(); + await jest.advanceTimersByTimeAsync(1000); expect(mockInternalClient.free).toHaveBeenCalled(); + jest.useRealTimers(); }); it("destroys the override client when explicitly setting the client to undefined", async () => { diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index e2c9c77e204..e5104f3c68d 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -2,7 +2,10 @@ import { combineLatest, concatMap, Observable, + share, shareReplay, + ReplaySubject, + timer, map, distinctUntilChanged, tap, @@ -263,7 +266,10 @@ export class DefaultSdkService implements SdkService { }, ), tap({ finalize: () => this.sdkClientCache.delete(userId) }), - shareReplay({ refCount: true, bufferSize: 1 }), + share({ + connector: () => new ReplaySubject(1), + resetOnRefCountZero: () => timer(1000), + }), ); this.sdkClientCache.set(userId, client$);