Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New way to config Firestore SDK Cache. #7015

Merged
merged 25 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/brown-beers-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@firebase/firestore": minor
"firebase-repo-scripts-prune-dts": minor
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
---

Introduces a new way to config Firestore SDK Cache.
54 changes: 54 additions & 0 deletions common/api-review/firestore.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,12 @@ export class FirestoreError extends FirebaseError {
// @public
export type FirestoreErrorCode = 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated';

// @public
export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache;

// @public
export interface FirestoreSettings {
cache?: FirestoreLocalCache;
cacheSizeBytes?: number;
experimentalAutoDetectLongPolling?: boolean;
experimentalForceLongPolling?: boolean;
Expand Down Expand Up @@ -285,6 +289,47 @@ export interface IndexConfiguration {
readonly indexes?: Index[];
}

// @public
export interface IndexedDbLocalCache {
// (undocumented)
kind: 'indexeddb';
}

// @public
export function indexedDbLocalCache(settings?: IndexedDbSettings): IndexedDbLocalCache;

// @public
export interface IndexedDbMultipleTabManager {
// (undocumented)
kind: 'IndexedDbMultipleTab';
}

// @public
export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager;

// @public
export interface IndexedDbSettings {
cacheSizeBytes?: number;
tabManager?: IndexedDbTabManager;
}

// @public
export interface IndexedDbSingleTabManager {
// (undocumented)
kind: 'indexedDbSingleTab';
}

// @public
export function indexedDbSingleTabManager(settings: IndexedDbSingleTabManagerSettings | undefined): IndexedDbSingleTabManager;

// @public
export interface IndexedDbSingleTabManagerSettings {
forceOwnership?: boolean;
}

// @public
export type IndexedDbTabManager = IndexedDbSingleTabManager | IndexedDbMultipleTabManager;

// @beta
export interface IndexField {
// (undocumented)
Expand Down Expand Up @@ -324,6 +369,15 @@ export interface LoadBundleTaskProgress {

export { LogLevel }

// @public
export interface MemoryLocalCache {
// (undocumented)
kind: 'memory';
}

// @public
export function memoryLocalCache(): MemoryLocalCache;

// @public
export function namedQuery(firestore: Firestore, name: string): Promise<Query | null>;

Expand Down
4 changes: 3 additions & 1 deletion packages/firestore/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ export {

export {
FirestoreLocalCache,
MemoryLocalCache,
IndexedDbLocalCache,
IndexedDbMultipleTabManager,
indexedDbLocalCache,
indexedDbMultipleTabManager,
IndexedDbSettings,
indexedDbSingleTabManager,
IndexedDbSingleTabManager,
IndexedDbSingleTabManagerSettings,
MemoryLocalCache,
memoryLocalCache,
IndexedDbTabManager
} from './api/cache_config';
Expand Down
190 changes: 170 additions & 20 deletions packages/firestore/src/api/cache_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,86 +23,185 @@ import {
OnlineComponentProvider
} from '../core/component_provider';

export interface MemoryLocalCache {
/* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */
/**
* Provides a in-memory cache to the SDK. This is the default cache unless explicitly
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
* specified otherwise.
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
*
* To use, create an instance using the factory function `memoryLocalCache()`, then
* set the instance to `FirestoreSettings.cache` and call `initializeFirestore` using
* the settings object.
*/
export type MemoryLocalCache = {
kind: 'memory';
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
/**
* @internal
*/
_offlineComponentProvider: MemoryOfflineComponentProvider;
}
};

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

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

toJSON(): {} {
return { kind: this.kind };
}
}

export interface IndexedDbLocalCache {
/**
* Provides a cache backed by IndexedDb to the SDK.
*
* To use, create an instance using the factory function `indexedDbLocalCache()`, then
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
* set the instance to `FirestoreSettings.cache` and call `initializeFirestore` using
* the settings object.
*/
export type IndexedDbLocalCache = {
kind: 'indexeddb';
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
/**
* @internal
*/
_offlineComponentProvider: OfflineComponentProvider;
}
};

class IndexedDbLocalCacheImpl implements IndexedDbLocalCache {
kind: 'indexeddb' = 'indexeddb';
/**
* @internal
*/
_onlineComponentProvider: OnlineComponentProvider;
/**
* @internal
*/
_offlineComponentProvider: OfflineComponentProvider;

constructor(settings: IndexedDbSettings | undefined) {
let tabManager: IndexedDbTabManager;
if (settings?.tabManager) {
settings.tabManager.initialize(settings);
settings.tabManager._initialize(settings);
tabManager = settings.tabManager;
} else {
tabManager = indexedDbSingleTabManager(undefined);
tabManager.initialize(settings);
tabManager._initialize(settings);
}
this._onlineComponentProvider = tabManager._onlineComponentProvider!;
this._offlineComponentProvider = tabManager._offlineComponentProvider!;
}

toJSON(): {} {
return { kind: this.kind };
}
}

/**
* Union type from all supported SDK cache layer.
*/
export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache;

// Factory function
/**
* Creates an instance of `MemoryLocalCache`. The instance can be set to
* `FirestoreSettings.cache` to tell the SDK what cache layer to use.
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
*/
export function memoryLocalCache(): MemoryLocalCache {
return new MemoryLocalCacheImpl();
}

export interface IndexedDbSettings {
/**
* An settings object to configure an `IndexedDbLocalCache` instance.
*/
export type IndexedDbSettings = {
/**
* An approximate cache size threshold for the on-disk data. If the cache
* grows beyond this size, Firestore will start removing data that hasn't been
* recently used. The size is not a guarantee that the cache will stay below
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
* that size, only that if the cache exceeds the given size, cleanup will be
* attempted.
*
* The default value is 40 MB. The threshold must be set to at least 1 MB, and
* can be set to `CACHE_SIZE_UNLIMITED` to disable garbage collection.
*/
cacheSizeBytes?: number;
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
// default to singleTabManager({forceOwnership: false})

/**
* Specifies how multiple tabs/windows will be managed by the SDK.
*/
tabManager?: IndexedDbTabManager;
}
};

// Factory function
/**
* Creates an instance of `IndexedDbLocalCache`. The instance can be set to
* `FirestoreSettings.cache` to tell the SDK what cache layer to use.
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
*/
export function indexedDbLocalCache(
settings?: IndexedDbSettings
): IndexedDbLocalCache {
return new IndexedDbLocalCacheImpl(settings);
}

export interface IndexedDbSingleTabManager {
/**
* A tab manager supportting only one tab, no synchronization will be
* performed across tabs.
*/
export type IndexedDbSingleTabManager = {
kind: 'indexedDbSingleTab';
initialize: (
/**
* @internal
*/
_initialize: (
settings: Omit<IndexedDbSettings, 'tabManager'> | undefined
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
) => void;
/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
/**
* @internal
*/
_offlineComponentProvider?: OfflineComponentProvider;
}
};

class SingleTabManagerImpl implements IndexedDbSingleTabManager {
kind: 'indexedDbSingleTab' = 'indexedDbSingleTab';

/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
/**
* @internal
*/
_offlineComponentProvider?: OfflineComponentProvider;

constructor(private forceOwnership?: boolean) {}

initialize(
toJSON(): {} {
return { kind: this.kind };
}

/**
* @internal
*/
_initialize(
settings: Omit<IndexedDbSettings, 'tabManager'> | undefined
): void {
this._onlineComponentProvider = new OnlineComponentProvider();
Expand All @@ -114,20 +213,47 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager {
}
}

export interface IndexedDbMultipleTabManager {
/**
* A tab manager supportting multiple tabs. SDK will synchronize queries and
* mutations done across all tabs using the SDK.
*/
export type IndexedDbMultipleTabManager = {
kind: 'IndexedDbMultipleTab';
initialize: (settings: Omit<IndexedDbSettings, 'tabManager'>) => void;
/**
* @internal
*/
_initialize: (settings: Omit<IndexedDbSettings, 'tabManager'>) => void;
/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
/**
* @internal
*/

_offlineComponentProvider?: OfflineComponentProvider;
}
};

class MultiTabManagerImpl implements IndexedDbMultipleTabManager {
kind: 'IndexedDbMultipleTab' = 'IndexedDbMultipleTab';

/**
* @internal
*/
_onlineComponentProvider?: OnlineComponentProvider;
/**
* @internal
*/
_offlineComponentProvider?: OfflineComponentProvider;

initialize(
toJSON(): {} {
return { kind: this.kind };
}

/**
* @internal
*/
_initialize(
settings: Omit<IndexedDbSettings, 'tabManager'> | undefined
): void {
this._onlineComponentProvider = new OnlineComponentProvider();
Expand All @@ -138,15 +264,39 @@ class MultiTabManagerImpl implements IndexedDbMultipleTabManager {
}
}

/**
* A union of all avaialbe tab managers.
*/
export type IndexedDbTabManager =
| IndexedDbSingleTabManager
| IndexedDbMultipleTabManager;

/**
* Type to configure an `IndexedDbSingleTabManager` instace.
*/
export type IndexedDbSingleTabManagerSettings = {
/**
* Whether to force enable indexeddb cache for the client. This cannot be used
wu-hui marked this conversation as resolved.
Show resolved Hide resolved
* with multi-tab synchronization and is primarily intended for use with Web
* Workers. Setting this to `true` will enable indexeddb, but cause other
* tabs using indexeddb cache to fail.
*/
forceOwnership?: boolean;
};
/**
* Creates an instance of `IndexedDbSingleTabManager`.
*
* @param settings Configures the created tab manager.
*/
export function indexedDbSingleTabManager(
settings: { forceOwnership?: boolean } | undefined
settings: IndexedDbSingleTabManagerSettings | undefined
): IndexedDbSingleTabManager {
return new SingleTabManagerImpl(settings?.forceOwnership);
}

/**
* Creates an instance of `IndexedDbMultipleTabManager`.
*/
export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager {
return new MultiTabManagerImpl();
}
Loading