Skip to content

Commit

Permalink
Support restart of FirestoreClient (#8430)
Browse files Browse the repository at this point in the history
* Support restart of FirestoreClient

* Fix errors

* Pretty

* Fix

* lint

* pretty

* fix

* undo not required changes

* Only appy required test changes

* Apply suggestions from Andy

* Fixes from PR review
  • Loading branch information
tom-andersen committed Sep 20, 2024
1 parent 2561b63 commit 47b0913
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 202 deletions.
5 changes: 5 additions & 0 deletions .changeset/small-geckos-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@firebase/firestore': patch
---

Refactor Firestore client instantiation. This prepares for future features that require client to restart.
83 changes: 45 additions & 38 deletions packages/firestore/src/api/cache_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
LruGcMemoryOfflineComponentProvider,
MemoryOfflineComponentProvider,
MultiTabOfflineComponentProvider,
OfflineComponentProvider,
OfflineComponentProviderFactory,
OnlineComponentProviderFactory,
OnlineComponentProvider
} from '../core/component_provider';

Expand All @@ -38,31 +39,31 @@ export type MemoryLocalCache = {
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
_onlineComponentProvider: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;
};

class MemoryLocalCacheImpl implements MemoryLocalCache {
kind: 'memory' = 'memory';
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
_onlineComponentProvider: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;

constructor(settings?: MemoryCacheSettings) {
this._onlineComponentProvider = new OnlineComponentProvider();
this._onlineComponentProvider = OnlineComponentProvider.provider;
if (settings?.garbageCollector) {
this._offlineComponentProvider =
settings.garbageCollector._offlineComponentProvider;
} else {
this._offlineComponentProvider = new MemoryOfflineComponentProvider();
this._offlineComponentProvider = MemoryOfflineComponentProvider.provider;
}
}

Expand All @@ -83,23 +84,23 @@ export type PersistentLocalCache = {
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
_onlineComponentProvider: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider: OfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;
};

class PersistentLocalCacheImpl implements PersistentLocalCache {
kind: 'persistent' = 'persistent';
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
_onlineComponentProvider: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider: OfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;

constructor(settings: PersistentCacheSettings | undefined) {
let tabManager: PersistentTabManager;
Expand Down Expand Up @@ -147,7 +148,7 @@ export type MemoryEagerGarbageCollector = {
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;
};

/**
Expand All @@ -167,18 +168,18 @@ export type MemoryLruGarbageCollector = {
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;
};

class MemoryEagerGarbageCollectorImpl implements MemoryEagerGarbageCollector {
kind: 'memoryEager' = 'memoryEager';
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;

constructor() {
this._offlineComponentProvider = new MemoryOfflineComponentProvider();
this._offlineComponentProvider = MemoryOfflineComponentProvider.provider;
}

toJSON(): {} {
Expand All @@ -191,12 +192,12 @@ class MemoryLruGarbageCollectorImpl implements MemoryLruGarbageCollector {
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
_offlineComponentProvider: OfflineComponentProviderFactory;

constructor(cacheSize?: number) {
this._offlineComponentProvider = new LruGcMemoryOfflineComponentProvider(
cacheSize
);
this._offlineComponentProvider = {
build: () => new LruGcMemoryOfflineComponentProvider(cacheSize)
};
}

toJSON(): {} {
Expand Down Expand Up @@ -297,11 +298,11 @@ export type PersistentSingleTabManager = {
/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
_onlineComponentProvider?: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider?: OfflineComponentProvider;
_offlineComponentProvider?: OfflineComponentProviderFactory;
};

class SingleTabManagerImpl implements PersistentSingleTabManager {
Expand All @@ -310,11 +311,11 @@ class SingleTabManagerImpl implements PersistentSingleTabManager {
/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
_onlineComponentProvider?: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider?: OfflineComponentProvider;
_offlineComponentProvider?: OfflineComponentProviderFactory;

constructor(private forceOwnership?: boolean) {}

Expand All @@ -328,12 +329,15 @@ class SingleTabManagerImpl implements PersistentSingleTabManager {
_initialize(
settings: Omit<PersistentCacheSettings, 'tabManager'> | undefined
): void {
this._onlineComponentProvider = new OnlineComponentProvider();
this._offlineComponentProvider = new IndexedDbOfflineComponentProvider(
this._onlineComponentProvider,
settings?.cacheSizeBytes,
this.forceOwnership
);
this._onlineComponentProvider = OnlineComponentProvider.provider;
this._offlineComponentProvider = {
build: (onlineComponents: OnlineComponentProvider) =>
new IndexedDbOfflineComponentProvider(
onlineComponents,
settings?.cacheSizeBytes,
this.forceOwnership
)
};
}
}

Expand All @@ -350,12 +354,12 @@ export type PersistentMultipleTabManager = {
/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
_onlineComponentProvider?: OnlineComponentProviderFactory;
/**
* @internal
*/

_offlineComponentProvider?: OfflineComponentProvider;
_offlineComponentProvider?: OfflineComponentProviderFactory;
};

class MultiTabManagerImpl implements PersistentMultipleTabManager {
Expand All @@ -364,11 +368,11 @@ class MultiTabManagerImpl implements PersistentMultipleTabManager {
/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
_onlineComponentProvider?: OnlineComponentProviderFactory;
/**
* @internal
*/
_offlineComponentProvider?: OfflineComponentProvider;
_offlineComponentProvider?: OfflineComponentProviderFactory;

toJSON(): {} {
return { kind: this.kind };
Expand All @@ -380,11 +384,14 @@ class MultiTabManagerImpl implements PersistentMultipleTabManager {
_initialize(
settings: Omit<PersistentCacheSettings, 'tabManager'> | undefined
): void {
this._onlineComponentProvider = new OnlineComponentProvider();
this._offlineComponentProvider = new MultiTabOfflineComponentProvider(
this._onlineComponentProvider,
settings?.cacheSizeBytes
);
this._onlineComponentProvider = OnlineComponentProvider.provider;
this._offlineComponentProvider = {
build: (onlineComponents: OnlineComponentProvider) =>
new MultiTabOfflineComponentProvider(
onlineComponents,
settings?.cacheSizeBytes
)
};
}
}

Expand Down
33 changes: 24 additions & 9 deletions packages/firestore/src/api/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export class FirebaseAuthCredentialsProvider
* The auth token listener registered with FirebaseApp, retained here so we
* can unregister it.
*/
private tokenListener!: () => void;
private tokenListener: (() => void) | undefined;

/** Tracks the current User. */
private currentUser: User = User.UNAUTHENTICATED;
Expand All @@ -256,6 +256,10 @@ export class FirebaseAuthCredentialsProvider
asyncQueue: AsyncQueue,
changeListener: CredentialChangeListener<User>
): void {
hardAssert(
this.tokenListener === undefined,
'Token listener already added'
);
let lastTokenId = this.tokenCounter;

// A change listener that prevents double-firing for the same token change.
Expand Down Expand Up @@ -293,8 +297,10 @@ export class FirebaseAuthCredentialsProvider
const registerAuth = (auth: FirebaseAuthInternal): void => {
logDebug('FirebaseAuthCredentialsProvider', 'Auth detected');
this.auth = auth;
this.auth.addAuthTokenListener(this.tokenListener);
awaitNextToken();
if (this.tokenListener) {
this.auth.addAuthTokenListener(this.tokenListener);
awaitNextToken();
}
};

this.authProvider.onInit(auth => registerAuth(auth));
Expand Down Expand Up @@ -365,9 +371,10 @@ export class FirebaseAuthCredentialsProvider
}

shutdown(): void {
if (this.auth) {
this.auth.removeAuthTokenListener(this.tokenListener!);
if (this.auth && this.tokenListener) {
this.auth.removeAuthTokenListener(this.tokenListener);
}
this.tokenListener = undefined;
}

// Auth.getUid() can return null even with a user logged in. It is because
Expand Down Expand Up @@ -484,7 +491,7 @@ export class FirebaseAppCheckTokenProvider
* The AppCheck token listener registered with FirebaseApp, retained here so
* we can unregister it.
*/
private tokenListener!: AppCheckTokenListener;
private tokenListener: AppCheckTokenListener | undefined;
private forceRefresh = false;
private appCheck: FirebaseAppCheckInternal | null = null;
private latestAppCheckToken: string | null = null;
Expand All @@ -497,6 +504,11 @@ export class FirebaseAppCheckTokenProvider
asyncQueue: AsyncQueue,
changeListener: CredentialChangeListener<string>
): void {
hardAssert(
this.tokenListener === undefined,
'Token listener already added'
);

const onTokenChanged: (
tokenResult: AppCheckTokenResult
) => Promise<void> = tokenResult => {
Expand Down Expand Up @@ -524,7 +536,9 @@ export class FirebaseAppCheckTokenProvider
const registerAppCheck = (appCheck: FirebaseAppCheckInternal): void => {
logDebug('FirebaseAppCheckTokenProvider', 'AppCheck detected');
this.appCheck = appCheck;
this.appCheck.addTokenListener(this.tokenListener);
if (this.tokenListener) {
this.appCheck.addTokenListener(this.tokenListener);
}
};

this.appCheckProvider.onInit(appCheck => registerAppCheck(appCheck));
Expand Down Expand Up @@ -579,9 +593,10 @@ export class FirebaseAppCheckTokenProvider
}

shutdown(): void {
if (this.appCheck) {
this.appCheck.removeTokenListener(this.tokenListener!);
if (this.appCheck && this.tokenListener) {
this.appCheck.removeTokenListener(this.tokenListener);
}
this.tokenListener = undefined;
}
}

Expand Down
Loading

0 comments on commit 47b0913

Please sign in to comment.