From e8b53c622d3426a14c46719b30ebff33f06800b2 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 4 Jan 2023 10:38:26 -0500 Subject: [PATCH 01/39] Initial implementation --- packages/firestore/src/api/database.ts | 62 +++++++++++++++---- .../firestore/src/core/component_provider.ts | 33 ++++++++++ .../firestore/src/local/memory_persistence.ts | 7 +++ 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 29cca7e68ca..edd2e1fc630 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -26,6 +26,7 @@ import { deepEqual, getDefaultEmulatorHostnameAndPort } from '@firebase/util'; import { User } from '../auth/user'; import { IndexedDbOfflineComponentProvider, + LruGcMemoryOfflineComponentProvider, MultiTabOfflineComponentProvider, OfflineComponentProvider, OnlineComponentProvider @@ -286,28 +287,65 @@ export function configureFirestore(firestore: Firestore): void { } /** - * Attempts to enable persistent storage, if possible. + * Attempts to enable the LRU garbage collector for memory persistence. * * Must be called before any other functions (other than * {@link initializeFirestore}, {@link (getFirestore:1)} or * {@link clearIndexedDbPersistence}. * - * If this fails, `enableIndexedDbPersistence()` will reject the promise it - * returns. Note that even after this failure, the {@link Firestore} instance will - * remain usable, however offline persistence will be disabled. + * By default, any documents that are not part of an active query result or + * with no mutation attached to them are removed from memory immediately. * - * There are several reasons why this can fail, which can be identified by - * the `code` on the error. - * - * * failed-precondition: The app is already open in another browser tab. - * * unimplemented: The browser is incompatible with the offline - * persistence implementation. + * This function changes the default behavior, to enable a least-recent-used + * garbage collector. Documents will be collected when their total size exceeds + * `Settings.cacheSizeBytes`, with least recently used documents get removed first. * * @param firestore - The {@link Firestore} instance to enable persistence for. - * @param persistenceSettings - Optional settings object to configure - * persistence. * @returns A `Promise` that represents successfully enabling persistent storage. */ +export function enableMemoryLRUGarbageCollection( + firestore: Firestore +): Promise { + firestore = cast(firestore, Firestore); + verifyNotInitialized(firestore); + + const client = ensureFirestoreConfigured(firestore); + const settings = firestore._freezeSettings(); + + const onlineComponentProvider = new OnlineComponentProvider(); + const offlineComponentProvider = new LruGcMemoryOfflineComponentProvider( + settings.cacheSizeBytes + ); + return setPersistenceProviders( + client, + onlineComponentProvider, + offlineComponentProvider + ); +} + +/** + * Attempts to enable persistent storage, if possible. // + * // + * Must be called before any other functions (other than // + * {@link initializeFirestore}, {@link (getFirestore:1)} or // + * {@link clearIndexedDbPersistence}. // + * // + * If this fails, `enableIndexedDbPersistence()` will reject the promise it // + * returns. Note that even after this failure, the {@link Firestore} instance will // + * remain usable, however offline persistence will be disabled. // + * // + * There are several reasons why this can fail, which can be identified by // + * the `code` on the error. // + * // + * * failed-precondition: The app is already open in another browser tab. // + * * unimplemented: The browser is incompatible with the offline // + * persistence implementation. // + * // + * @param firestore - The {@link Firestore} instance to enable persistence for. // + * @param persistenceSettings - Optional settings object to configure // + * persistence. // + * @returns A `Promise` that represents successfully enabling persistent storage. // + */ export function enableIndexedDbPersistence( firestore: Firestore, persistenceSettings?: PersistenceSettings diff --git a/packages/firestore/src/core/component_provider.ts b/packages/firestore/src/core/component_provider.ts index da978187329..0e4b6eff298 100644 --- a/packages/firestore/src/core/component_provider.ts +++ b/packages/firestore/src/core/component_provider.ts @@ -31,6 +31,7 @@ import { LruParams } from '../local/lru_garbage_collector'; import { LruScheduler } from '../local/lru_garbage_collector_impl'; import { MemoryEagerDelegate, + MemoryLruDelegate, MemoryPersistence } from '../local/memory_persistence'; import { Scheduler, Persistence } from '../local/persistence'; @@ -72,6 +73,7 @@ import { syncEngineSynchronizeWithChangedDocuments } from './sync_engine_impl'; import { OnlineStateSource } from './types'; +import { hardAssert } from '../util/assert'; export interface ComponentConfiguration { asyncQueue: AsyncQueue; @@ -172,6 +174,37 @@ export class MemoryOfflineComponentProvider } } +export class LruGcMemoryOfflineComponentProvider extends MemoryOfflineComponentProvider { + constructor(protected readonly cacheSizeBytes: number | undefined) { + super(); + } + + createGarbageCollectionScheduler( + cfg: ComponentConfiguration, + localStore: LocalStore + ): Scheduler | null { + hardAssert( + this.persistence.referenceDelegate instanceof MemoryLruDelegate, + 'referenceDelegate is expected to be an instance of MemoryLruDelegate.' + ); + + const garbageCollector = + this.persistence.referenceDelegate.garbageCollector; + return new LruScheduler(garbageCollector, cfg.asyncQueue, localStore); + } + + createPersistence(cfg: ComponentConfiguration): Persistence { + const lruParams = + this.cacheSizeBytes !== undefined + ? LruParams.withCacheSize(this.cacheSizeBytes) + : LruParams.DEFAULT; + return new MemoryPersistence( + p => MemoryLruDelegate.factory(p, lruParams), + this.serializer + ); + } +} + /** * Provides all components needed for Firestore with IndexedDB persistence. */ diff --git a/packages/firestore/src/local/memory_persistence.ts b/packages/firestore/src/local/memory_persistence.ts index 2e5a993f19c..1fda77e9538 100644 --- a/packages/firestore/src/local/memory_persistence.ts +++ b/packages/firestore/src/local/memory_persistence.ts @@ -353,6 +353,13 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate { this.garbageCollector = newLruGarbageCollector(this, lruParams); } + static factory( + persistence: MemoryPersistence, + lruParams: LruParams | null + ): MemoryLruDelegate { + return new MemoryLruDelegate(persistence, lruParams!!); + } + // No-ops, present so memory persistence doesn't have to care which delegate // it has. onTransactionStarted(): void {} From 3a922f1a3218fe92ab1cff9db3f461068b8c8185 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 11 Jan 2023 15:10:49 -0500 Subject: [PATCH 02/39] Add spec tests for LRU GC --- packages/firestore/src/api.ts | 1 + packages/firestore/src/api/database.ts | 42 +++--- .../firestore/src/core/component_provider.ts | 2 +- .../src/local/lru_garbage_collector_impl.ts | 6 +- .../test/integration/api/database.test.ts | 20 +++ .../test/integration/api/get_options.test.ts | 37 +++-- .../test/integration/util/helpers.ts | 22 ++- .../test/unit/specs/bundle_spec.test.ts | 6 +- .../unit/specs/existence_filter_spec.test.ts | 2 +- .../specs/garbage_collection_spect.test.ts | 137 ++++++++++++++++++ .../test/unit/specs/limbo_spec.test.ts | 12 +- .../test/unit/specs/limit_spec.test.ts | 14 +- .../test/unit/specs/listen_spec.test.ts | 34 ++--- .../test/unit/specs/orderby_spec.test.ts | 2 +- .../test/unit/specs/perf_spec.test.ts | 18 +-- .../test/unit/specs/persistence_spec.test.ts | 4 +- .../test/unit/specs/recovery_spec.test.ts | 12 +- .../test/unit/specs/remote_store_spec.test.ts | 6 +- .../test/unit/specs/resume_token_spec.test.ts | 2 +- .../firestore/test/unit/specs/spec_builder.ts | 30 +++- .../test/unit/specs/spec_test_components.ts | 21 ++- .../test/unit/specs/spec_test_runner.ts | 54 +++++-- .../test/unit/specs/write_spec.test.ts | 8 +- 23 files changed, 371 insertions(+), 121 deletions(-) create mode 100644 packages/firestore/test/unit/specs/garbage_collection_spect.test.ts diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 2742195eecd..85955776d6d 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -36,6 +36,7 @@ export { initializeFirestore, getFirestore, enableIndexedDbPersistence, + enableMemoryLRUGarbageCollection, enableMultiTabIndexedDbPersistence, clearIndexedDbPersistence, waitForPendingWrites, diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index edd2e1fc630..9cf53d06e24 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -324,27 +324,27 @@ export function enableMemoryLRUGarbageCollection( } /** - * Attempts to enable persistent storage, if possible. // - * // - * Must be called before any other functions (other than // - * {@link initializeFirestore}, {@link (getFirestore:1)} or // - * {@link clearIndexedDbPersistence}. // - * // - * If this fails, `enableIndexedDbPersistence()` will reject the promise it // - * returns. Note that even after this failure, the {@link Firestore} instance will // - * remain usable, however offline persistence will be disabled. // - * // - * There are several reasons why this can fail, which can be identified by // - * the `code` on the error. // - * // - * * failed-precondition: The app is already open in another browser tab. // - * * unimplemented: The browser is incompatible with the offline // - * persistence implementation. // - * // - * @param firestore - The {@link Firestore} instance to enable persistence for. // - * @param persistenceSettings - Optional settings object to configure // - * persistence. // - * @returns A `Promise` that represents successfully enabling persistent storage. // + * Attempts to enable persistent storage, if possible. + * + * Must be called before any other functions (other than + * {@link initializeFirestore}, {@link (getFirestore:1)} or + * {@link clearIndexedDbPersistence}. + * + * If this fails, `enableIndexedDbPersistence()` will reject the promise it + * returns. Note that even after this failure, the {@link Firestore} instance will + * remain usable, however offline persistence will be disabled. + * + * There are several reasons why this can fail, which can be identified by + * the `code` on the error. + * + * * failed-precondition: The app is already open in another browser tab. + * * unimplemented: The browser is incompatible with the offline + * persistence implementation. + * + * @param firestore - The {@link Firestore} instance to enable persistence for. + * @param persistenceSettings - Optional settings object to configure + * persistence. + * @returns A `Promise` that represents successfully enabling persistent storage. */ export function enableIndexedDbPersistence( firestore: Firestore, diff --git a/packages/firestore/src/core/component_provider.ts b/packages/firestore/src/core/component_provider.ts index 0e4b6eff298..447e0f07857 100644 --- a/packages/firestore/src/core/component_provider.ts +++ b/packages/firestore/src/core/component_provider.ts @@ -54,6 +54,7 @@ import { remoteStoreShutdown } from '../remote/remote_store'; import { JsonProtoSerializer } from '../remote/serializer'; +import { hardAssert } from '../util/assert'; import { AsyncQueue } from '../util/async_queue'; import { Code, FirestoreError } from '../util/error'; @@ -73,7 +74,6 @@ import { syncEngineSynchronizeWithChangedDocuments } from './sync_engine_impl'; import { OnlineStateSource } from './types'; -import { hardAssert } from '../util/assert'; export interface ComponentConfiguration { asyncQueue: AsyncQueue; diff --git a/packages/firestore/src/local/lru_garbage_collector_impl.ts b/packages/firestore/src/local/lru_garbage_collector_impl.ts index ed280d3e845..b88412cc29a 100644 --- a/packages/firestore/src/local/lru_garbage_collector_impl.ts +++ b/packages/firestore/src/local/lru_garbage_collector_impl.ts @@ -176,8 +176,10 @@ export class LruScheduler implements Scheduler { } } -/** Implements the steps for LRU garbage collection. */ -class LruGarbageCollectorImpl implements LruGarbageCollector { +/** + * Implements the steps for LRU garbage collection. Exported for testing purpose only. + */ +export class LruGarbageCollectorImpl implements LruGarbageCollector { constructor( private readonly delegate: LruDelegate, readonly params: LruParams diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 5f3e2dc6c61..420aae5bbcc 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -20,6 +20,7 @@ import { Deferred } from '@firebase/util'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../../../src/local/lru_garbage_collector_impl'; import { EventsAccumulator } from '../util/events_accumulator'; import { addDoc, @@ -67,6 +68,7 @@ import { } from '../util/firebase_export'; import { apiDescribe, + withEnsuredGcTestDb, withTestCollection, withTestDbsSettings, withTestDb, @@ -1751,4 +1753,22 @@ apiDescribe('Database', (persistence: boolean) => { } ); }); + + it('Can get document from cache with GC enabled.', () => { + const initialData = { key: 'value' }; + return withEnsuredGcTestDb( + persistence, + LRU_MINIMUM_CACHE_SIZE_BYTES, + async db => { + const docRef = doc(collection(db, 'test-collection')); + await setDoc(docRef, initialData); + return getDoc(docRef).then(doc => { + expect(doc.exists()).to.be.true; + expect(doc.metadata.fromCache).to.be.false; + expect(doc.metadata.hasPendingWrites).to.be.false; + expect(doc.data()).to.deep.equal(initialData); + }); + } + ); + }); }); diff --git a/packages/firestore/test/integration/api/get_options.test.ts b/packages/firestore/test/integration/api/get_options.test.ts index 066802693d8..4a147a883e7 100644 --- a/packages/firestore/test/integration/api/get_options.test.ts +++ b/packages/firestore/test/integration/api/get_options.test.ts @@ -16,8 +16,10 @@ */ import { expect } from 'chai'; +import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../../../src/local/lru_garbage_collector_impl'; import { + collection, deleteDoc, disableNetwork, doc, @@ -34,7 +36,8 @@ import { toDataMap, apiDescribe, withTestCollection, - withTestDocAndInitialData + withTestDocAndInitialData, + withEnsuredGcTestDb } from '../util/helpers'; apiDescribe('GetOptions', (persistence: boolean) => { @@ -68,20 +71,24 @@ apiDescribe('GetOptions', (persistence: boolean) => { it('get document while offline with default get options', () => { const initialData = { key: 'value' }; - return withTestDocAndInitialData(persistence, initialData, (docRef, db) => { - // Register a snapshot to force the data to stay in the cache and not be - // garbage collected. - onSnapshot(docRef, () => {}); - return getDoc(docRef) - .then(() => disableNetwork(db)) - .then(() => getDoc(docRef)) - .then(doc => { - expect(doc.exists()).to.be.true; - expect(doc.metadata.fromCache).to.be.true; - expect(doc.metadata.hasPendingWrites).to.be.false; - expect(doc.data()).to.deep.equal(initialData); - }); - }); + // Use an instance with Gc turned on. + return withEnsuredGcTestDb( + persistence, + LRU_MINIMUM_CACHE_SIZE_BYTES, + async db => { + const docRef = doc(collection(db, 'test-collection')); + await setDoc(docRef, initialData); + return getDoc(docRef) + .then(() => disableNetwork(db)) + .then(() => getDoc(docRef)) + .then(doc => { + expect(doc.exists()).to.be.true; + expect(doc.metadata.fromCache).to.be.true; + expect(doc.metadata.hasPendingWrites).to.be.false; + expect(doc.data()).to.deep.equal(initialData); + }); + } + ); }); it('get collection while offline with default get options', () => { diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 79dbacaafa0..4d4dbf8a5a9 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -32,7 +32,8 @@ import { PrivateSettings, SnapshotListenOptions, newTestFirestore, - newTestApp + newTestApp, + enableMemoryLRUGarbageCollection } from './firebase_export'; import { ALT_PROJECT_ID, @@ -141,6 +142,25 @@ export function withTestDb( }); } +export function withEnsuredGcTestDb( + persistence: boolean, + sizeBytes: number, + fn: (db: Firestore) => Promise +): Promise { + return withTestDbsSettings( + persistence, + DEFAULT_PROJECT_ID, + { ...DEFAULT_SETTINGS, cacheSizeBytes: sizeBytes }, + 1, + async ([db]) => { + if (!persistence) { + await enableMemoryLRUGarbageCollection(db); + } + return fn(db); + } + ); +} + /** Runs provided fn with a db for an alternate project id. */ export function withAlternateTestDb( persistence: boolean, diff --git a/packages/firestore/test/unit/specs/bundle_spec.test.ts b/packages/firestore/test/unit/specs/bundle_spec.test.ts index 3efa1be4d63..2027e69abb8 100644 --- a/packages/firestore/test/unit/specs/bundle_spec.test.ts +++ b/packages/firestore/test/unit/specs/bundle_spec.test.ts @@ -178,7 +178,7 @@ describeSpec('Bundles:', ['no-ios'], () => { spec() // TODO(b/160878667): Figure out what happens when memory eager GC is on // a bundle is loaded. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 250, docA) .expectEvents(query1, { @@ -224,7 +224,7 @@ describeSpec('Bundles:', ['no-ios'], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 250, docA) .expectEvents(query1, { @@ -260,7 +260,7 @@ describeSpec('Bundles:', ['no-ios'], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 250) // Backend tells is there is no such doc. diff --git a/packages/firestore/test/unit/specs/existence_filter_spec.test.ts b/packages/firestore/test/unit/specs/existence_filter_spec.test.ts index 4943d986bd2..a40af3d30f4 100644 --- a/packages/firestore/test/unit/specs/existence_filter_spec.test.ts +++ b/packages/firestore/test/unit/specs/existence_filter_spec.test.ts @@ -69,7 +69,7 @@ describeSpec('Existence Filters:', [], () => { const doc1 = doc('collection/1', 2000, { v: 2 }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { added: [doc1] }) diff --git a/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts b/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts new file mode 100644 index 00000000000..aeabf5b5692 --- /dev/null +++ b/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts @@ -0,0 +1,137 @@ +/** + * @license + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { setLogLevel } from '@firebase/logger'; +import { doc, filter, query } from '../../util/helpers'; + +import { describeSpec, specTest } from './describe_spec'; +import { spec } from './spec_builder'; + +describeSpec('Garbage Collection:', [], () => { + specTest( + 'Contents of query are cleared when listen is removed.', + ['eager-gc'], + 'Explicitly tests eager GC behavior', + () => { + const query1 = query('collection'); + const docA = doc('collection/a', 1000, { key: 'a' }); + return ( + spec() + .userListens(query1) + .watchAcksFull(query1, 1000, docA) + .expectEvents(query1, { added: [docA] }) + .userUnlistens(query1) + // should get no events. + .userListens(query1) + ); + } + ); + + specTest('Contents of query are kept after listen is removed.', [], () => { + const query1 = query('collection'); + const docA = doc('collection/a', 1000, { key: 'a' }); + return spec() + .withEagerGCForMemoryPersistence(false) + .userListens(query1) + .watchAcksFull(query1, 1000, docA) + .expectEvents(query1, { added: [docA] }) + .userUnlistens(query1) + .userListens(query1) + .expectListen(query1, { resumeToken: 'resume-token-1000' }) + .expectEvents(query1, { added: [docA], fromCache: true }); + }); + + specTest( + 'Contents of query are kept after listen is removed, and GC threshold is not reached', + [], + () => { + const query1 = query('collection'); + const docA = doc('collection/a', 1000, { key: 'a' }); + return spec() + .withEagerGCForMemoryPersistence(false) + .userListens(query1) + .watchAcksFull(query1, 1000, docA) + .expectEvents(query1, { added: [docA] }) + .userUnlistens(query1) + .triggerLruGC(1000) + .userListens(query1) + .expectListen(query1, { resumeToken: 'resume-token-1000' }) + .expectEvents(query1, { added: [docA], fromCache: true }); + } + ); + + specTest( + 'Contents of query are kept after listen is removed, and GC threshold is reached', + [], + () => { + const query1 = query('collection'); + const docA = doc('collection/a', 1000, { key: 'a' }); + return ( + spec() + .withEagerGCForMemoryPersistence(false) + .userListens(query1) + .watchAcksFull(query1, 1000, docA) + .expectEvents(query1, { added: [docA] }) + .userUnlistens(query1) + .triggerLruGC(1) + .removeExpectedTargetMapping(query1) + // should get no events. + .userListens(query1) + ); + } + ); + + specTest( + 'Contents of active query are kept while inactive results are removed after GC', + [], + () => { + const queryFull = query('collection'); + const queryA = query('collection', filter('key', '==', 'a')); + const docA = doc('collection/a', 1000, { key: 'a' }); + const docB = doc('collection/b', 1000, { key: 'b' }); + const docC = doc('collection/c', 1000, { key: 'c' }); + const docCMutated = doc('collection/c', 1000, { + key: 'c', + extra: 'extra' + }).setHasLocalMutations(); + const docD = doc('collection/d', 1000, { key: 'd' }); + return ( + spec() + .withEagerGCForMemoryPersistence(false) + .userListens(queryFull) + .watchAcksFull(queryFull, 1000, docA, docB, docC, docD) + .expectEvents(queryFull, { added: [docA, docB, docC, docD] }) + .userUnlistens(queryFull) + .userListens(queryA) + .expectEvents(queryA, { added: [docA], fromCache: true }) + .watchAcksFull(queryA, 1500, docA) + .expectEvents(queryA, { fromCache: false }) + .userSets('collection/c', { key: 'c', extra: 'extra' }) + .triggerLruGC(1) + .removeExpectedTargetMapping(queryFull) + .userUnlistens(queryA) + // should get no events. + .userListens(queryFull) + .expectEvents(queryFull, { + added: [docA, docCMutated], + hasPendingWrites: true, + fromCache: true + }) + ); + } + ); +}); diff --git a/packages/firestore/test/unit/specs/limbo_spec.test.ts b/packages/firestore/test/unit/specs/limbo_spec.test.ts index b7db2049ec9..bebe0b4175b 100644 --- a/packages/firestore/test/unit/specs/limbo_spec.test.ts +++ b/packages/firestore/test/unit/specs/limbo_spec.test.ts @@ -203,7 +203,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() // No GC so we can keep the cache populated. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // Full query to populate the cache with docA and docB .userListens(fullQuery) @@ -275,7 +275,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() // No GC so we can keep the cache populated. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // Full query to populate the cache with docA and docB .userListens(fullQuery) @@ -512,7 +512,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userSets('collection/a', { matches: true }) .userSets('collection/b', { matches: true }) .writeAcks('collection/a', 1000) @@ -926,7 +926,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // Start a limbo resolution listen for a document (doc1). .userListens(fullQuery) @@ -964,7 +964,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .withMaxConcurrentLimboResolutions(1) // Max out the number of active limbo resolutions. @@ -1014,7 +1014,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .withMaxConcurrentLimboResolutions(1) // Max out the number of active limbo resolutions. diff --git a/packages/firestore/test/unit/specs/limit_spec.test.ts b/packages/firestore/test/unit/specs/limit_spec.test.ts index 678f929743d..b02502f944f 100644 --- a/packages/firestore/test/unit/specs/limit_spec.test.ts +++ b/packages/firestore/test/unit/specs/limit_spec.test.ts @@ -51,7 +51,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1000, { key: 'b' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, doc1, doc2) .expectEvents(query1, { @@ -165,7 +165,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { matches: true }); const doc3 = doc('collection/c', 1000, { matches: true }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(fullQuery) .watchAcksFull(fullQuery, 1002, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -192,7 +192,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { matches: true }); const doc3 = doc('collection/c', 1003, { matches: true }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(fullQuery) .watchAcksFull(fullQuery, 1003, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -226,7 +226,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { pos: 2 }); const doc3 = doc('collection/c', 1003, { pos: 3 }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(fullQuery) .watchAcksFull(fullQuery, 1003, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -262,7 +262,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { pos: 2 }); const doc3 = doc('collection/c', 1003, { pos: 3 }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(fullQuery) .watchAcksFull(fullQuery, 1003, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -303,7 +303,7 @@ describeSpec('Limits:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(limitQuery) .watchAcksFull(limitQuery, 1001, firstDocument) .expectEvents(limitQuery, { added: [firstDocument] }) @@ -379,7 +379,7 @@ describeSpec('Limits:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // We issue a limit query with an orderBy constraint. .userListens(limitQuery) .watchAcksFull(limitQuery, 2001, firstDocument) diff --git a/packages/firestore/test/unit/specs/listen_spec.test.ts b/packages/firestore/test/unit/specs/listen_spec.test.ts index 8bc62391838..4d114a82d95 100644 --- a/packages/firestore/test/unit/specs/listen_spec.test.ts +++ b/packages/firestore/test/unit/specs/listen_spec.test.ts @@ -210,7 +210,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection'); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcks(query1) .watchRemoves( @@ -265,7 +265,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, docAv1) .expectEvents(query1, { added: [docAv1] }) @@ -301,7 +301,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, docAv1) .expectEvents(query1, { added: [docAv1] }) @@ -334,7 +334,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(visibleQuery) .watchAcksFull(visibleQuery, 1000, docAv1) .expectEvents(visibleQuery, { added: [docAv1] }) @@ -377,7 +377,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(visibleQuery) .watchAcksFull(visibleQuery, 1000, docAv1) .expectEvents(visibleQuery, { added: [docAv1] }) @@ -474,7 +474,7 @@ describeSpec('Listens:', [], () => { const docB = doc('collection/b', 1000, { key: 'a' }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // Add a collection query with two documents, one of which gets deleted // (the second document guarantees that we later raise an event from // cache). @@ -508,7 +508,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); const deletedDocA = deletedDoc('collection/a', 2000); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -528,7 +528,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); const docB = doc('collection/b', 2000, { key: 'b' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -548,7 +548,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcks(query1) .watchSends({ affects: [query1] }, docA) @@ -570,7 +570,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection'); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchRemoves( query1, @@ -586,7 +586,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection'); const docA = doc('collection/a', 2000, { key: 'a' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000) .expectEvents(query1, {}) @@ -606,7 +606,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection', filter('array', 'array-contains', 42)); const docA = doc('collection/a', 2000, { foo: 'bar', array: [1, 42, 3] }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000) .expectEvents(query1, {}) @@ -628,7 +628,7 @@ describeSpec('Listens:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -657,7 +657,7 @@ describeSpec('Listens:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -690,7 +690,7 @@ describeSpec('Listens:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, initialVersion, docA) .expectEvents(query1, { added: [docA] }) @@ -1704,7 +1704,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // Populate the cache with the empty query results. .userListens(query1) .watchAcksFull(query1, 1000) @@ -1731,7 +1731,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) // Populate the cache with the empty query results. .userListens(query1) .watchAcksFull(query1, 1000, doc1) diff --git a/packages/firestore/test/unit/specs/orderby_spec.test.ts b/packages/firestore/test/unit/specs/orderby_spec.test.ts index a6593dbd5d1..afbd223b2b2 100644 --- a/packages/firestore/test/unit/specs/orderby_spec.test.ts +++ b/packages/firestore/test/unit/specs/orderby_spec.test.ts @@ -57,7 +57,7 @@ describeSpec('OrderBy:', [], () => { const docB = doc('collection/b', 1001, { key: 'b', sort: 1 }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1002, docA, docB) .expectEvents(query1, { added: [docB, docA] }) diff --git a/packages/firestore/test/unit/specs/perf_spec.test.ts b/packages/firestore/test/unit/specs/perf_spec.test.ts index 310ec4554b6..3a17d73c61d 100644 --- a/packages/firestore/test/unit/specs/perf_spec.test.ts +++ b/packages/firestore/test/unit/specs/perf_spec.test.ts @@ -30,7 +30,7 @@ describeSpec( ['benchmark'], () => { specTest('Insert a new document', [], () => { - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); for (let i = 0; i < STEP_COUNT; ++i) { steps .userSets(`collection/{i}`, { doc: i }) @@ -44,7 +44,7 @@ describeSpec( [], () => { let currentVersion = 1; - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); for (let i = 0; i < STEP_COUNT; ++i) { const query1 = query(`collection/${i}`); @@ -77,7 +77,7 @@ describeSpec( const cachedDocumentCount = 100; 9; const query1 = query(`collection`, orderBy('v')); - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); const docs: Document[] = []; for (let i = 0; i < cachedDocumentCount; ++i) { @@ -101,7 +101,7 @@ describeSpec( specTest('Update a single document', [], () => { const steps = spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userSets(`collection/doc`, { v: 0 }); for (let i = 1; i <= STEP_COUNT; ++i) { @@ -119,7 +119,7 @@ describeSpec( const query1 = query(`collection/doc`); let currentVersion = 1; - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); let docLocal = doc(`collection/doc`, 0, { v: 0 @@ -168,7 +168,7 @@ describeSpec( const documentsPerStep = 100; const query1 = query(`collection`, orderBy('v')); - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); let currentVersion = 1; @@ -206,7 +206,7 @@ describeSpec( const documentsPerStep = 100; let currentVersion = 1; - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); for (let i = 1; i <= STEP_COUNT; ++i) { const collPath = `collection/${i}/coll`; @@ -241,7 +241,7 @@ describeSpec( const queriesPerStep = 25; let currentVersion = 1; - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); for (let i = 1; i <= STEP_COUNT; ++i) { // We use a different subcollection for each iteration to ensure @@ -290,7 +290,7 @@ describeSpec( const matchingCount = 10; const queryCount = 10; - const steps = spec().withGCEnabled(false); + const steps = spec().withEagerGCForMemoryPersistence(false); const collPath = `collection`; const query1 = query(collPath, orderBy('val')); diff --git a/packages/firestore/test/unit/specs/persistence_spec.test.ts b/packages/firestore/test/unit/specs/persistence_spec.test.ts index 700b0ca4424..c3d57b1a2d4 100644 --- a/packages/firestore/test/unit/specs/persistence_spec.test.ts +++ b/packages/firestore/test/unit/specs/persistence_spec.test.ts @@ -78,7 +78,7 @@ describeSpec('Persistence:', [], () => { const doc1 = doc('collection/key', 1000, { foo: 'bar' }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { added: [doc1] }) @@ -93,7 +93,7 @@ describeSpec('Persistence:', [], () => { const query1 = query('collection'); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userSets('collection/key', { foo: 'bar' }) // Normally the write would get GC'd from remote documents here. .writeAcks('collection/key', 1000) diff --git a/packages/firestore/test/unit/specs/recovery_spec.test.ts b/packages/firestore/test/unit/specs/recovery_spec.test.ts index 3da50a0b556..349ad64f6cb 100644 --- a/packages/firestore/test/unit/specs/recovery_spec.test.ts +++ b/packages/firestore/test/unit/specs/recovery_spec.test.ts @@ -473,7 +473,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const deletedDoc1 = deletedDoc('collection/key1', 2000); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { @@ -533,7 +533,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc2 = doc('collection/key2', 2000, { foo: 'b' }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { @@ -569,7 +569,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc2 = doc('collection/key2', 2000, { foo: 'b' }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(doc1Query) .watchAcksFull(doc1Query, 1000, doc1a) .expectEvents(doc1Query, { @@ -617,7 +617,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc1b = doc('collection/key1', 1500, { included: false }); const limboQuery = newQueryForPath(doc1a.key.path); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(fullQuery) .watchAcksFull(fullQuery, 1000, doc1a) .expectEvents(fullQuery, { @@ -658,7 +658,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc1 = doc('collection/key1', 1, { included: true }); const limboQuery = newQueryForPath(doc1.key.path); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(fullQuery) .watchAcksFull(fullQuery, 1000, doc1) .expectEvents(fullQuery, { @@ -796,7 +796,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc1 = doc('collection/key1', 1, { foo: 'a' }); const doc2 = doc('collection/key2', 2, { foo: 'b' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { diff --git a/packages/firestore/test/unit/specs/remote_store_spec.test.ts b/packages/firestore/test/unit/specs/remote_store_spec.test.ts index d315edf4166..3593332f6a2 100644 --- a/packages/firestore/test/unit/specs/remote_store_spec.test.ts +++ b/packages/firestore/test/unit/specs/remote_store_spec.test.ts @@ -26,7 +26,7 @@ describeSpec('Remote store:', [], () => { const query1 = query('collection'); const doc1 = doc('collection/a', 1000, { key: 'a' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcks(query1) .userUnlistens(query1) // Now we simulate a quick unlisten. @@ -46,7 +46,7 @@ describeSpec('Remote store:', [], () => { const doc3 = doc('collection/c', 1000, { key: 'c' }); const doc4 = doc('collection/d', 1000, { key: 'd' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcks(query1) .userUnlistens(query1) // Now we simulate a quick unlisten. @@ -72,7 +72,7 @@ describeSpec('Remote store:', [], () => { const doc1 = doc('collection/a', 1000, { key: 'a' }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) // Close before we get an ack, this should reset our pending // target counts. diff --git a/packages/firestore/test/unit/specs/resume_token_spec.test.ts b/packages/firestore/test/unit/specs/resume_token_spec.test.ts index cd0f5b9435a..4c5feaab79f 100644 --- a/packages/firestore/test/unit/specs/resume_token_spec.test.ts +++ b/packages/firestore/test/unit/specs/resume_token_spec.test.ts @@ -43,7 +43,7 @@ describeSpec('Resume tokens:', [], () => { const query1 = query('collection'); const doc1 = doc('collection/a', 1000, { key: 'a' }); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcks(query1) .watchSends({ affects: [query1] }, doc1) diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 39e397c0fbf..217a66adf90 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -177,7 +177,7 @@ class CachedTargetIdGenerator { * duplicate tests in every client. */ export class SpecBuilder { - protected config: SpecConfig = { useGarbageCollection: true, numClients: 1 }; + protected config: SpecConfig = { useEagerGCForMemory: true, numClients: 1 }; // currentStep is built up (in particular, expectations can be added to it) // until nextStep() is called to append it to steps. protected currentStep: SpecStep | null = null; @@ -240,13 +240,14 @@ export class SpecBuilder { return runSpec(name, tags, usePersistence, this.config, this.steps); } - // Configures Garbage Collection behavior (on or off). Default is on. - withGCEnabled(gcEnabled: boolean): this { + // Configures Eager Garbage Collection behavior (on or off) for memory persistence. Default is on. + // For disk persistence, this has no effect, manual LRU GC is always used. + withEagerGCForMemoryPersistence(eagerGCEnabled: boolean): this { debugAssert( !this.currentStep, 'withGCEnabled() must be called before all spec steps.' ); - this.config.useGarbageCollection = gcEnabled; + this.config.useEagerGCForMemory = eagerGCEnabled; return this; } @@ -319,7 +320,7 @@ export class SpecBuilder { const targetId = this.queryMapping.get(target)!; this.removeQueryFromActiveTargets(query, targetId); - if (this.config.useGarbageCollection && !this.activeTargets[targetId]) { + if (this.config.useEagerGCForMemory && !this.activeTargets[targetId]) { this.queryMapping.delete(target); this.queryIdGenerator.purge(target); } @@ -942,7 +943,7 @@ export class SpecBuilder { this.removeQueryFromActiveTargets(query, targetId); - if (this.config.useGarbageCollection && !this.activeTargets[targetId]) { + if (this.config.useEagerGCForMemory && !this.activeTargets[targetId]) { this.queryMapping.delete(target); this.queryIdGenerator.purge(target); } @@ -1015,6 +1016,21 @@ export class SpecBuilder { return this; } + triggerLruGC(cacheThreshold: number): this { + this.nextStep(); + this.currentStep = { + triggerLruGC: cacheThreshold + }; + return this; + } + + removeExpectedTargetMapping(query: Query): this { + const target = queryToTarget(query); + this.queryMapping.delete(target); + this.queryIdGenerator.purge(target); + return this; + } + private static queryToSpec(query: Query): SpecQuery { // TODO(dimond): full query support const spec: SpecQuery = { path: query.path.canonicalString() }; @@ -1242,6 +1258,6 @@ export function client( withGcEnabled?: boolean ): MultiClientSpecBuilder { const specBuilder = new MultiClientSpecBuilder(); - specBuilder.withGCEnabled(withGcEnabled === true); + specBuilder.withEagerGCForMemoryPersistence(withGcEnabled === true); return specBuilder.client(num); } diff --git a/packages/firestore/test/unit/specs/spec_test_components.ts b/packages/firestore/test/unit/specs/spec_test_components.ts index 1436efa3fcd..d595eed183b 100644 --- a/packages/firestore/test/unit/specs/spec_test_components.ts +++ b/packages/firestore/test/unit/specs/spec_test_components.ts @@ -32,7 +32,14 @@ import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { LocalStore } from '../../../src/local/local_store'; -import { LruParams } from '../../../src/local/lru_garbage_collector'; +import { + ActiveTargets, + LruGarbageCollector, + LruDelegate, + LruParams, + LruResults +} from '../../../src/local/lru_garbage_collector'; +import { LruGarbageCollectorImpl } from '../../../src/local/lru_garbage_collector_impl'; import { MemoryEagerDelegate, MemoryLruDelegate, @@ -207,7 +214,7 @@ export class MockMemoryOfflineComponentProvider extends MemoryOfflineComponentPr persistence!: MockMemoryPersistence; connection!: MockConnection; - constructor(private readonly gcEnabled: boolean) { + constructor(private readonly eagerGCEnabled: boolean) { super(); } @@ -219,7 +226,7 @@ export class MockMemoryOfflineComponentProvider extends MemoryOfflineComponentPr createPersistence(cfg: ComponentConfiguration): Persistence { return new MockMemoryPersistence( - this.gcEnabled + this.eagerGCEnabled ? MemoryEagerDelegate.factory : p => new MemoryLruDelegate(p, LruParams.DEFAULT), newSerializer(cfg.databaseInfo.databaseId) @@ -227,6 +234,10 @@ export class MockMemoryOfflineComponentProvider extends MemoryOfflineComponentPr } } +// A LRU garbage collector whose params are mutable. +type Mutable = { -readonly [P in keyof T]: T[P] }; +export type MutableLruGarbageCollector = Mutable; + export class MockConnection implements Connection { watchStream: StreamBridge | null = null; @@ -256,8 +267,8 @@ export class MockConnection implements Connection { shouldResourcePathBeIncludedInRequest: boolean = false; /** - * Tracks the currently active watch targets as detected by the mock watch - * stream, as a mapping from target ID to query Target. + * Tracks the currently active watch targets as detected by the mock watch // + * stream, as a mapping from target ID to query Target. // */ activeTargets: { [targetId: number]: api.Target } = {}; diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index e64dcbfd214..a84e220450f 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -66,6 +66,7 @@ import { ChangeType, DocumentViewChange } from '../../../src/core/view_snapshot'; +import { IndexedDbLruDelegateImpl } from '../../../src/local/indexeddb_lru_delegate_impl'; import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { DbPrimaryClient, @@ -78,6 +79,12 @@ import { } from '../../../src/local/indexeddb_sentinels'; import { LocalStore } from '../../../src/local/local_store'; import { localStoreConfigureFieldIndexes } from '../../../src/local/local_store_impl'; +import { + LruGarbageCollector, + LruParams, + LruResults +} from '../../../src/local/lru_garbage_collector'; +import { MemoryLruDelegate } from '../../../src/local/memory_persistence'; import { ClientId, SharedClientState @@ -171,6 +178,7 @@ import { MockMemoryPersistence, MockMultiTabOfflineComponentProvider, MockOnlineComponentProvider, + MutableLruGarbageCollector, QueryEvent, SharedWriteTracker } from './spec_test_components'; @@ -245,9 +253,10 @@ abstract class TestRunner { private localStore!: LocalStore; private remoteStore!: RemoteStore; private persistence!: MockMemoryPersistence | MockIndexedDbPersistence; + private lruGarbageCollector!: MutableLruGarbageCollector; protected sharedClientState!: SharedClientState; - private useGarbageCollection: boolean; + private useEagerGCForMemory: boolean; private numClients: number; private maxConcurrentLimboResolutions?: number; private databaseInfo: DatabaseInfo; @@ -286,7 +295,7 @@ abstract class TestRunner { /* useProto3Json= */ true ); - this.useGarbageCollection = config.useGarbageCollection; + this.useEagerGCForMemory = config.useEagerGCForMemory; this.numClients = config.numClients; this.maxConcurrentLimboResolutions = config.maxConcurrentLimboResolutions; this.expectedActiveLimboDocs = []; @@ -318,7 +327,7 @@ abstract class TestRunner { await this.initializeOfflineComponentProvider( onlineComponentProvider, configuration, - this.useGarbageCollection + this.useEagerGCForMemory ); await onlineComponentProvider.initialize( offlineComponentProvider, @@ -327,6 +336,15 @@ abstract class TestRunner { this.sharedClientState = offlineComponentProvider.sharedClientState; this.persistence = offlineComponentProvider.persistence; + // Setting reference to lru collector for manual runs + if ( + this.persistence.referenceDelegate instanceof MemoryLruDelegate || + this.persistence.referenceDelegate instanceof IndexedDbLruDelegateImpl + ) { + this.lruGarbageCollector = + this.persistence.referenceDelegate.garbageCollector; + } + this.localStore = offlineComponentProvider.localStore; this.remoteStore = onlineComponentProvider.remoteStore; this.syncEngine = onlineComponentProvider.syncEngine; @@ -348,7 +366,7 @@ abstract class TestRunner { protected abstract initializeOfflineComponentProvider( onlineComponentProvider: MockOnlineComponentProvider, configuration: ComponentConfiguration, - gcEnabled: boolean + eagerGcEnabled: boolean ): Promise< MockMultiTabOfflineComponentProvider | MockMemoryOfflineComponentProvider >; @@ -442,6 +460,8 @@ abstract class TestRunner { } else if ('applyClientState' in step) { // PORTING NOTE: Only used by web multi-tab tests. return this.doApplyClientState(step.applyClientState!); + } else if ('triggerLruGC' in step) { + return this.doTriggerLruGC(step.triggerLruGC!); } else if ('changeUser' in step) { return this.doChangeUser(step.changeUser!); } else if ('failDatabase' in step) { @@ -871,6 +891,17 @@ abstract class TestRunner { ); } + private async doTriggerLruGC(cacheThreshold: number): Promise { + return this.queue.enqueue(async () => { + if (!!this.lruGarbageCollector) { + this.lruGarbageCollector.params.cacheSizeCollectionThreshold = + cacheThreshold; + this.lruGarbageCollector.params.percentileToCollect = 100; + await this.localStore.collectGarbage(this.lruGarbageCollector); + } + }); + } + private async doFailDatabase( failActions: PersistenceAction[] ): Promise { @@ -1216,9 +1247,11 @@ class MemoryTestRunner extends TestRunner { protected async initializeOfflineComponentProvider( onlineComponentProvider: MockOnlineComponentProvider, configuration: ComponentConfiguration, - gcEnabled: boolean + eagerGCEnabled: boolean ): Promise { - const componentProvider = new MockMemoryOfflineComponentProvider(gcEnabled); + const componentProvider = new MockMemoryOfflineComponentProvider( + eagerGCEnabled + ); await componentProvider.initialize(configuration); return componentProvider; } @@ -1241,7 +1274,7 @@ class IndexedDbTestRunner extends TestRunner { protected async initializeOfflineComponentProvider( onlineComponentProvider: MockOnlineComponentProvider, configuration: ComponentConfiguration, - gcEnabled: boolean + _: boolean ): Promise { const offlineComponentProvider = new MockMultiTabOfflineComponentProvider( testWindow(this.sharedFakeWebStorage), @@ -1329,8 +1362,8 @@ export async function runSpec( /** Specifies initial configuration information for the test. */ export interface SpecConfig { - /** A boolean to enable / disable GC. */ - useGarbageCollection: boolean; + /** A boolean to enable / disable eager GC for memory persistence. */ + useEagerGCForMemory: boolean; /** The number of active clients for this test run. */ numClients: number; @@ -1453,6 +1486,9 @@ export interface SpecStep { /** Change to a new active user (specified by uid or null for anonymous). */ changeUser?: string | null; + /** Trigger a GC event with given cache threshold in bytes. */ + triggerLruGC?: number; + /** * Restarts the SyncEngine from scratch, except re-uses persistence and auth * components. This allows you to queue writes, get documents into cache, diff --git a/packages/firestore/test/unit/specs/write_spec.test.ts b/packages/firestore/test/unit/specs/write_spec.test.ts index a4f4396fb74..64de299eaef 100644 --- a/packages/firestore/test/unit/specs/write_spec.test.ts +++ b/packages/firestore/test/unit/specs/write_spec.test.ts @@ -109,7 +109,7 @@ describeSpec('Writes:', [], () => { v: 1 }).setHasLocalMutations(); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userSets('collection/doc', { v: 1 }) .userListens(query1) .expectEvents(query1, { @@ -129,7 +129,7 @@ describeSpec('Writes:', [], () => { v: 1 }).setHasCommittedMutations(); return spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userSets('collection/doc', { v: 1 }) .writeAcks('collection/doc', 1000) .userListens(query1) @@ -146,7 +146,7 @@ describeSpec('Writes:', [], () => { const docV2 = doc('collection/doc', /* remoteVersion= */ 1000, { v: 2 }); return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userListens(query1) .watchAcksFull(query1, 250) .expectEvents(query1, {}) @@ -291,7 +291,7 @@ describeSpec('Writes:', [], () => { return ( spec() - .withGCEnabled(false) + .withEagerGCForMemoryPersistence(false) .userSets('collection/doc', { local: 1 }) .userListens(query1) .expectEvents(query1, { From 107a051f43fe2c0a51014a202649563281eedc48 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 11 Jan 2023 15:12:40 -0500 Subject: [PATCH 03/39] lint --- .../firestore/test/integration/api/get_options.test.ts | 2 +- .../test/unit/specs/garbage_collection_spect.test.ts | 1 - .../firestore/test/unit/specs/spec_test_components.ts | 8 +------- packages/firestore/test/unit/specs/spec_test_runner.ts | 5 ----- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/firestore/test/integration/api/get_options.test.ts b/packages/firestore/test/integration/api/get_options.test.ts index 4a147a883e7..5c0b96d30c3 100644 --- a/packages/firestore/test/integration/api/get_options.test.ts +++ b/packages/firestore/test/integration/api/get_options.test.ts @@ -16,8 +16,8 @@ */ import { expect } from 'chai'; -import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../../../src/local/lru_garbage_collector_impl'; +import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../../../src/local/lru_garbage_collector_impl'; import { collection, deleteDoc, diff --git a/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts b/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts index aeabf5b5692..913ae1dcf35 100644 --- a/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts +++ b/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import { setLogLevel } from '@firebase/logger'; import { doc, filter, query } from '../../util/helpers'; import { describeSpec, specTest } from './describe_spec'; diff --git a/packages/firestore/test/unit/specs/spec_test_components.ts b/packages/firestore/test/unit/specs/spec_test_components.ts index d595eed183b..72a390bdbe2 100644 --- a/packages/firestore/test/unit/specs/spec_test_components.ts +++ b/packages/firestore/test/unit/specs/spec_test_components.ts @@ -32,13 +32,7 @@ import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { LocalStore } from '../../../src/local/local_store'; -import { - ActiveTargets, - LruGarbageCollector, - LruDelegate, - LruParams, - LruResults -} from '../../../src/local/lru_garbage_collector'; +import { LruParams } from '../../../src/local/lru_garbage_collector'; import { LruGarbageCollectorImpl } from '../../../src/local/lru_garbage_collector_impl'; import { MemoryEagerDelegate, diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index a84e220450f..cf15fed01c1 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -79,11 +79,6 @@ import { } from '../../../src/local/indexeddb_sentinels'; import { LocalStore } from '../../../src/local/local_store'; import { localStoreConfigureFieldIndexes } from '../../../src/local/local_store_impl'; -import { - LruGarbageCollector, - LruParams, - LruResults -} from '../../../src/local/lru_garbage_collector'; import { MemoryLruDelegate } from '../../../src/local/memory_persistence'; import { ClientId, From 14ed4f332e7b0c4e63c0799e782eb254b8ff6389 Mon Sep 17 00:00:00 2001 From: wu-hui Date: Wed, 11 Jan 2023 20:38:39 +0000 Subject: [PATCH 04/39] Update API reports --- common/api-review/firestore.api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 5dd5742b318..8c5b47d4445 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -163,6 +163,9 @@ export { EmulatorMockTokenOptions } // @public export function enableIndexedDbPersistence(firestore: Firestore, persistenceSettings?: PersistenceSettings): Promise; +// @public +export function enableMemoryLRUGarbageCollection(firestore: Firestore): Promise; + // @public export function enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise; From 7b9d18d7fecfba5978bcd5e3f84aaef7d0e733c2 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 12 Jan 2023 12:03:00 -0500 Subject: [PATCH 05/39] Spec test clean up --- .../test/unit/specs/bundle_spec.test.ts | 6 ++-- .../unit/specs/existence_filter_spec.test.ts | 2 +- .../specs/garbage_collection_spect.test.ts | 8 ++--- .../test/unit/specs/limbo_spec.test.ts | 12 +++---- .../test/unit/specs/limit_spec.test.ts | 14 ++++---- .../test/unit/specs/listen_spec.test.ts | 34 +++++++++---------- .../test/unit/specs/orderby_spec.test.ts | 2 +- .../test/unit/specs/perf_spec.test.ts | 18 +++++----- .../test/unit/specs/persistence_spec.test.ts | 4 +-- .../test/unit/specs/recovery_spec.test.ts | 12 +++---- .../test/unit/specs/remote_store_spec.test.ts | 6 ++-- .../test/unit/specs/resume_token_spec.test.ts | 2 +- .../firestore/test/unit/specs/spec_builder.ts | 10 +++--- .../test/unit/specs/write_spec.test.ts | 8 ++--- 14 files changed, 69 insertions(+), 69 deletions(-) diff --git a/packages/firestore/test/unit/specs/bundle_spec.test.ts b/packages/firestore/test/unit/specs/bundle_spec.test.ts index 2027e69abb8..ef4f04ee34f 100644 --- a/packages/firestore/test/unit/specs/bundle_spec.test.ts +++ b/packages/firestore/test/unit/specs/bundle_spec.test.ts @@ -178,7 +178,7 @@ describeSpec('Bundles:', ['no-ios'], () => { spec() // TODO(b/160878667): Figure out what happens when memory eager GC is on // a bundle is loaded. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 250, docA) .expectEvents(query1, { @@ -224,7 +224,7 @@ describeSpec('Bundles:', ['no-ios'], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 250, docA) .expectEvents(query1, { @@ -260,7 +260,7 @@ describeSpec('Bundles:', ['no-ios'], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 250) // Backend tells is there is no such doc. diff --git a/packages/firestore/test/unit/specs/existence_filter_spec.test.ts b/packages/firestore/test/unit/specs/existence_filter_spec.test.ts index a40af3d30f4..e2b5e5b2bf6 100644 --- a/packages/firestore/test/unit/specs/existence_filter_spec.test.ts +++ b/packages/firestore/test/unit/specs/existence_filter_spec.test.ts @@ -69,7 +69,7 @@ describeSpec('Existence Filters:', [], () => { const doc1 = doc('collection/1', 2000, { v: 2 }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { added: [doc1] }) diff --git a/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts b/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts index 913ae1dcf35..41af3b5f656 100644 --- a/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts +++ b/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts @@ -44,7 +44,7 @@ describeSpec('Garbage Collection:', [], () => { const query1 = query('collection'); const docA = doc('collection/a', 1000, { key: 'a' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -61,7 +61,7 @@ describeSpec('Garbage Collection:', [], () => { const query1 = query('collection'); const docA = doc('collection/a', 1000, { key: 'a' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -81,7 +81,7 @@ describeSpec('Garbage Collection:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -110,7 +110,7 @@ describeSpec('Garbage Collection:', [], () => { const docD = doc('collection/d', 1000, { key: 'd' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(queryFull) .watchAcksFull(queryFull, 1000, docA, docB, docC, docD) .expectEvents(queryFull, { added: [docA, docB, docC, docD] }) diff --git a/packages/firestore/test/unit/specs/limbo_spec.test.ts b/packages/firestore/test/unit/specs/limbo_spec.test.ts index bebe0b4175b..0b6a420bbc8 100644 --- a/packages/firestore/test/unit/specs/limbo_spec.test.ts +++ b/packages/firestore/test/unit/specs/limbo_spec.test.ts @@ -203,7 +203,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() // No GC so we can keep the cache populated. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // Full query to populate the cache with docA and docB .userListens(fullQuery) @@ -275,7 +275,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() // No GC so we can keep the cache populated. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // Full query to populate the cache with docA and docB .userListens(fullQuery) @@ -512,7 +512,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userSets('collection/a', { matches: true }) .userSets('collection/b', { matches: true }) .writeAcks('collection/a', 1000) @@ -926,7 +926,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // Start a limbo resolution listen for a document (doc1). .userListens(fullQuery) @@ -964,7 +964,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .withMaxConcurrentLimboResolutions(1) // Max out the number of active limbo resolutions. @@ -1014,7 +1014,7 @@ describeSpec('Limbo Documents:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .withMaxConcurrentLimboResolutions(1) // Max out the number of active limbo resolutions. diff --git a/packages/firestore/test/unit/specs/limit_spec.test.ts b/packages/firestore/test/unit/specs/limit_spec.test.ts index b02502f944f..80b12d19a46 100644 --- a/packages/firestore/test/unit/specs/limit_spec.test.ts +++ b/packages/firestore/test/unit/specs/limit_spec.test.ts @@ -51,7 +51,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1000, { key: 'b' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, doc1, doc2) .expectEvents(query1, { @@ -165,7 +165,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { matches: true }); const doc3 = doc('collection/c', 1000, { matches: true }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(fullQuery) .watchAcksFull(fullQuery, 1002, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -192,7 +192,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { matches: true }); const doc3 = doc('collection/c', 1003, { matches: true }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(fullQuery) .watchAcksFull(fullQuery, 1003, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -226,7 +226,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { pos: 2 }); const doc3 = doc('collection/c', 1003, { pos: 3 }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(fullQuery) .watchAcksFull(fullQuery, 1003, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -262,7 +262,7 @@ describeSpec('Limits:', [], () => { const doc2 = doc('collection/b', 1002, { pos: 2 }); const doc3 = doc('collection/c', 1003, { pos: 3 }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(fullQuery) .watchAcksFull(fullQuery, 1003, doc1, doc2, doc3) .expectEvents(fullQuery, { added: [doc1, doc2, doc3] }) @@ -303,7 +303,7 @@ describeSpec('Limits:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(limitQuery) .watchAcksFull(limitQuery, 1001, firstDocument) .expectEvents(limitQuery, { added: [firstDocument] }) @@ -379,7 +379,7 @@ describeSpec('Limits:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // We issue a limit query with an orderBy constraint. .userListens(limitQuery) .watchAcksFull(limitQuery, 2001, firstDocument) diff --git a/packages/firestore/test/unit/specs/listen_spec.test.ts b/packages/firestore/test/unit/specs/listen_spec.test.ts index 4d114a82d95..68efbebd6af 100644 --- a/packages/firestore/test/unit/specs/listen_spec.test.ts +++ b/packages/firestore/test/unit/specs/listen_spec.test.ts @@ -210,7 +210,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection'); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcks(query1) .watchRemoves( @@ -265,7 +265,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docAv1) .expectEvents(query1, { added: [docAv1] }) @@ -301,7 +301,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docAv1) .expectEvents(query1, { added: [docAv1] }) @@ -334,7 +334,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(visibleQuery) .watchAcksFull(visibleQuery, 1000, docAv1) .expectEvents(visibleQuery, { added: [docAv1] }) @@ -377,7 +377,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(visibleQuery) .watchAcksFull(visibleQuery, 1000, docAv1) .expectEvents(visibleQuery, { added: [docAv1] }) @@ -474,7 +474,7 @@ describeSpec('Listens:', [], () => { const docB = doc('collection/b', 1000, { key: 'a' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // Add a collection query with two documents, one of which gets deleted // (the second document guarantees that we later raise an event from // cache). @@ -508,7 +508,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); const deletedDocA = deletedDoc('collection/a', 2000); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -528,7 +528,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); const docB = doc('collection/b', 2000, { key: 'b' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -548,7 +548,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcks(query1) .watchSends({ affects: [query1] }, docA) @@ -570,7 +570,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection'); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchRemoves( query1, @@ -586,7 +586,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection'); const docA = doc('collection/a', 2000, { key: 'a' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000) .expectEvents(query1, {}) @@ -606,7 +606,7 @@ describeSpec('Listens:', [], () => { const query1 = query('collection', filter('array', 'array-contains', 42)); const docA = doc('collection/a', 2000, { foo: 'bar', array: [1, 42, 3] }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000) .expectEvents(query1, {}) @@ -628,7 +628,7 @@ describeSpec('Listens:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -657,7 +657,7 @@ describeSpec('Listens:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) @@ -690,7 +690,7 @@ describeSpec('Listens:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, initialVersion, docA) .expectEvents(query1, { added: [docA] }) @@ -1704,7 +1704,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // Populate the cache with the empty query results. .userListens(query1) .watchAcksFull(query1, 1000) @@ -1731,7 +1731,7 @@ describeSpec('Listens:', [], () => { return ( spec() // Disable GC so the cache persists across listens. - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() // Populate the cache with the empty query results. .userListens(query1) .watchAcksFull(query1, 1000, doc1) diff --git a/packages/firestore/test/unit/specs/orderby_spec.test.ts b/packages/firestore/test/unit/specs/orderby_spec.test.ts index afbd223b2b2..daea9f09950 100644 --- a/packages/firestore/test/unit/specs/orderby_spec.test.ts +++ b/packages/firestore/test/unit/specs/orderby_spec.test.ts @@ -57,7 +57,7 @@ describeSpec('OrderBy:', [], () => { const docB = doc('collection/b', 1001, { key: 'b', sort: 1 }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1002, docA, docB) .expectEvents(query1, { added: [docB, docA] }) diff --git a/packages/firestore/test/unit/specs/perf_spec.test.ts b/packages/firestore/test/unit/specs/perf_spec.test.ts index 3a17d73c61d..45e953b523d 100644 --- a/packages/firestore/test/unit/specs/perf_spec.test.ts +++ b/packages/firestore/test/unit/specs/perf_spec.test.ts @@ -30,7 +30,7 @@ describeSpec( ['benchmark'], () => { specTest('Insert a new document', [], () => { - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); for (let i = 0; i < STEP_COUNT; ++i) { steps .userSets(`collection/{i}`, { doc: i }) @@ -44,7 +44,7 @@ describeSpec( [], () => { let currentVersion = 1; - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); for (let i = 0; i < STEP_COUNT; ++i) { const query1 = query(`collection/${i}`); @@ -77,7 +77,7 @@ describeSpec( const cachedDocumentCount = 100; 9; const query1 = query(`collection`, orderBy('v')); - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); const docs: Document[] = []; for (let i = 0; i < cachedDocumentCount; ++i) { @@ -101,7 +101,7 @@ describeSpec( specTest('Update a single document', [], () => { const steps = spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userSets(`collection/doc`, { v: 0 }); for (let i = 1; i <= STEP_COUNT; ++i) { @@ -119,7 +119,7 @@ describeSpec( const query1 = query(`collection/doc`); let currentVersion = 1; - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); let docLocal = doc(`collection/doc`, 0, { v: 0 @@ -168,7 +168,7 @@ describeSpec( const documentsPerStep = 100; const query1 = query(`collection`, orderBy('v')); - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); let currentVersion = 1; @@ -206,7 +206,7 @@ describeSpec( const documentsPerStep = 100; let currentVersion = 1; - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); for (let i = 1; i <= STEP_COUNT; ++i) { const collPath = `collection/${i}/coll`; @@ -241,7 +241,7 @@ describeSpec( const queriesPerStep = 25; let currentVersion = 1; - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); for (let i = 1; i <= STEP_COUNT; ++i) { // We use a different subcollection for each iteration to ensure @@ -290,7 +290,7 @@ describeSpec( const matchingCount = 10; const queryCount = 10; - const steps = spec().withEagerGCForMemoryPersistence(false); + const steps = spec().ensureManualLruGC(); const collPath = `collection`; const query1 = query(collPath, orderBy('val')); diff --git a/packages/firestore/test/unit/specs/persistence_spec.test.ts b/packages/firestore/test/unit/specs/persistence_spec.test.ts index c3d57b1a2d4..34a333f60cf 100644 --- a/packages/firestore/test/unit/specs/persistence_spec.test.ts +++ b/packages/firestore/test/unit/specs/persistence_spec.test.ts @@ -78,7 +78,7 @@ describeSpec('Persistence:', [], () => { const doc1 = doc('collection/key', 1000, { foo: 'bar' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { added: [doc1] }) @@ -93,7 +93,7 @@ describeSpec('Persistence:', [], () => { const query1 = query('collection'); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userSets('collection/key', { foo: 'bar' }) // Normally the write would get GC'd from remote documents here. .writeAcks('collection/key', 1000) diff --git a/packages/firestore/test/unit/specs/recovery_spec.test.ts b/packages/firestore/test/unit/specs/recovery_spec.test.ts index 349ad64f6cb..2d522babd94 100644 --- a/packages/firestore/test/unit/specs/recovery_spec.test.ts +++ b/packages/firestore/test/unit/specs/recovery_spec.test.ts @@ -473,7 +473,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const deletedDoc1 = deletedDoc('collection/key1', 2000); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { @@ -533,7 +533,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc2 = doc('collection/key2', 2000, { foo: 'b' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { @@ -569,7 +569,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc2 = doc('collection/key2', 2000, { foo: 'b' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(doc1Query) .watchAcksFull(doc1Query, 1000, doc1a) .expectEvents(doc1Query, { @@ -617,7 +617,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc1b = doc('collection/key1', 1500, { included: false }); const limboQuery = newQueryForPath(doc1a.key.path); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(fullQuery) .watchAcksFull(fullQuery, 1000, doc1a) .expectEvents(fullQuery, { @@ -658,7 +658,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc1 = doc('collection/key1', 1, { included: true }); const limboQuery = newQueryForPath(doc1.key.path); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(fullQuery) .watchAcksFull(fullQuery, 1000, doc1) .expectEvents(fullQuery, { @@ -796,7 +796,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => { const doc1 = doc('collection/key1', 1, { foo: 'a' }); const doc2 = doc('collection/key2', 2, { foo: 'b' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 1000, doc1) .expectEvents(query1, { diff --git a/packages/firestore/test/unit/specs/remote_store_spec.test.ts b/packages/firestore/test/unit/specs/remote_store_spec.test.ts index 3593332f6a2..7c923aa67f2 100644 --- a/packages/firestore/test/unit/specs/remote_store_spec.test.ts +++ b/packages/firestore/test/unit/specs/remote_store_spec.test.ts @@ -26,7 +26,7 @@ describeSpec('Remote store:', [], () => { const query1 = query('collection'); const doc1 = doc('collection/a', 1000, { key: 'a' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcks(query1) .userUnlistens(query1) // Now we simulate a quick unlisten. @@ -46,7 +46,7 @@ describeSpec('Remote store:', [], () => { const doc3 = doc('collection/c', 1000, { key: 'c' }); const doc4 = doc('collection/d', 1000, { key: 'd' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcks(query1) .userUnlistens(query1) // Now we simulate a quick unlisten. @@ -72,7 +72,7 @@ describeSpec('Remote store:', [], () => { const doc1 = doc('collection/a', 1000, { key: 'a' }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) // Close before we get an ack, this should reset our pending // target counts. diff --git a/packages/firestore/test/unit/specs/resume_token_spec.test.ts b/packages/firestore/test/unit/specs/resume_token_spec.test.ts index 4c5feaab79f..8d8d8181192 100644 --- a/packages/firestore/test/unit/specs/resume_token_spec.test.ts +++ b/packages/firestore/test/unit/specs/resume_token_spec.test.ts @@ -43,7 +43,7 @@ describeSpec('Resume tokens:', [], () => { const query1 = query('collection'); const doc1 = doc('collection/a', 1000, { key: 'a' }); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcks(query1) .watchSends({ affects: [query1] }, doc1) diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 217a66adf90..106ad2233bb 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -240,14 +240,14 @@ export class SpecBuilder { return runSpec(name, tags, usePersistence, this.config, this.steps); } - // Configures Eager Garbage Collection behavior (on or off) for memory persistence. Default is on. - // For disk persistence, this has no effect, manual LRU GC is always used. - withEagerGCForMemoryPersistence(eagerGCEnabled: boolean): this { + // Ensures manual LRU GC for both memory and indexeddb persistence. + // In spec tests, GC is always manually triggered via triggerLruGC(). + ensureManualLruGC(): this { debugAssert( !this.currentStep, 'withGCEnabled() must be called before all spec steps.' ); - this.config.useEagerGCForMemory = eagerGCEnabled; + this.config.useEagerGCForMemory = false; return this; } @@ -1258,6 +1258,6 @@ export function client( withGcEnabled?: boolean ): MultiClientSpecBuilder { const specBuilder = new MultiClientSpecBuilder(); - specBuilder.withEagerGCForMemoryPersistence(withGcEnabled === true); + specBuilder.ensureManualLruGC(); return specBuilder.client(num); } diff --git a/packages/firestore/test/unit/specs/write_spec.test.ts b/packages/firestore/test/unit/specs/write_spec.test.ts index 64de299eaef..9965382c750 100644 --- a/packages/firestore/test/unit/specs/write_spec.test.ts +++ b/packages/firestore/test/unit/specs/write_spec.test.ts @@ -109,7 +109,7 @@ describeSpec('Writes:', [], () => { v: 1 }).setHasLocalMutations(); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userSets('collection/doc', { v: 1 }) .userListens(query1) .expectEvents(query1, { @@ -129,7 +129,7 @@ describeSpec('Writes:', [], () => { v: 1 }).setHasCommittedMutations(); return spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userSets('collection/doc', { v: 1 }) .writeAcks('collection/doc', 1000) .userListens(query1) @@ -146,7 +146,7 @@ describeSpec('Writes:', [], () => { const docV2 = doc('collection/doc', /* remoteVersion= */ 1000, { v: 2 }); return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userListens(query1) .watchAcksFull(query1, 250) .expectEvents(query1, {}) @@ -291,7 +291,7 @@ describeSpec('Writes:', [], () => { return ( spec() - .withEagerGCForMemoryPersistence(false) + .ensureManualLruGC() .userSets('collection/doc', { local: 1 }) .userListens(query1) .expectEvents(query1, { From 2ea1fad4880c2ee8a9ddb3df72c81875a147e9f4 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 12 Jan 2023 12:48:51 -0500 Subject: [PATCH 06/39] Fix error and polishing. --- packages/firestore/src/api/database.ts | 6 ++-- .../test/integration/api/database.test.ts | 27 +++++++-------- .../test/integration/api/get_options.test.ts | 33 ++++++++----------- .../test/integration/util/helpers.ts | 5 ++- 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 9cf53d06e24..b94da1a99b0 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -287,7 +287,7 @@ export function configureFirestore(firestore: Firestore): void { } /** - * Attempts to enable the LRU garbage collector for memory persistence. + * Attempts to enable LRU garbage collection for memory persistence. * * Must be called before any other functions (other than * {@link initializeFirestore}, {@link (getFirestore:1)} or @@ -300,8 +300,8 @@ export function configureFirestore(firestore: Firestore): void { * garbage collector. Documents will be collected when their total size exceeds * `Settings.cacheSizeBytes`, with least recently used documents get removed first. * - * @param firestore - The {@link Firestore} instance to enable persistence for. - * @returns A `Promise` that represents successfully enabling persistent storage. + * @param firestore - The {@link Firestore} instance to enable LRU garbage collection for. + * @returns A `Promise` that represents successfully enabling LRU garbage collection. */ export function enableMemoryLRUGarbageCollection( firestore: Firestore diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 420aae5bbcc..cdbc36ca6d5 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -20,7 +20,6 @@ import { Deferred } from '@firebase/util'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; -import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../../../src/local/lru_garbage_collector_impl'; import { EventsAccumulator } from '../util/events_accumulator'; import { addDoc, @@ -68,7 +67,7 @@ import { } from '../util/firebase_export'; import { apiDescribe, - withEnsuredGcTestDb, + withEnsuredLruGcTestDb, withTestCollection, withTestDbsSettings, withTestDb, @@ -1756,19 +1755,15 @@ apiDescribe('Database', (persistence: boolean) => { it('Can get document from cache with GC enabled.', () => { const initialData = { key: 'value' }; - return withEnsuredGcTestDb( - persistence, - LRU_MINIMUM_CACHE_SIZE_BYTES, - async db => { - const docRef = doc(collection(db, 'test-collection')); - await setDoc(docRef, initialData); - return getDoc(docRef).then(doc => { - expect(doc.exists()).to.be.true; - expect(doc.metadata.fromCache).to.be.false; - expect(doc.metadata.hasPendingWrites).to.be.false; - expect(doc.data()).to.deep.equal(initialData); - }); - } - ); + return withEnsuredLruGcTestDb(persistence, async db => { + const docRef = doc(collection(db, 'test-collection')); + await setDoc(docRef, initialData); + return getDoc(docRef).then(doc => { + expect(doc.exists()).to.be.true; + expect(doc.metadata.fromCache).to.be.false; + expect(doc.metadata.hasPendingWrites).to.be.false; + expect(doc.data()).to.deep.equal(initialData); + }); + }); }); }); diff --git a/packages/firestore/test/integration/api/get_options.test.ts b/packages/firestore/test/integration/api/get_options.test.ts index 5c0b96d30c3..000d3899307 100644 --- a/packages/firestore/test/integration/api/get_options.test.ts +++ b/packages/firestore/test/integration/api/get_options.test.ts @@ -17,7 +17,6 @@ import { expect } from 'chai'; -import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../../../src/local/lru_garbage_collector_impl'; import { collection, deleteDoc, @@ -37,7 +36,7 @@ import { apiDescribe, withTestCollection, withTestDocAndInitialData, - withEnsuredGcTestDb + withEnsuredLruGcTestDb } from '../util/helpers'; apiDescribe('GetOptions', (persistence: boolean) => { @@ -72,23 +71,19 @@ apiDescribe('GetOptions', (persistence: boolean) => { it('get document while offline with default get options', () => { const initialData = { key: 'value' }; // Use an instance with Gc turned on. - return withEnsuredGcTestDb( - persistence, - LRU_MINIMUM_CACHE_SIZE_BYTES, - async db => { - const docRef = doc(collection(db, 'test-collection')); - await setDoc(docRef, initialData); - return getDoc(docRef) - .then(() => disableNetwork(db)) - .then(() => getDoc(docRef)) - .then(doc => { - expect(doc.exists()).to.be.true; - expect(doc.metadata.fromCache).to.be.true; - expect(doc.metadata.hasPendingWrites).to.be.false; - expect(doc.data()).to.deep.equal(initialData); - }); - } - ); + return withEnsuredLruGcTestDb(persistence, async db => { + const docRef = doc(collection(db, 'test-collection')); + await setDoc(docRef, initialData); + return getDoc(docRef) + .then(() => disableNetwork(db)) + .then(() => getDoc(docRef)) + .then(doc => { + expect(doc.exists()).to.be.true; + expect(doc.metadata.fromCache).to.be.true; + expect(doc.metadata.hasPendingWrites).to.be.false; + expect(doc.data()).to.deep.equal(initialData); + }); + }); }); it('get collection while offline with default get options', () => { diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 4d4dbf8a5a9..2852a3a1ebb 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -142,15 +142,14 @@ export function withTestDb( }); } -export function withEnsuredGcTestDb( +export function withEnsuredLruGcTestDb( persistence: boolean, - sizeBytes: number, fn: (db: Firestore) => Promise ): Promise { return withTestDbsSettings( persistence, DEFAULT_PROJECT_ID, - { ...DEFAULT_SETTINGS, cacheSizeBytes: sizeBytes }, + { ...DEFAULT_SETTINGS, cacheSizeBytes: 1 * 1024 * 1024 }, 1, async ([db]) => { if (!persistence) { From 92b1cac118b3424b9fae8fb15cb8915bff086277 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 12 Jan 2023 13:08:10 -0500 Subject: [PATCH 07/39] Fix a build error --- .../src/local/lru_garbage_collector_impl.ts | 4 ++-- .../test/unit/specs/spec_test_components.ts | 5 ----- .../firestore/test/unit/specs/spec_test_runner.ts | 13 ++++++++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/firestore/src/local/lru_garbage_collector_impl.ts b/packages/firestore/src/local/lru_garbage_collector_impl.ts index b88412cc29a..745d82cb792 100644 --- a/packages/firestore/src/local/lru_garbage_collector_impl.ts +++ b/packages/firestore/src/local/lru_garbage_collector_impl.ts @@ -177,9 +177,9 @@ export class LruScheduler implements Scheduler { } /** - * Implements the steps for LRU garbage collection. Exported for testing purpose only. + * Implements the steps for LRU garbage collection. */ -export class LruGarbageCollectorImpl implements LruGarbageCollector { +class LruGarbageCollectorImpl implements LruGarbageCollector { constructor( private readonly delegate: LruDelegate, readonly params: LruParams diff --git a/packages/firestore/test/unit/specs/spec_test_components.ts b/packages/firestore/test/unit/specs/spec_test_components.ts index 72a390bdbe2..ec0d2bb5bb2 100644 --- a/packages/firestore/test/unit/specs/spec_test_components.ts +++ b/packages/firestore/test/unit/specs/spec_test_components.ts @@ -33,7 +33,6 @@ import { } from '../../../src/local/indexeddb_persistence'; import { LocalStore } from '../../../src/local/local_store'; import { LruParams } from '../../../src/local/lru_garbage_collector'; -import { LruGarbageCollectorImpl } from '../../../src/local/lru_garbage_collector_impl'; import { MemoryEagerDelegate, MemoryLruDelegate, @@ -228,10 +227,6 @@ export class MockMemoryOfflineComponentProvider extends MemoryOfflineComponentPr } } -// A LRU garbage collector whose params are mutable. -type Mutable = { -readonly [P in keyof T]: T[P] }; -export type MutableLruGarbageCollector = Mutable; - export class MockConnection implements Connection { watchStream: StreamBridge | null = null; diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index cf15fed01c1..bf9ed8e2f9f 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -79,6 +79,7 @@ import { } from '../../../src/local/indexeddb_sentinels'; import { LocalStore } from '../../../src/local/local_store'; import { localStoreConfigureFieldIndexes } from '../../../src/local/local_store_impl'; +import { LruGarbageCollector } from '../../../src/local/lru_garbage_collector'; import { MemoryLruDelegate } from '../../../src/local/memory_persistence'; import { ClientId, @@ -173,7 +174,6 @@ import { MockMemoryPersistence, MockMultiTabOfflineComponentProvider, MockOnlineComponentProvider, - MutableLruGarbageCollector, QueryEvent, SharedWriteTracker } from './spec_test_components'; @@ -248,7 +248,7 @@ abstract class TestRunner { private localStore!: LocalStore; private remoteStore!: RemoteStore; private persistence!: MockMemoryPersistence | MockIndexedDbPersistence; - private lruGarbageCollector!: MutableLruGarbageCollector; + private lruGarbageCollector!: LruGarbageCollector; protected sharedClientState!: SharedClientState; private useEagerGCForMemory: boolean; @@ -889,9 +889,12 @@ abstract class TestRunner { private async doTriggerLruGC(cacheThreshold: number): Promise { return this.queue.enqueue(async () => { if (!!this.lruGarbageCollector) { - this.lruGarbageCollector.params.cacheSizeCollectionThreshold = - cacheThreshold; - this.lruGarbageCollector.params.percentileToCollect = 100; + const params = this.lruGarbageCollector.params as { + cacheSizeCollectionThreshold: number; + percentileToCollect: number; + }; + params.cacheSizeCollectionThreshold = cacheThreshold; + params.percentileToCollect = 100; await this.localStore.collectGarbage(this.lruGarbageCollector); } }); From 4ed6691d0fb5c95fccbef24294d1fb5bd13e221a Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 8 Feb 2023 13:12:42 -0500 Subject: [PATCH 08/39] New way to config Firestore SDK Cache. --- packages/firestore/src/api.ts | 13 ++ packages/firestore/src/api/cache_config.ts | 152 ++++++++++++++++++ packages/firestore/src/api/database.ts | 19 +++ packages/firestore/src/api/settings.ts | 6 + .../firestore/src/core/firestore_client.ts | 46 ++++-- packages/firestore/src/lite-api/settings.ts | 11 ++ .../test/integration/util/helpers.ts | 8 +- 7 files changed, 240 insertions(+), 15 deletions(-) create mode 100644 packages/firestore/src/api/cache_config.ts diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 2742195eecd..33760205e0e 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -28,6 +28,19 @@ export { AggregateQuerySnapshot } from './lite-api/aggregate_types'; +export { + FirestoreLocalCache, + MemoryLocalCache, + IndexedDbLocalCache, + IndexedDbMultipleTabManager, + indexedDbLocalCache, + indexedDbMultipleTabManager, + indexedDbSingleTabManager, + IndexedDbSingleTabManager, + memoryLocalCache, + IndexedDbTabManager +} from './api/cache_config'; + export { FieldPath, documentId } from './api/field_path'; export { diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts new file mode 100644 index 00000000000..7bd37f71f02 --- /dev/null +++ b/packages/firestore/src/api/cache_config.ts @@ -0,0 +1,152 @@ +/** + * @license + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + IndexedDbOfflineComponentProvider, + MemoryOfflineComponentProvider, + MultiTabOfflineComponentProvider, + OfflineComponentProvider, + OnlineComponentProvider +} from '../core/component_provider'; + +export interface MemoryLocalCache { + kind: 'memory'; + _onlineComponentProvider: OnlineComponentProvider; + _offlineComponentProvider: MemoryOfflineComponentProvider; +} + +class MemoryLocalCacheImpl implements MemoryLocalCache { + kind: 'memory' = 'memory'; + _onlineComponentProvider: OnlineComponentProvider; + _offlineComponentProvider: MemoryOfflineComponentProvider; + + constructor() { + this._onlineComponentProvider = new OnlineComponentProvider(); + this._offlineComponentProvider = new MemoryOfflineComponentProvider(); + } +} + +export interface IndexedDbLocalCache { + kind: 'indexeddb'; + _onlineComponentProvider: OnlineComponentProvider; + _offlineComponentProvider: OfflineComponentProvider; +} + +class IndexedDbLocalCacheImpl implements IndexedDbLocalCache { + kind: 'indexeddb' = 'indexeddb'; + _onlineComponentProvider: OnlineComponentProvider; + _offlineComponentProvider: OfflineComponentProvider; + + constructor(settings: IndexedDbSettings | undefined) { + let tabManager: IndexedDbTabManager; + if (settings?.tabManager) { + settings.tabManager.initialize(settings); + tabManager = settings.tabManager; + } else { + tabManager = indexedDbSingleTabManager(undefined); + tabManager.initialize(settings); + } + this._onlineComponentProvider = tabManager._onlineComponentProvider!; + this._offlineComponentProvider = tabManager._offlineComponentProvider!; + } +} + +export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; + +// Factory function +export function memoryLocalCache(): MemoryLocalCache { + return new MemoryLocalCacheImpl(); +} + +export interface IndexedDbSettings { + cacheSizeBytes?: number; + // default to singleTabManager({forceOwnership: false}) + tabManager?: IndexedDbTabManager; +} + +// Factory function +export function indexedDbLocalCache( + settings?: IndexedDbSettings +): IndexedDbLocalCache { + return new IndexedDbLocalCacheImpl(settings); +} + +export interface IndexedDbSingleTabManager { + kind: 'indexedDbSingleTab'; + initialize: ( + settings: Omit | undefined + ) => void; + _onlineComponentProvider?: OnlineComponentProvider; + _offlineComponentProvider?: OfflineComponentProvider; +} + +class SingleTabManagerImpl implements IndexedDbSingleTabManager { + kind: 'indexedDbSingleTab' = 'indexedDbSingleTab'; + + _onlineComponentProvider?: OnlineComponentProvider; + _offlineComponentProvider?: OfflineComponentProvider; + + constructor(private forceOwnership?: boolean) {} + + initialize( + settings: Omit | undefined + ): void { + this._onlineComponentProvider = new OnlineComponentProvider(); + this._offlineComponentProvider = new IndexedDbOfflineComponentProvider( + this._onlineComponentProvider, + settings?.cacheSizeBytes, + this.forceOwnership + ); + } +} + +export interface IndexedDbMultipleTabManager { + kind: 'IndexedDbMultipleTab'; + initialize: (settings: Omit) => void; + _onlineComponentProvider?: OnlineComponentProvider; + _offlineComponentProvider?: OfflineComponentProvider; +} + +class MultiTabManagerImpl implements IndexedDbMultipleTabManager { + kind: 'IndexedDbMultipleTab' = 'IndexedDbMultipleTab'; + + _onlineComponentProvider?: OnlineComponentProvider; + _offlineComponentProvider?: OfflineComponentProvider; + + initialize( + settings: Omit | undefined + ): void { + this._onlineComponentProvider = new OnlineComponentProvider(); + this._offlineComponentProvider = new MultiTabOfflineComponentProvider( + this._onlineComponentProvider, + settings?.cacheSizeBytes + ); + } +} + +export type IndexedDbTabManager = + | IndexedDbSingleTabManager + | IndexedDbMultipleTabManager; + +export function indexedDbSingleTabManager( + settings: { forceOwnership?: boolean } | undefined +): IndexedDbSingleTabManager { + return new SingleTabManagerImpl(settings?.forceOwnership); +} +export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager { + return new MultiTabManagerImpl(); +} diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 29cca7e68ca..cb2f2daf373 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -283,6 +283,15 @@ export function configureFirestore(firestore: Firestore): void { firestore._queue, databaseInfo ); + if ( + settings.cache?._offlineComponentProvider && + settings.cache?._onlineComponentProvider + ) { + firestore._firestoreClient.uninitializedComponentsProvider = { + offline: settings.cache._offlineComponentProvider, + online: settings.cache._onlineComponentProvider + }; + } } /** @@ -308,6 +317,7 @@ export function configureFirestore(firestore: Firestore): void { * persistence. * @returns A `Promise` that represents successfully enabling persistent storage. */ +// TODO(wuandy): mark obselete export function enableIndexedDbPersistence( firestore: Firestore, persistenceSettings?: PersistenceSettings @@ -316,6 +326,10 @@ export function enableIndexedDbPersistence( verifyNotInitialized(firestore); const client = ensureFirestoreConfigured(firestore); + if (client.uninitializedComponentsProvider) { + throw new FirestoreError(Code.INVALID_ARGUMENT, 'Already specified.'); + } + const settings = firestore._freezeSettings(); const onlineComponentProvider = new OnlineComponentProvider(); @@ -353,6 +367,7 @@ export function enableIndexedDbPersistence( * @returns A `Promise` that represents successfully enabling persistent * storage. */ +// TODO(wuandy): mark obselete export function enableMultiTabIndexedDbPersistence( firestore: Firestore ): Promise { @@ -360,6 +375,10 @@ export function enableMultiTabIndexedDbPersistence( verifyNotInitialized(firestore); const client = ensureFirestoreConfigured(firestore); + if (client.uninitializedComponentsProvider) { + throw new FirestoreError(Code.INVALID_ARGUMENT, 'Already specified.'); + } + const settings = firestore._freezeSettings(); const onlineComponentProvider = new OnlineComponentProvider(); diff --git a/packages/firestore/src/api/settings.ts b/packages/firestore/src/api/settings.ts index f6e92854495..e56b2aa1bd9 100644 --- a/packages/firestore/src/api/settings.ts +++ b/packages/firestore/src/api/settings.ts @@ -17,6 +17,8 @@ import { FirestoreSettings as LiteSettings } from '../lite-api/settings'; +import { FirestoreLocalCache } from './cache_config'; + export { DEFAULT_HOST } from '../lite-api/settings'; /** @@ -30,6 +32,7 @@ export interface PersistenceSettings { * Workers. Setting this to `true` will enable persistence, but cause other * tabs using persistence to fail. */ + // TODO(wuandy): Deprecate this forceOwnership?: boolean; } @@ -48,8 +51,11 @@ export interface FirestoreSettings extends LiteSettings { * 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. */ + // TODO(wuandy): Deprecate this cacheSizeBytes?: number; + cache?: FirestoreLocalCache; + /** * Forces the SDK’s underlying network transport (WebChannel) to use * long-polling. Each response from the backend will be closed immediately diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 2c2c0af1771..67c7245c599 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -112,6 +112,10 @@ export class FirestoreClient { appCheckToken: string, user: User ) => Promise = () => Promise.resolve(); + uninitializedComponentsProvider?: { + offline: OfflineComponentProvider; + online: OnlineComponentProvider; + }; offlineComponents?: OfflineComponentProvider; onlineComponents?: OnlineComponentProvider; @@ -120,12 +124,12 @@ export class FirestoreClient { private authCredentials: CredentialsProvider, private appCheckCredentials: CredentialsProvider, /** - * Asynchronous queue responsible for all of our internal processing. When - * we get incoming work from the user (via public API) or the network - * (incoming GRPC messages), we should always schedule onto this queue. - * This ensures all of our work is properly serialized (e.g. we don't - * start processing a new operation while the previous one is waiting for - * an async I/O to complete). + * Asynchronous queue responsible for all of our internal processing. When // + * we get incoming work from the user (via public API) or the network // + * (incoming GRPC messages), we should always schedule onto this queue. // + * This ensures all of our work is properly serialized (e.g. we don't // + * start processing a new operation while the previous one is waiting for // + * an async I/O to complete). // */ public asyncQueue: AsyncQueue, private databaseInfo: DatabaseInfo @@ -265,11 +269,19 @@ async function ensureOfflineComponents( client: FirestoreClient ): Promise { if (!client.offlineComponents) { - logDebug(LOG_TAG, 'Using default OfflineComponentProvider'); - await setOfflineComponentProvider( - client, - new MemoryOfflineComponentProvider() - ); + if (client.uninitializedComponentsProvider) { + logDebug(LOG_TAG, 'Using user provided OfflineComponentProvider'); + await setOfflineComponentProvider( + client, + client.uninitializedComponentsProvider.offline + ); + } else { + logDebug(LOG_TAG, 'Using default OfflineComponentProvider'); + await setOfflineComponentProvider( + client, + new MemoryOfflineComponentProvider() + ); + } } return client.offlineComponents!; @@ -279,8 +291,16 @@ async function ensureOnlineComponents( client: FirestoreClient ): Promise { if (!client.onlineComponents) { - logDebug(LOG_TAG, 'Using default OnlineComponentProvider'); - await setOnlineComponentProvider(client, new OnlineComponentProvider()); + if (client.uninitializedComponentsProvider) { + logDebug(LOG_TAG, 'Using user provided OnlineComponentProvider'); + await setOnlineComponentProvider( + client, + client.uninitializedComponentsProvider.online + ); + } else { + logDebug(LOG_TAG, 'Using default OnlineComponentProvider'); + await setOnlineComponentProvider(client, new OnlineComponentProvider()); + } } return client.onlineComponents!; diff --git a/packages/firestore/src/lite-api/settings.ts b/packages/firestore/src/lite-api/settings.ts index 3743cc344d0..160188c33f7 100644 --- a/packages/firestore/src/lite-api/settings.ts +++ b/packages/firestore/src/lite-api/settings.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { FirestoreLocalCache } from '../api/cache_config'; import { CredentialsSettings } from '../api/credentials'; import { LRU_COLLECTION_DISABLED, @@ -23,6 +24,7 @@ import { import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../local/lru_garbage_collector_impl'; import { Code, FirestoreError } from '../util/error'; import { validateIsNotUsedTogether } from '../util/input_validation'; +import { logWarn } from '../util/log'; // settings() defaults: export const DEFAULT_HOST = 'firestore.googleapis.com'; @@ -60,6 +62,8 @@ export interface PrivateSettings extends FirestoreSettings { experimentalAutoDetectLongPolling?: boolean; // Used in firestore@exp useFetchStreams?: boolean; + + cache?: FirestoreLocalCache; } /** @@ -83,6 +87,7 @@ export class FirestoreSettingsImpl { readonly ignoreUndefinedProperties: boolean; readonly useFetchStreams: boolean; + readonly cache?: FirestoreLocalCache; // Can be a google-auth-library or gapi client. // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -105,6 +110,12 @@ export class FirestoreSettingsImpl { this.credentials = settings.credentials; this.ignoreUndefinedProperties = !!settings.ignoreUndefinedProperties; + logWarn( + `Setting offline cache to ${JSON.stringify( + settings + )} from PrivateSettings` + ); + this.cache = settings.cache; if (settings.cacheSizeBytes === undefined) { this.cacheSizeBytes = LRU_DEFAULT_CACHE_SIZE_BYTES; diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 79dbacaafa0..2624d958fa0 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -17,6 +17,9 @@ import { isIndexedDBAvailable } from '@firebase/util'; +import { indexedDbLocalCache } from '../../../src/api/cache_config'; +import { logWarn } from '../../../src/util/log'; + import { collection, doc, @@ -184,10 +187,11 @@ export async function withTestDbsSettings( const dbs: Firestore[] = []; for (let i = 0; i < numDbs; i++) { - const db = newTestFirestore(newTestApp(projectId), settings); + logWarn(`set persistence from helper: ${persistence}`); if (persistence) { - await enableIndexedDbPersistence(db); + settings.cache = indexedDbLocalCache(); } + const db = newTestFirestore(newTestApp(projectId), settings); dbs.push(db); } From 48d5077fafca462db20140062f701730122de0b3 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 9 Feb 2023 11:59:53 -0500 Subject: [PATCH 09/39] fixups --- packages/firestore/src/api/cache_config.ts | 28 +++++++++++++++---- .../firestore/src/core/firestore_client.ts | 12 ++++---- packages/firestore/src/lite-api/settings.ts | 10 +++---- .../api_internal/idle_timeout.test.ts | 2 +- .../test/integration/util/helpers.ts | 7 +++-- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 7bd37f71f02..1fa2c0f9589 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -38,6 +38,10 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { this._onlineComponentProvider = new OnlineComponentProvider(); this._offlineComponentProvider = new MemoryOfflineComponentProvider(); } + + toJSON() { + return { kind: this.kind }; + } } export interface IndexedDbLocalCache { @@ -54,15 +58,19 @@ class IndexedDbLocalCacheImpl implements IndexedDbLocalCache { 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 }; + } } export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; @@ -87,7 +95,7 @@ export function indexedDbLocalCache( export interface IndexedDbSingleTabManager { kind: 'indexedDbSingleTab'; - initialize: ( + _initialize: ( settings: Omit | undefined ) => void; _onlineComponentProvider?: OnlineComponentProvider; @@ -102,7 +110,11 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { constructor(private forceOwnership?: boolean) {} - initialize( + toJSON() { + return { kind: this.kind }; + } + + _initialize( settings: Omit | undefined ): void { this._onlineComponentProvider = new OnlineComponentProvider(); @@ -116,7 +128,7 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { export interface IndexedDbMultipleTabManager { kind: 'IndexedDbMultipleTab'; - initialize: (settings: Omit) => void; + _initialize: (settings: Omit) => void; _onlineComponentProvider?: OnlineComponentProvider; _offlineComponentProvider?: OfflineComponentProvider; } @@ -127,7 +139,11 @@ class MultiTabManagerImpl implements IndexedDbMultipleTabManager { _onlineComponentProvider?: OnlineComponentProvider; _offlineComponentProvider?: OfflineComponentProvider; - initialize( + toJSON() { + return { kind: this.kind }; + } + + _initialize( settings: Omit | undefined ): void { this._onlineComponentProvider = new OnlineComponentProvider(); diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 67c7245c599..b17fc8b47e1 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -124,12 +124,12 @@ export class FirestoreClient { private authCredentials: CredentialsProvider, private appCheckCredentials: CredentialsProvider, /** - * Asynchronous queue responsible for all of our internal processing. When // - * we get incoming work from the user (via public API) or the network // - * (incoming GRPC messages), we should always schedule onto this queue. // - * This ensures all of our work is properly serialized (e.g. we don't // - * start processing a new operation while the previous one is waiting for // - * an async I/O to complete). // + * Asynchronous queue responsible for all of our internal processing. When + * we get incoming work from the user (via public API) or the network + * (incoming GRPC messages), we should always schedule onto this queue. + * This ensures all of our work is properly serialized (e.g. we don't + * start processing a new operation while the previous one is waiting for + * an async I/O to complete). */ public asyncQueue: AsyncQueue, private databaseInfo: DatabaseInfo diff --git a/packages/firestore/src/lite-api/settings.ts b/packages/firestore/src/lite-api/settings.ts index 160188c33f7..dc4af779b38 100644 --- a/packages/firestore/src/lite-api/settings.ts +++ b/packages/firestore/src/lite-api/settings.ts @@ -110,11 +110,11 @@ export class FirestoreSettingsImpl { this.credentials = settings.credentials; this.ignoreUndefinedProperties = !!settings.ignoreUndefinedProperties; - logWarn( - `Setting offline cache to ${JSON.stringify( - settings - )} from PrivateSettings` - ); + // logWarn( + // `Setting offline cache to ${JSON.stringify( + // settings.cache + // )} from PrivateSettings` + // ); this.cache = settings.cache; if (settings.cacheSizeBytes === undefined) { diff --git a/packages/firestore/test/integration/api_internal/idle_timeout.test.ts b/packages/firestore/test/integration/api_internal/idle_timeout.test.ts index d0a31ee9461..f6d5f77f7e5 100644 --- a/packages/firestore/test/integration/api_internal/idle_timeout.test.ts +++ b/packages/firestore/test/integration/api_internal/idle_timeout.test.ts @@ -22,7 +22,7 @@ import { apiDescribe, withTestDb } from '../util/helpers'; import { asyncQueue } from '../util/internal_helpers'; apiDescribe('Idle Timeout', (persistence: boolean) => { - it('can write document after idle timeout', () => { + it.only('can write document after idle timeout', () => { return withTestDb(persistence, db => { const docRef = doc(collection(db, 'test-collection')); return setDoc(docRef, { foo: 'bar' }) diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 2624d958fa0..f8ecc1bfb3a 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -17,7 +17,10 @@ import { isIndexedDBAvailable } from '@firebase/util'; -import { indexedDbLocalCache } from '../../../src/api/cache_config'; +import { + indexedDbLocalCache, + memoryLocalCache +} from '../../../src/api/cache_config'; import { logWarn } from '../../../src/util/log'; import { @@ -187,7 +190,7 @@ export async function withTestDbsSettings( const dbs: Firestore[] = []; for (let i = 0; i < numDbs; i++) { - logWarn(`set persistence from helper: ${persistence}`); + // logWarn(`set persistence from helper: ${persistence}`); if (persistence) { settings.cache = indexedDbLocalCache(); } From 7d0b18694f0bb323ca907013fc468fd6f74beabc Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 9 Feb 2023 14:02:42 -0500 Subject: [PATCH 10/39] Fix some test failures --- packages/firestore/src/api.ts | 1 + packages/firestore/src/api/cache_config.ts | 8 ++++---- packages/firestore/src/api/database.ts | 11 +++++++++-- .../firestore/src/api/index_configuration.ts | 17 ++++++++--------- packages/firestore/src/core/firestore_client.ts | 15 +++++++++++++++ packages/firestore/src/lite-api/settings.ts | 1 - .../test/integration/api/database.test.ts | 1 + .../test/integration/api/validation.test.ts | 4 +--- .../api_internal/idle_timeout.test.ts | 2 +- .../firestore/test/integration/util/helpers.ts | 12 ++++-------- 10 files changed, 44 insertions(+), 28 deletions(-) diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 33760205e0e..e014f5b4d08 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -35,6 +35,7 @@ export { IndexedDbMultipleTabManager, indexedDbLocalCache, indexedDbMultipleTabManager, + IndexedDbSettings, indexedDbSingleTabManager, IndexedDbSingleTabManager, memoryLocalCache, diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 1fa2c0f9589..817f134acc1 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -39,7 +39,7 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { this._offlineComponentProvider = new MemoryOfflineComponentProvider(); } - toJSON() { + toJSON(): {} { return { kind: this.kind }; } } @@ -68,7 +68,7 @@ class IndexedDbLocalCacheImpl implements IndexedDbLocalCache { this._offlineComponentProvider = tabManager._offlineComponentProvider!; } - toJSON() { + toJSON(): {} { return { kind: this.kind }; } } @@ -110,7 +110,7 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { constructor(private forceOwnership?: boolean) {} - toJSON() { + toJSON(): {} { return { kind: this.kind }; } @@ -139,7 +139,7 @@ class MultiTabManagerImpl implements IndexedDbMultipleTabManager { _onlineComponentProvider?: OnlineComponentProvider; _offlineComponentProvider?: OfflineComponentProvider; - toJSON() { + toJSON(): {} { return { kind: this.kind }; } diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index cb2f2daf373..20a03abead2 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -288,6 +288,7 @@ export function configureFirestore(firestore: Firestore): void { settings.cache?._onlineComponentProvider ) { firestore._firestoreClient.uninitializedComponentsProvider = { + offlineKind: settings.cache.kind, offline: settings.cache._offlineComponentProvider, online: settings.cache._onlineComponentProvider }; @@ -327,7 +328,10 @@ export function enableIndexedDbPersistence( const client = ensureFirestoreConfigured(firestore); if (client.uninitializedComponentsProvider) { - throw new FirestoreError(Code.INVALID_ARGUMENT, 'Already specified.'); + throw new FirestoreError( + Code.FAILED_PRECONDITION, + 'SDK cache is already specified.' + ); } const settings = firestore._freezeSettings(); @@ -376,7 +380,10 @@ export function enableMultiTabIndexedDbPersistence( const client = ensureFirestoreConfigured(firestore); if (client.uninitializedComponentsProvider) { - throw new FirestoreError(Code.INVALID_ARGUMENT, 'Already specified.'); + throw new FirestoreError( + Code.FAILED_PRECONDITION, + 'SDK cache is already specified.' + ); } const settings = firestore._freezeSettings(); diff --git a/packages/firestore/src/api/index_configuration.ts b/packages/firestore/src/api/index_configuration.ts index 9ea31a28db1..c11a9665f46 100644 --- a/packages/firestore/src/api/index_configuration.ts +++ b/packages/firestore/src/api/index_configuration.ts @@ -15,9 +15,8 @@ * limitations under the License. */ -import { getLocalStore } from '../core/firestore_client'; +import { firestoreClientSetIndexConfiguration } from '../core/firestore_client'; import { fieldPathFromDotSeparatedString } from '../lite-api/user_data_reader'; -import { localStoreConfigureFieldIndexes } from '../local/local_store_impl'; import { FieldIndex, IndexKind, @@ -151,17 +150,17 @@ export function setIndexConfiguration( ): Promise { firestore = cast(firestore, Firestore); const client = ensureFirestoreConfigured(firestore); - - // PORTING NOTE: We don't return an error if the user has not enabled - // persistence since `enableIndexeddbPersistence()` can fail on the Web. - if (!client.offlineComponents?.indexBackfillerScheduler) { + if ( + !client.uninitializedComponentsProvider || + client.uninitializedComponentsProvider?.offlineKind === 'memory' + ) { + // PORTING NOTE: We don't return an error if the user has not enabled + // persistence since `enableIndexeddbPersistence()` can fail on the Web. logWarn('Cannot enable indexes when persistence is disabled'); return Promise.resolve(); } const parsedIndexes = parseIndexes(jsonOrConfiguration); - return getLocalStore(client).then(localStore => - localStoreConfigureFieldIndexes(localStore, parsedIndexes) - ); + return firestoreClientSetIndexConfiguration(client, parsedIndexes); } export function parseIndexes( diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index b17fc8b47e1..051337bdb80 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -31,6 +31,7 @@ import { User } from '../auth/user'; import { Query as LiteQuery } from '../lite-api/reference'; import { LocalStore } from '../local/local_store'; import { + localStoreConfigureFieldIndexes, localStoreExecuteQuery, localStoreGetNamedQuery, localStoreHandleUserChange, @@ -39,6 +40,7 @@ import { import { Persistence } from '../local/persistence'; import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; +import { FieldIndex } from '../model/field_index'; import { Mutation } from '../model/mutation'; import { toByteStreamReader } from '../platform/byte_stream_reader'; import { newSerializer, newTextEncoder } from '../platform/serializer'; @@ -114,6 +116,7 @@ export class FirestoreClient { ) => Promise = () => Promise.resolve(); uninitializedComponentsProvider?: { offline: OfflineComponentProvider; + offlineKind: 'memory' | 'indexeddb'; online: OnlineComponentProvider; }; @@ -768,3 +771,15 @@ function createBundleReader( } return newBundleReader(toByteStreamReader(content), serializer); } + +export function firestoreClientSetIndexConfiguration( + client: FirestoreClient, + indexes: FieldIndex[] +): Promise { + return client.asyncQueue.enqueue(async () => { + return localStoreConfigureFieldIndexes( + await getLocalStore(client), + indexes + ); + }); +} diff --git a/packages/firestore/src/lite-api/settings.ts b/packages/firestore/src/lite-api/settings.ts index dc4af779b38..9de2d0eb705 100644 --- a/packages/firestore/src/lite-api/settings.ts +++ b/packages/firestore/src/lite-api/settings.ts @@ -24,7 +24,6 @@ import { import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../local/lru_garbage_collector_impl'; import { Code, FirestoreError } from '../util/error'; import { validateIsNotUsedTogether } from '../util/input_validation'; -import { logWarn } from '../util/log'; // settings() defaults: export const DEFAULT_HOST = 'firestore.googleapis.com'; diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 5f3e2dc6c61..174048020e6 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -1214,6 +1214,7 @@ apiDescribe('Database', (persistence: boolean) => { 'cannot clear persistence if the client has been initialized', async () => { await withTestDoc(persistence, async (docRef, firestore) => { + await setDoc(docRef, {}); const expectedError = 'Persistence can only be cleared before a Firestore instance is ' + 'initialized or after it is terminated.'; diff --git a/packages/firestore/test/integration/api/validation.test.ts b/packages/firestore/test/integration/api/validation.test.ts index c118e485310..229a9a582b7 100644 --- a/packages/firestore/test/integration/api/validation.test.ts +++ b/packages/firestore/test/integration/api/validation.test.ts @@ -240,9 +240,7 @@ apiDescribe('Validation:', (persistence: boolean) => { doc(db, 'foo/bar'); } expect(() => enableIndexedDbPersistence(db)).to.throw( - 'Firestore has already been started and persistence can no ' + - 'longer be enabled. You can only enable persistence before ' + - 'calling any other methods on a Firestore object.' + 'SDK cache is already specified.' ); } ); diff --git a/packages/firestore/test/integration/api_internal/idle_timeout.test.ts b/packages/firestore/test/integration/api_internal/idle_timeout.test.ts index f6d5f77f7e5..d0a31ee9461 100644 --- a/packages/firestore/test/integration/api_internal/idle_timeout.test.ts +++ b/packages/firestore/test/integration/api_internal/idle_timeout.test.ts @@ -22,7 +22,7 @@ import { apiDescribe, withTestDb } from '../util/helpers'; import { asyncQueue } from '../util/internal_helpers'; apiDescribe('Idle Timeout', (persistence: boolean) => { - it.only('can write document after idle timeout', () => { + it('can write document after idle timeout', () => { return withTestDb(persistence, db => { const docRef = doc(collection(db, 'test-collection')); return setDoc(docRef, { foo: 'bar' }) diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index f8ecc1bfb3a..a0ffff32a52 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -17,18 +17,13 @@ import { isIndexedDBAvailable } from '@firebase/util'; -import { - indexedDbLocalCache, - memoryLocalCache -} from '../../../src/api/cache_config'; -import { logWarn } from '../../../src/util/log'; - import { collection, doc, DocumentReference, Firestore, terminate, + indexedDbLocalCache, clearIndexedDbPersistence, enableIndexedDbPersistence, CollectionReference, @@ -191,10 +186,11 @@ export async function withTestDbsSettings( for (let i = 0; i < numDbs; i++) { // logWarn(`set persistence from helper: ${persistence}`); + const newSettings = { ...settings }; if (persistence) { - settings.cache = indexedDbLocalCache(); + newSettings.cache = indexedDbLocalCache(); } - const db = newTestFirestore(newTestApp(projectId), settings); + const db = newTestFirestore(newTestApp(projectId), newSettings); dbs.push(db); } From d651d88fdc5478c648c98ef573a355f0e28427ac Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Tue, 21 Feb 2023 11:47:00 -0500 Subject: [PATCH 11/39] Fixing dts file --- common/api-review/firestore.api.md | 54 ++++++++++++ packages/firestore/src/api.ts | 2 +- packages/firestore/src/api/cache_config.ts | 82 ++++++++++++++++--- packages/firestore/src/api/database.ts | 12 +-- .../firestore/src/api/index_configuration.ts | 4 +- .../firestore/src/core/firestore_client.ts | 40 ++++----- .../test/integration/util/helpers.ts | 1 - repo-scripts/prune-dts/extract-public-api.ts | 1 + 8 files changed, 156 insertions(+), 40 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 5dd5742b318..cd248cfff95 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -216,8 +216,13 @@ 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 (undocumented) +export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; + // @public export interface FirestoreSettings { + // (undocumented) + cache?: FirestoreLocalCache; cacheSizeBytes?: number; experimentalAutoDetectLongPolling?: boolean; experimentalForceLongPolling?: boolean; @@ -285,6 +290,46 @@ export interface IndexConfiguration { readonly indexes?: Index[]; } +// @public (undocumented) +export interface IndexedDbLocalCache { + // (undocumented) + kind: 'indexeddb'; +} + +// @public (undocumented) +export function indexedDbLocalCache(settings?: IndexedDbSettings): IndexedDbLocalCache; + +// @public (undocumented) +export interface IndexedDbMultipleTabManager { + // (undocumented) + kind: 'IndexedDbMultipleTab'; +} + +// @public (undocumented) +export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager; + +// @public (undocumented) +export interface IndexedDbSettings { + // (undocumented) + cacheSizeBytes?: number; + // (undocumented) + tabManager?: IndexedDbTabManager; +} + +// @public (undocumented) +export interface IndexedDbSingleTabManager { + // (undocumented) + kind: 'indexedDbSingleTab'; +} + +// @public (undocumented) +export function indexedDbSingleTabManager(settings: { + forceOwnership?: boolean; +} | undefined): IndexedDbSingleTabManager; + +// @public (undocumented) +export type IndexedDbTabManager = IndexedDbSingleTabManager | IndexedDbMultipleTabManager; + // @beta export interface IndexField { // (undocumented) @@ -324,6 +369,15 @@ export interface LoadBundleTaskProgress { export { LogLevel } +// @public (undocumented) +export interface MemoryLocalCache { + // (undocumented) + kind: 'memory'; +} + +// @public (undocumented) +export function memoryLocalCache(): MemoryLocalCache; + // @public export function namedQuery(firestore: Firestore, name: string): Promise; diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index e014f5b4d08..ae33fec98af 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -30,7 +30,6 @@ export { export { FirestoreLocalCache, - MemoryLocalCache, IndexedDbLocalCache, IndexedDbMultipleTabManager, indexedDbLocalCache, @@ -38,6 +37,7 @@ export { IndexedDbSettings, indexedDbSingleTabManager, IndexedDbSingleTabManager, + MemoryLocalCache, memoryLocalCache, IndexedDbTabManager } from './api/cache_config'; diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 817f134acc1..1294b281d7f 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -23,15 +23,28 @@ import { OnlineComponentProvider } from '../core/component_provider'; -export interface MemoryLocalCache { +/* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */ +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() { @@ -44,15 +57,27 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { } } -export interface IndexedDbLocalCache { +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) { @@ -80,11 +105,11 @@ export function memoryLocalCache(): MemoryLocalCache { return new MemoryLocalCacheImpl(); } -export interface IndexedDbSettings { +export type IndexedDbSettings = { cacheSizeBytes?: number; // default to singleTabManager({forceOwnership: false}) tabManager?: IndexedDbTabManager; -} +}; // Factory function export function indexedDbLocalCache( @@ -93,19 +118,34 @@ export function indexedDbLocalCache( return new IndexedDbLocalCacheImpl(settings); } -export interface IndexedDbSingleTabManager { +export type IndexedDbSingleTabManager = { kind: 'indexedDbSingleTab'; + /** + * @internal + */ _initialize: ( settings: Omit | undefined ) => void; + /** + * @internal + */ _onlineComponentProvider?: OnlineComponentProvider; + /** + * @internal + */ _offlineComponentProvider?: OfflineComponentProvider; -} +}; class SingleTabManagerImpl implements IndexedDbSingleTabManager { kind: 'indexedDbSingleTab' = 'indexedDbSingleTab'; + /** + * @internal + */ _onlineComponentProvider?: OnlineComponentProvider; + /** + * @internal + */ _offlineComponentProvider?: OfflineComponentProvider; constructor(private forceOwnership?: boolean) {} @@ -114,6 +154,9 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { return { kind: this.kind }; } + /** + * @internal + */ _initialize( settings: Omit | undefined ): void { @@ -126,23 +169,42 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { } } -export interface IndexedDbMultipleTabManager { +export type IndexedDbMultipleTabManager = { kind: 'IndexedDbMultipleTab'; + /** + * @internal + */ _initialize: (settings: Omit) => void; + /** + * @internal + */ _onlineComponentProvider?: OnlineComponentProvider; + /** + * @internal + */ + _offlineComponentProvider?: OfflineComponentProvider; -} +}; class MultiTabManagerImpl implements IndexedDbMultipleTabManager { kind: 'IndexedDbMultipleTab' = 'IndexedDbMultipleTab'; + /** + * @internal + */ _onlineComponentProvider?: OnlineComponentProvider; + /** + * @internal + */ _offlineComponentProvider?: OfflineComponentProvider; toJSON(): {} { return { kind: this.kind }; } + /** + * @internal + */ _initialize( settings: Omit | undefined ): void { diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 20a03abead2..0d843e1d6ef 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -287,10 +287,10 @@ export function configureFirestore(firestore: Firestore): void { settings.cache?._offlineComponentProvider && settings.cache?._onlineComponentProvider ) { - firestore._firestoreClient.uninitializedComponentsProvider = { - offlineKind: settings.cache.kind, - offline: settings.cache._offlineComponentProvider, - online: settings.cache._onlineComponentProvider + firestore._firestoreClient._uninitializedComponentsProvider = { + _offlineKind: settings.cache.kind, + _offline: settings.cache._offlineComponentProvider, + _online: settings.cache._onlineComponentProvider }; } } @@ -327,7 +327,7 @@ export function enableIndexedDbPersistence( verifyNotInitialized(firestore); const client = ensureFirestoreConfigured(firestore); - if (client.uninitializedComponentsProvider) { + if (client._uninitializedComponentsProvider) { throw new FirestoreError( Code.FAILED_PRECONDITION, 'SDK cache is already specified.' @@ -379,7 +379,7 @@ export function enableMultiTabIndexedDbPersistence( verifyNotInitialized(firestore); const client = ensureFirestoreConfigured(firestore); - if (client.uninitializedComponentsProvider) { + if (client._uninitializedComponentsProvider) { throw new FirestoreError( Code.FAILED_PRECONDITION, 'SDK cache is already specified.' diff --git a/packages/firestore/src/api/index_configuration.ts b/packages/firestore/src/api/index_configuration.ts index c11a9665f46..3fd20944cc0 100644 --- a/packages/firestore/src/api/index_configuration.ts +++ b/packages/firestore/src/api/index_configuration.ts @@ -151,8 +151,8 @@ export function setIndexConfiguration( firestore = cast(firestore, Firestore); const client = ensureFirestoreConfigured(firestore); if ( - !client.uninitializedComponentsProvider || - client.uninitializedComponentsProvider?.offlineKind === 'memory' + !client._uninitializedComponentsProvider || + client._uninitializedComponentsProvider?._offlineKind === 'memory' ) { // PORTING NOTE: We don't return an error if the user has not enabled // persistence since `enableIndexeddbPersistence()` can fail on the Web. diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 051337bdb80..30440ac0798 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -114,14 +114,14 @@ export class FirestoreClient { appCheckToken: string, user: User ) => Promise = () => Promise.resolve(); - uninitializedComponentsProvider?: { - offline: OfflineComponentProvider; - offlineKind: 'memory' | 'indexeddb'; - online: OnlineComponentProvider; + _uninitializedComponentsProvider?: { + _offline: OfflineComponentProvider; + _offlineKind: 'memory' | 'indexeddb'; + _online: OnlineComponentProvider; }; - offlineComponents?: OfflineComponentProvider; - onlineComponents?: OnlineComponentProvider; + _offlineComponents?: OfflineComponentProvider; + _onlineComponents?: OnlineComponentProvider; constructor( private authCredentials: CredentialsProvider, @@ -188,11 +188,11 @@ export class FirestoreClient { const deferred = new Deferred(); this.asyncQueue.enqueueAndForgetEvenWhileRestricted(async () => { try { - if (this.onlineComponents) { - await this.onlineComponents.terminate(); + if (this._onlineComponents) { + await this._onlineComponents.terminate(); } - if (this.offlineComponents) { - await this.offlineComponents.terminate(); + if (this._offlineComponents) { + await this._offlineComponents.terminate(); } // The credentials provider must be terminated after shutting down the @@ -240,7 +240,7 @@ export async function setOfflineComponentProvider( client.terminate() ); - client.offlineComponents = offlineComponentProvider; + client._offlineComponents = offlineComponentProvider; } export async function setOnlineComponentProvider( @@ -265,18 +265,18 @@ export async function setOnlineComponentProvider( client.setAppCheckTokenChangeListener((_, user) => remoteStoreHandleCredentialChange(onlineComponentProvider.remoteStore, user) ); - client.onlineComponents = onlineComponentProvider; + client._onlineComponents = onlineComponentProvider; } async function ensureOfflineComponents( client: FirestoreClient ): Promise { - if (!client.offlineComponents) { - if (client.uninitializedComponentsProvider) { + if (!client._offlineComponents) { + if (client._uninitializedComponentsProvider) { logDebug(LOG_TAG, 'Using user provided OfflineComponentProvider'); await setOfflineComponentProvider( client, - client.uninitializedComponentsProvider.offline + client._uninitializedComponentsProvider._offline ); } else { logDebug(LOG_TAG, 'Using default OfflineComponentProvider'); @@ -287,18 +287,18 @@ async function ensureOfflineComponents( } } - return client.offlineComponents!; + return client._offlineComponents!; } async function ensureOnlineComponents( client: FirestoreClient ): Promise { - if (!client.onlineComponents) { - if (client.uninitializedComponentsProvider) { + if (!client._onlineComponents) { + if (client._uninitializedComponentsProvider) { logDebug(LOG_TAG, 'Using user provided OnlineComponentProvider'); await setOnlineComponentProvider( client, - client.uninitializedComponentsProvider.online + client._uninitializedComponentsProvider._online ); } else { logDebug(LOG_TAG, 'Using default OnlineComponentProvider'); @@ -306,7 +306,7 @@ async function ensureOnlineComponents( } } - return client.onlineComponents!; + return client._onlineComponents!; } function getPersistence(client: FirestoreClient): Promise { diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index a0ffff32a52..945f27600df 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -185,7 +185,6 @@ export async function withTestDbsSettings( const dbs: Firestore[] = []; for (let i = 0; i < numDbs; i++) { - // logWarn(`set persistence from helper: ${persistence}`); const newSettings = { ...settings }; if (persistence) { newSettings.cache = indexedDbLocalCache(); diff --git a/repo-scripts/prune-dts/extract-public-api.ts b/repo-scripts/prune-dts/extract-public-api.ts index c6ca172a70f..adf63416758 100644 --- a/repo-scripts/prune-dts/extract-public-api.ts +++ b/repo-scripts/prune-dts/extract-public-api.ts @@ -175,6 +175,7 @@ export async function generateApi( /* dtsRollupEnabled= */ false, /* apiReportEnabled= */ true ); + console.log(`Invoking extractor with config ${extractorConfig}`); Extractor.invoke(extractorConfig, { localBuild: true }); console.log(`API report for ${packageName} written to ${reportFolder}`); } From 880bd55bf0e8547604ccb21853254df99dcbc4f8 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Tue, 28 Feb 2023 07:27:01 -0500 Subject: [PATCH 12/39] Fixing integration tests --- .../test/integration/api/database.test.ts | 19 ++++++++++++++----- .../test/integration/util/helpers.ts | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index cdbc36ca6d5..98d632d1a01 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -74,7 +74,8 @@ import { withTestDbs, withTestDoc, withTestDocAndInitialData, - withNamedTestDbsOrSkipUnlessUsingEmulator + withNamedTestDbsOrSkipUnlessUsingEmulator, + withEnsuredEagerGcTestDb } from '../util/helpers'; import { DEFAULT_SETTINGS, DEFAULT_PROJECT_ID } from '../util/settings'; @@ -1753,15 +1754,23 @@ apiDescribe('Database', (persistence: boolean) => { ); }); - it('Can get document from cache with GC enabled.', () => { + it('Cannot get document from cache with eager GC enabled.', () => { + const initialData = { key: 'value' }; + return withEnsuredEagerGcTestDb(async db => { + const docRef = doc(collection(db, 'test-collection')); + await setDoc(docRef, initialData); + await expect(getDocFromCache(docRef)).to.be.rejectedWith('Failed to get'); + }); + }); + + it('Can get document from cache with Lru GC enabled.', () => { const initialData = { key: 'value' }; return withEnsuredLruGcTestDb(persistence, async db => { const docRef = doc(collection(db, 'test-collection')); await setDoc(docRef, initialData); - return getDoc(docRef).then(doc => { + return getDocFromCache(docRef).then(doc => { expect(doc.exists()).to.be.true; - expect(doc.metadata.fromCache).to.be.false; - expect(doc.metadata.hasPendingWrites).to.be.false; + expect(doc.metadata.fromCache).to.be.true; expect(doc.data()).to.deep.equal(initialData); }); }); diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 2852a3a1ebb..13497d5616a 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -142,6 +142,20 @@ export function withTestDb( }); } +export function withEnsuredEagerGcTestDb( + fn: (db: Firestore) => Promise +): Promise { + return withTestDbsSettings( + false, + DEFAULT_PROJECT_ID, + { ...DEFAULT_SETTINGS, cacheSizeBytes: 1 * 1024 * 1024 }, + 1, + async ([db]) => { + return fn(db); + } + ); +} + export function withEnsuredLruGcTestDb( persistence: boolean, fn: (db: Firestore) => Promise From c36e96f03517357a9cc09fed4cc7a3da7084dd46 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Tue, 28 Feb 2023 12:20:44 -0500 Subject: [PATCH 13/39] rename file --- ...e_collection_spect.test.ts => garbage_collection_spec.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/firestore/test/unit/specs/{garbage_collection_spect.test.ts => garbage_collection_spec.test.ts} (100%) diff --git a/packages/firestore/test/unit/specs/garbage_collection_spect.test.ts b/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts similarity index 100% rename from packages/firestore/test/unit/specs/garbage_collection_spect.test.ts rename to packages/firestore/test/unit/specs/garbage_collection_spec.test.ts From 85ea1282a47151f18c85079470cc89b66c4e907a Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Tue, 28 Feb 2023 13:53:50 -0500 Subject: [PATCH 14/39] Increase cache size threshold --- .../firestore/test/unit/specs/garbage_collection_spec.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts b/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts index 41af3b5f656..f9115727ee8 100644 --- a/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts +++ b/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts @@ -66,7 +66,7 @@ describeSpec('Garbage Collection:', [], () => { .watchAcksFull(query1, 1000, docA) .expectEvents(query1, { added: [docA] }) .userUnlistens(query1) - .triggerLruGC(1000) + .triggerLruGC(1000_000_000) .userListens(query1) .expectListen(query1, { resumeToken: 'resume-token-1000' }) .expectEvents(query1, { added: [docA], fromCache: true }); From 28215dac1fd56538be6de219ea917867ecc2f4fe Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 1 Mar 2023 11:11:57 -0500 Subject: [PATCH 15/39] Add public comments --- packages/firestore/src/api/cache_config.ts | 80 ++++++++++++++++++++-- packages/firestore/src/api/database.ts | 34 ++++++++- packages/firestore/src/api/settings.ts | 15 +++- 3 files changed, 121 insertions(+), 8 deletions(-) diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 1294b281d7f..f88e7c7b333 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -24,6 +24,14 @@ import { } from '../core/component_provider'; /* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */ +/** + * Provides a in-memory cache to the SDK. This is the default cache unless explicitly + * specified otherwise. + * + * 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'; /** @@ -57,6 +65,13 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { } } +/** + * Provides a cache backed by IndexedDb to the SDK. + * + * To use, create an instance using the factory function `indexedDbLocalCache()`, then + * set the instance to `FirestoreSettings.cache` and call `initializeFirestore` using + * the settings object. + */ export type IndexedDbLocalCache = { kind: 'indexeddb'; /** @@ -98,26 +113,55 @@ class IndexedDbLocalCacheImpl implements IndexedDbLocalCache { } } +/** + * 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. + */ export function memoryLocalCache(): MemoryLocalCache { return new MemoryLocalCacheImpl(); } +/** + * 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 + * 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; - // 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. + */ export function indexedDbLocalCache( settings?: IndexedDbSettings ): IndexedDbLocalCache { return new IndexedDbLocalCacheImpl(settings); } +/** + * A tab manager supportting only one tab, no synchronization will be + * performed across tabs. + */ export type IndexedDbSingleTabManager = { kind: 'indexedDbSingleTab'; /** @@ -169,6 +213,10 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { } } +/** + * A tab manager supportting multiple tabs. SDK will synchronize queries and + * mutations done across all tabs using the SDK. + */ export type IndexedDbMultipleTabManager = { kind: 'IndexedDbMultipleTab'; /** @@ -216,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 + * 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(); } diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 0d843e1d6ef..174016b2a10 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -171,6 +171,14 @@ export function initializeFirestore( } } + if (settings.cacheSizeBytes !== undefined && settings.cache !== undefined) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `cache and cacheSizeBytes cannot be specified at the same time as cacheSizeBytes will` + + `be deprecated. Instead, specify the cache size in the cache object` + ); + } + if ( settings.cacheSizeBytes !== undefined && settings.cacheSizeBytes !== CACHE_SIZE_UNLIMITED && @@ -296,6 +304,11 @@ export function configureFirestore(firestore: Firestore): void { } /** + * NOTE: this function will be deprecated in a future major release. Instead, set + * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to + * turn on indexeddb cache. Calling this function when `FirestoreSettings.cache` + * is already specified will throw an exception. + * * Attempts to enable persistent storage, if possible. * * Must be called before any other functions (other than @@ -318,7 +331,6 @@ export function configureFirestore(firestore: Firestore): void { * persistence. * @returns A `Promise` that represents successfully enabling persistent storage. */ -// TODO(wuandy): mark obselete export function enableIndexedDbPersistence( firestore: Firestore, persistenceSettings?: PersistenceSettings @@ -336,6 +348,13 @@ export function enableIndexedDbPersistence( const settings = firestore._freezeSettings(); + if (settings.cache !== undefined) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'FirestoreSettings.cache is already specified.' + ); + } + const onlineComponentProvider = new OnlineComponentProvider(); const offlineComponentProvider = new IndexedDbOfflineComponentProvider( onlineComponentProvider, @@ -350,6 +369,11 @@ export function enableIndexedDbPersistence( } /** + * NOTE: this function will be deprecated in a future major release. Instead, set + * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to + * turn on indexeddb cache. Calling this function when `FirestoreSettings.cache` + * is already specified will throw an exception. + * * Attempts to enable multi-tab persistent storage, if possible. If enabled * across all tabs, all operations share access to local persistence, including * shared execution of queries and latency-compensated local document updates @@ -371,7 +395,6 @@ export function enableIndexedDbPersistence( * @returns A `Promise` that represents successfully enabling persistent * storage. */ -// TODO(wuandy): mark obselete export function enableMultiTabIndexedDbPersistence( firestore: Firestore ): Promise { @@ -388,6 +411,13 @@ export function enableMultiTabIndexedDbPersistence( const settings = firestore._freezeSettings(); + if (settings.cache !== undefined) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'FirestoreSettings.cache is already specified.' + ); + } + const onlineComponentProvider = new OnlineComponentProvider(); const offlineComponentProvider = new MultiTabOfflineComponentProvider( onlineComponentProvider, diff --git a/packages/firestore/src/api/settings.ts b/packages/firestore/src/api/settings.ts index e56b2aa1bd9..ec0e26439aa 100644 --- a/packages/firestore/src/api/settings.ts +++ b/packages/firestore/src/api/settings.ts @@ -32,7 +32,6 @@ export interface PersistenceSettings { * Workers. Setting this to `true` will enable persistence, but cause other * tabs using persistence to fail. */ - // TODO(wuandy): Deprecate this forceOwnership?: boolean; } @@ -42,6 +41,9 @@ export interface PersistenceSettings { */ export interface FirestoreSettings extends LiteSettings { /** + * NOTE: This field will be deprecated in a future major release. Use `cache` field + * instead to specify cache size, and other cache configurations. + * * 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 @@ -51,9 +53,18 @@ export interface FirestoreSettings extends LiteSettings { * 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. */ - // TODO(wuandy): Deprecate this cacheSizeBytes?: number; + /** + * Specifies the cache used by the SDK. Availabe options are `MemoryLocalCache` + * and `IndexedDbLocalCache`, each with different configuration options. + * + * When unspecified, `MemoryLocalCache` will be used by default. + * + * NOTE: setting this field and `cacheSizeBytes` at the same time will throw + * exception during SDK initialization. Instead, using the configuration in + * the `FirestoreLocalCache` object to specify the cache size. + */ cache?: FirestoreLocalCache; /** From 36715cd54693ac868ef0132d40b35396b999baf1 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 1 Mar 2023 11:38:01 -0500 Subject: [PATCH 16/39] API report --- common/api-review/firestore.api.md | 32 ++++++++++---------- packages/firestore/src/api.ts | 1 + packages/firestore/src/lite-api/settings.ts | 5 --- repo-scripts/prune-dts/extract-public-api.ts | 1 - 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index cd248cfff95..72f2c84a2dd 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -216,12 +216,11 @@ 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 (undocumented) +// @public export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; // @public export interface FirestoreSettings { - // (undocumented) cache?: FirestoreLocalCache; cacheSizeBytes?: number; experimentalAutoDetectLongPolling?: boolean; @@ -290,44 +289,45 @@ export interface IndexConfiguration { readonly indexes?: Index[]; } -// @public (undocumented) +// @public export interface IndexedDbLocalCache { // (undocumented) kind: 'indexeddb'; } -// @public (undocumented) +// @public export function indexedDbLocalCache(settings?: IndexedDbSettings): IndexedDbLocalCache; -// @public (undocumented) +// @public export interface IndexedDbMultipleTabManager { // (undocumented) kind: 'IndexedDbMultipleTab'; } -// @public (undocumented) +// @public export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager; -// @public (undocumented) +// @public export interface IndexedDbSettings { - // (undocumented) cacheSizeBytes?: number; - // (undocumented) tabManager?: IndexedDbTabManager; } -// @public (undocumented) +// @public export interface IndexedDbSingleTabManager { // (undocumented) kind: 'indexedDbSingleTab'; } -// @public (undocumented) -export function indexedDbSingleTabManager(settings: { +// @public +export function indexedDbSingleTabManager(settings: IndexedDbSingleTabManagerSettings | undefined): IndexedDbSingleTabManager; + +// @public +export interface IndexedDbSingleTabManagerSettings { forceOwnership?: boolean; -} | undefined): IndexedDbSingleTabManager; +} -// @public (undocumented) +// @public export type IndexedDbTabManager = IndexedDbSingleTabManager | IndexedDbMultipleTabManager; // @beta @@ -369,13 +369,13 @@ export interface LoadBundleTaskProgress { export { LogLevel } -// @public (undocumented) +// @public export interface MemoryLocalCache { // (undocumented) kind: 'memory'; } -// @public (undocumented) +// @public export function memoryLocalCache(): MemoryLocalCache; // @public diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index ae33fec98af..895630c2fbe 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -37,6 +37,7 @@ export { IndexedDbSettings, indexedDbSingleTabManager, IndexedDbSingleTabManager, + IndexedDbSingleTabManagerSettings, MemoryLocalCache, memoryLocalCache, IndexedDbTabManager diff --git a/packages/firestore/src/lite-api/settings.ts b/packages/firestore/src/lite-api/settings.ts index 9de2d0eb705..a5f0a9325b7 100644 --- a/packages/firestore/src/lite-api/settings.ts +++ b/packages/firestore/src/lite-api/settings.ts @@ -109,11 +109,6 @@ export class FirestoreSettingsImpl { this.credentials = settings.credentials; this.ignoreUndefinedProperties = !!settings.ignoreUndefinedProperties; - // logWarn( - // `Setting offline cache to ${JSON.stringify( - // settings.cache - // )} from PrivateSettings` - // ); this.cache = settings.cache; if (settings.cacheSizeBytes === undefined) { diff --git a/repo-scripts/prune-dts/extract-public-api.ts b/repo-scripts/prune-dts/extract-public-api.ts index adf63416758..c6ca172a70f 100644 --- a/repo-scripts/prune-dts/extract-public-api.ts +++ b/repo-scripts/prune-dts/extract-public-api.ts @@ -175,7 +175,6 @@ export async function generateApi( /* dtsRollupEnabled= */ false, /* apiReportEnabled= */ true ); - console.log(`Invoking extractor with config ${extractorConfig}`); Extractor.invoke(extractorConfig, { localBuild: true }); console.log(`API report for ${packageName} written to ${reportFolder}`); } From 4bbcbe1908d276791cac083a1c029899207b35d2 Mon Sep 17 00:00:00 2001 From: wu-hui <53845758+wu-hui@users.noreply.github.com> Date: Wed, 1 Mar 2023 11:38:57 -0500 Subject: [PATCH 17/39] Create brown-beers-tease.md --- .changeset/brown-beers-tease.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/brown-beers-tease.md diff --git a/.changeset/brown-beers-tease.md b/.changeset/brown-beers-tease.md new file mode 100644 index 00000000000..869bd35639e --- /dev/null +++ b/.changeset/brown-beers-tease.md @@ -0,0 +1,6 @@ +--- +"@firebase/firestore": minor +"firebase-repo-scripts-prune-dts": minor +--- + +Introduces a new way to config Firestore SDK Cache. From 3d22995cfff15e7052befa20d679bb14cd6d9b03 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 1 Mar 2023 13:44:09 -0500 Subject: [PATCH 18/39] warning messages and more tests --- packages/firestore/src/api/database.ts | 22 +++++------- .../test/integration/api/provider.test.ts | 34 ++++++++++++++++++- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 174016b2a10..01178fafd9b 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -346,15 +346,12 @@ export function enableIndexedDbPersistence( ); } + logWarn( + 'enableIndexedDbPersistence() will be deprecated in the future, ' + + 'you can use `FirestoreSettings.cache` instead.' + ); const settings = firestore._freezeSettings(); - if (settings.cache !== undefined) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'FirestoreSettings.cache is already specified.' - ); - } - const onlineComponentProvider = new OnlineComponentProvider(); const offlineComponentProvider = new IndexedDbOfflineComponentProvider( onlineComponentProvider, @@ -409,15 +406,12 @@ export function enableMultiTabIndexedDbPersistence( ); } + logWarn( + 'enableMultiTabIndexedDbPersistence() will be deprecated in the future, ' + + 'you can use `FirestoreSettings.cache` instead.' + ); const settings = firestore._freezeSettings(); - if (settings.cache !== undefined) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'FirestoreSettings.cache is already specified.' - ); - } - const onlineComponentProvider = new OnlineComponentProvider(); const offlineComponentProvider = new MultiTabOfflineComponentProvider( onlineComponentProvider, diff --git a/packages/firestore/test/integration/api/provider.test.ts b/packages/firestore/test/integration/api/provider.test.ts index ffcd0e7f350..91a89cb6036 100644 --- a/packages/firestore/test/integration/api/provider.test.ts +++ b/packages/firestore/test/integration/api/provider.test.ts @@ -25,7 +25,11 @@ import { initializeFirestore, Firestore, terminate, - getDoc + getDoc, + enableIndexedDbPersistence, + setDoc, + memoryLocalCache, + getDocFromCache } from '../util/firebase_export'; import { DEFAULT_SETTINGS } from '../util/settings'; @@ -120,6 +124,34 @@ describe('Firestore Provider', () => { expect(fs1).to.be.equal(fs2); }); + it('can still use enableIndexedDbPersistence()', async () => { + const app = initializeApp( + { apiKey: 'fake-api-key', projectId: 'test-project' }, + 'test-use-enablePersistence' + ); + const db = initializeFirestore(app, DEFAULT_SETTINGS); + await enableIndexedDbPersistence(db); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + setDoc(doc(db, 'coll/doc'), { field: 'foo' }); + expect((await getDocFromCache(doc(db, 'coll/doc'))).data()).to.deep.equal({ + field: 'foo' + }); + }); + + it('cannot mix enableIndexedDbPersistence() and settings.cache', async () => { + const app = initializeApp( + { apiKey: 'fake-api-key', projectId: 'test-project' }, + 'test-cannot-mix' + ); + const db = initializeFirestore(app, { + ...DEFAULT_SETTINGS, + cache: memoryLocalCache() + }); + expect(() => enableIndexedDbPersistence(db)).to.throw( + 'SDK cache is already specified.' + ); + }); + it('cannot use once terminated', () => { const app = initializeApp( { apiKey: 'fake-api-key', projectId: 'test-project' }, From 5b16efdfd8c93f74262d694393d3192b4240faf7 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 2 Mar 2023 06:16:25 -0500 Subject: [PATCH 19/39] Addressing comments --- packages/firestore/src/api/cache_config.ts | 4 ++-- packages/firestore/src/api/database.ts | 6 +++--- packages/firestore/src/api/index_configuration.ts | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index f88e7c7b333..77b33e20d46 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -28,7 +28,7 @@ import { * Provides a in-memory cache to the SDK. This is the default cache unless explicitly * specified otherwise. * - * To use, create an instance using the factory function `memoryLocalCache()`, then + * To use, create an instance using the factory function {@link memoryLocalCache()}, then * set the instance to `FirestoreSettings.cache` and call `initializeFirestore` using * the settings object. */ @@ -68,7 +68,7 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { /** * Provides a cache backed by IndexedDb to the SDK. * - * To use, create an instance using the factory function `indexedDbLocalCache()`, then + * To use, create an instance using the factory function {@link indexedDbLocalCache()}, then * set the instance to `FirestoreSettings.cache` and call `initializeFirestore` using * the settings object. */ diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 01178fafd9b..3cee0c1c2f4 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -304,9 +304,9 @@ export function configureFirestore(firestore: Firestore): void { } /** - * NOTE: this function will be deprecated in a future major release. Instead, set + * @deprecated This function will be removed in a future major release. Instead, set * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to - * turn on indexeddb cache. Calling this function when `FirestoreSettings.cache` + * turn on IndexedDb cache. Calling this function when `FirestoreSettings.cache` * is already specified will throw an exception. * * Attempts to enable persistent storage, if possible. @@ -366,7 +366,7 @@ export function enableIndexedDbPersistence( } /** - * NOTE: this function will be deprecated in a future major release. Instead, set + * @deprecated This function will be removed in a future major release. Instead, set * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to * turn on indexeddb cache. Calling this function when `FirestoreSettings.cache` * is already specified will throw an exception. diff --git a/packages/firestore/src/api/index_configuration.ts b/packages/firestore/src/api/index_configuration.ts index 3fd20944cc0..94754636176 100644 --- a/packages/firestore/src/api/index_configuration.ts +++ b/packages/firestore/src/api/index_configuration.ts @@ -96,10 +96,8 @@ export interface IndexConfiguration { * Query execution will automatically start using the index once the index * entries have been written. * - * Indexes are only supported with IndexedDb persistence. Invoke either - * `enableIndexedDbPersistence()` or `enableMultiTabIndexedDbPersistence()` - * before setting an index configuration. If IndexedDb is not enabled, any - * index configuration is ignored. + * Indexes are only supported with IndexedDb persistence. If IndexedDb is not + * enabled, any index configuration is ignored. * * @param firestore - The {@link Firestore} instance to configure indexes for. * @param configuration -The index definition. From 89f78c7ea0a962b202f856becb07a005d61c5af8 Mon Sep 17 00:00:00 2001 From: wu-hui Date: Thu, 2 Mar 2023 11:49:56 +0000 Subject: [PATCH 20/39] Update API reports --- common/api-review/firestore.api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 72f2c84a2dd..a18263b5b62 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -160,10 +160,10 @@ export class DocumentSnapshot { export { EmulatorMockTokenOptions } -// @public +// @public @deprecated (undocumented) export function enableIndexedDbPersistence(firestore: Firestore, persistenceSettings?: PersistenceSettings): Promise; -// @public +// @public @deprecated (undocumented) export function enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise; // @public From 93738f3fd70068e922619a69eefbd7bca21eedcd Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 2 Mar 2023 07:20:42 -0500 Subject: [PATCH 21/39] new lru gc api --- packages/firestore/src/api.ts | 1 - packages/firestore/src/api/cache_config.ts | 128 +++++++++++++++++- packages/firestore/src/api/database.ts | 35 ----- .../test/integration/util/helpers.ts | 20 ++- 4 files changed, 139 insertions(+), 45 deletions(-) diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 57ab6f0d2a0..895630c2fbe 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -51,7 +51,6 @@ export { initializeFirestore, getFirestore, enableIndexedDbPersistence, - enableMemoryLRUGarbageCollection, enableMultiTabIndexedDbPersistence, clearIndexedDbPersistence, waitForPendingWrites, diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 77b33e20d46..eb49c5d6129 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -17,6 +17,7 @@ import { IndexedDbOfflineComponentProvider, + LruGcMemoryOfflineComponentProvider, MemoryOfflineComponentProvider, MultiTabOfflineComponentProvider, OfflineComponentProvider, @@ -55,9 +56,14 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { */ _offlineComponentProvider: MemoryOfflineComponentProvider; - constructor() { + constructor(settings?: MemoryCacheSettings) { this._onlineComponentProvider = new OnlineComponentProvider(); - this._offlineComponentProvider = new MemoryOfflineComponentProvider(); + if (settings?.garbageCollector) { + this._offlineComponentProvider = + settings.garbageCollector._offlineComponentProvider; + } else { + this._offlineComponentProvider = new MemoryOfflineComponentProvider(); + } } toJSON(): {} { @@ -118,12 +124,126 @@ class IndexedDbLocalCacheImpl implements IndexedDbLocalCache { */ export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; +/** + * Union type from all support gabage collectors for memory local cache. + */ +export type MemoryGarbageCollector = + | MemoryEagerGarbageCollector + | MemoryLruGarbageCollector; + +/** + * A garbage collector deletes documents whenever they are not part of any + * active queries, and have no local mutations attached to them. + * + * This collector tries to ensure lowest memory footprints from the SDK, + * at the risk of querying backend repeated for a document it could have + * cached locally. + * + * Use factory function {@link memoryEagerGarbageCollector()} to create a + * instance of this collector. + */ +export type MemoryEagerGarbageCollector = { + kind: 'memoryEager'; + /** + * @internal + */ + _offlineComponentProvider: MemoryOfflineComponentProvider; +}; + +/** + * A garbage collector deletes Least-Recently-Used documents in multiple + * batches. + * + * This collector is configured with a target size, and will only perform + * collection when the cached documents exceed the target size. It avoids + * querying backend repeated for the same query or document, at the risk + * of having a larger memory footprint. + * + * Use factory function {@link memoryLruGarbageCollector()} to create a + * instance of this collector. + */ +export type MemoryLruGarbageCollector = { + kind: 'memoryLru'; + /** + * @internal + */ + _offlineComponentProvider: MemoryOfflineComponentProvider; +}; + +class MemoryEagerGabageCollectorImpl implements MemoryEagerGarbageCollector { + kind: 'memoryEager' = 'memoryEager'; + /** + * @internal + */ + _offlineComponentProvider: MemoryOfflineComponentProvider; + + constructor() { + this._offlineComponentProvider = new MemoryOfflineComponentProvider(); + } + + toJSON(): {} { + return { kind: this.kind }; + } +} + +class MemoryLruGabageCollectorImpl implements MemoryLruGarbageCollector { + kind: 'memoryLru' = 'memoryLru'; + /** + * @internal + */ + _offlineComponentProvider: MemoryOfflineComponentProvider; + + constructor(cacheSize?: number) { + this._offlineComponentProvider = new LruGcMemoryOfflineComponentProvider( + cacheSize + ); + } + + toJSON(): {} { + return { kind: this.kind }; + } +} + +/** + * Creates an instance of `MemoryEagerGarbageCollector`. This is also the + * default garbage collector unless it is explicitly specified otherwise. + */ +export function memoryEagerGarbageCollector(): MemoryEagerGarbageCollector { + return new MemoryEagerGabageCollectorImpl(); +} + +/** + * Creates an instance of `MemoryLruGarbageCollector`. + * + * A target size can be specified as part of the setting parameter. The + * collector will start deleting documents once the cache size exceeds + * the given size. The default cache size is 40MB (40 * 1024 * 1024 bytes). + */ +export function memoryLruGarbageCollector(settings?: { + cacheSizeBytes?: number; +}): MemoryLruGarbageCollector { + return new MemoryLruGabageCollectorImpl(settings?.cacheSizeBytes); +} + +/** + * An settings object to configure an `MemoryLocalCache` instance. + */ +export type MemoryCacheSettings = { + /** + * The garbage collector to use, for the memory cache layer. + * A `MemoryEagerGarbageCollector` is used when this is undefined. + */ + garbageCollector?: MemoryGarbageCollector; +}; + /** * Creates an instance of `MemoryLocalCache`. The instance can be set to * `FirestoreSettings.cache` to tell the SDK what cache layer to use. */ -export function memoryLocalCache(): MemoryLocalCache { - return new MemoryLocalCacheImpl(); +export function memoryLocalCache( + settings?: MemoryCacheSettings +): MemoryLocalCache { + return new MemoryLocalCacheImpl(settings); } /** diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 57e6f733f81..b168fd13f00 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -304,41 +304,6 @@ export function configureFirestore(firestore: Firestore): void { } } -/** - * Must be called before any other functions (other than - * {@link initializeFirestore}, {@link (getFirestore:1)} or - * {@link clearIndexedDbPersistence}. - * - * By default, any documents that are not part of an active query result or - * with no mutation attached to them are removed from memory immediately. - * - * This function changes the default behavior, to enable a least-recent-used - * garbage collector. Documents will be collected when their total size exceeds - * `Settings.cacheSizeBytes`, with least recently used documents get removed first. - * - * @param firestore - The {@link Firestore} instance to enable LRU garbage collection for. - * @returns A `Promise` that represents successfully enabling LRU garbage collection. - */ -export function enableMemoryLRUGarbageCollection( - firestore: Firestore -): Promise { - firestore = cast(firestore, Firestore); - verifyNotInitialized(firestore); - - const client = ensureFirestoreConfigured(firestore); - const settings = firestore._freezeSettings(); - - const onlineComponentProvider = new OnlineComponentProvider(); - const offlineComponentProvider = new LruGcMemoryOfflineComponentProvider( - settings.cacheSizeBytes - ); - return setPersistenceProviders( - client, - onlineComponentProvider, - offlineComponentProvider - ); -} - /** * @deprecated This function will be removed in a future major release. Instead, set * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index cf0da625b7c..34403e3062f 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -16,6 +16,7 @@ */ import { isIndexedDBAvailable } from '@firebase/util'; +import { memoryLruGarbageCollector } from '../../../src/api/cache_config'; import { collection, @@ -34,7 +35,7 @@ import { SnapshotListenOptions, newTestFirestore, newTestApp, - enableMemoryLRUGarbageCollection + memoryLocalCache } from './firebase_export'; import { ALT_PROJECT_ID, @@ -161,15 +162,24 @@ export function withEnsuredLruGcTestDb( persistence: boolean, fn: (db: Firestore) => Promise ): Promise { + let newSettings = { ...DEFAULT_SETTINGS }; + if (persistence) { + newSettings.cache = indexedDbLocalCache({ + cacheSizeBytes: 1 * 1024 * 1024 + }); + } else { + newSettings.cache = memoryLocalCache({ + garbageCollector: memoryLruGarbageCollector({ + cacheSizeBytes: 1 * 1024 * 1024 + }) + }); + } return withTestDbsSettings( persistence, DEFAULT_PROJECT_ID, - { ...DEFAULT_SETTINGS, cacheSizeBytes: 1 * 1024 * 1024 }, + newSettings, 1, async ([db]) => { - if (!persistence) { - await enableMemoryLRUGarbageCollection(db); - } return fn(db); } ); From 1d5c82fbe9e4c5f03d52166435c9c6f2d8dd00e5 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 2 Mar 2023 11:31:30 -0500 Subject: [PATCH 22/39] Fix provider test failure for node w/o indexeddb --- common/api-review/firestore.api.md | 14 +-- packages/firestore/src/api.ts | 2 +- packages/firestore/src/api/cache_config.ts | 14 +-- packages/firestore/src/api/database.ts | 48 +--------- .../firestore/src/core/firestore_client.ts | 91 +++++++++++++++---- .../test/integration/api/provider.test.ts | 5 +- .../integration/browser/indexeddb.test.ts | 12 ++- .../test/integration/util/helpers.ts | 6 +- 8 files changed, 111 insertions(+), 81 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index a18263b5b62..2d259d58601 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -289,6 +289,12 @@ export interface IndexConfiguration { readonly indexes?: Index[]; } +// @public +export interface IndexedDbCacheSettings { + cacheSizeBytes?: number; + tabManager?: IndexedDbTabManager; +} + // @public export interface IndexedDbLocalCache { // (undocumented) @@ -296,7 +302,7 @@ export interface IndexedDbLocalCache { } // @public -export function indexedDbLocalCache(settings?: IndexedDbSettings): IndexedDbLocalCache; +export function indexedDbLocalCache(settings?: IndexedDbCacheSettings): IndexedDbLocalCache; // @public export interface IndexedDbMultipleTabManager { @@ -307,12 +313,6 @@ export interface IndexedDbMultipleTabManager { // @public export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager; -// @public -export interface IndexedDbSettings { - cacheSizeBytes?: number; - tabManager?: IndexedDbTabManager; -} - // @public export interface IndexedDbSingleTabManager { // (undocumented) diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 895630c2fbe..8ce497997d5 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -34,7 +34,7 @@ export { IndexedDbMultipleTabManager, indexedDbLocalCache, indexedDbMultipleTabManager, - IndexedDbSettings, + IndexedDbCacheSettings, indexedDbSingleTabManager, IndexedDbSingleTabManager, IndexedDbSingleTabManagerSettings, diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 77b33e20d46..04be3d6eacf 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -95,7 +95,7 @@ class IndexedDbLocalCacheImpl implements IndexedDbLocalCache { */ _offlineComponentProvider: OfflineComponentProvider; - constructor(settings: IndexedDbSettings | undefined) { + constructor(settings: IndexedDbCacheSettings | undefined) { let tabManager: IndexedDbTabManager; if (settings?.tabManager) { settings.tabManager._initialize(settings); @@ -129,7 +129,7 @@ export function memoryLocalCache(): MemoryLocalCache { /** * An settings object to configure an `IndexedDbLocalCache` instance. */ -export type IndexedDbSettings = { +export type IndexedDbCacheSettings = { /** * 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 @@ -153,7 +153,7 @@ export type IndexedDbSettings = { * `FirestoreSettings.cache` to tell the SDK what cache layer to use. */ export function indexedDbLocalCache( - settings?: IndexedDbSettings + settings?: IndexedDbCacheSettings ): IndexedDbLocalCache { return new IndexedDbLocalCacheImpl(settings); } @@ -168,7 +168,7 @@ export type IndexedDbSingleTabManager = { * @internal */ _initialize: ( - settings: Omit | undefined + settings: Omit | undefined ) => void; /** * @internal @@ -202,7 +202,7 @@ class SingleTabManagerImpl implements IndexedDbSingleTabManager { * @internal */ _initialize( - settings: Omit | undefined + settings: Omit | undefined ): void { this._onlineComponentProvider = new OnlineComponentProvider(); this._offlineComponentProvider = new IndexedDbOfflineComponentProvider( @@ -222,7 +222,7 @@ export type IndexedDbMultipleTabManager = { /** * @internal */ - _initialize: (settings: Omit) => void; + _initialize: (settings: Omit) => void; /** * @internal */ @@ -254,7 +254,7 @@ class MultiTabManagerImpl implements IndexedDbMultipleTabManager { * @internal */ _initialize( - settings: Omit | undefined + settings: Omit | undefined ): void { this._onlineComponentProvider = new OnlineComponentProvider(); this._offlineComponentProvider = new MultiTabOfflineComponentProvider( diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 3cee0c1c2f4..ce72a458d9f 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -32,6 +32,7 @@ import { } from '../core/component_provider'; import { DatabaseId, DEFAULT_DATABASE_NAME } from '../core/database_info'; import { + canFallbackFromIndexedDbError, FirestoreClient, firestoreClientDisableNetwork, firestoreClientEnableNetwork, @@ -75,11 +76,6 @@ declare module '@firebase/component' { } } -/** DOMException error code constants. */ -const DOM_EXCEPTION_INVALID_STATE = 11; -const DOM_EXCEPTION_ABORTED = 20; -const DOM_EXCEPTION_QUOTA_EXCEEDED = 22; - /** * Constant used to indicate the LRU garbage collection should be disabled. * Set this value as the `cacheSizeBytes` on the settings passed to the @@ -448,8 +444,8 @@ function setPersistenceProviders( throw error; } logWarn( - 'Error enabling offline persistence. Falling back to ' + - 'persistence disabled: ' + + 'Error enabling indexeddb cache. Falling back to ' + + 'memory cache: ' + error ); persistenceResult.reject(error); @@ -458,44 +454,6 @@ function setPersistenceProviders( .then(() => persistenceResult.promise); } -/** - * Decides whether the provided error allows us to gracefully disable - * persistence (as opposed to crashing the client). - */ -function canFallbackFromIndexedDbError( - error: FirestoreError | DOMException -): boolean { - if (error.name === 'FirebaseError') { - return ( - error.code === Code.FAILED_PRECONDITION || - error.code === Code.UNIMPLEMENTED - ); - } else if ( - typeof DOMException !== 'undefined' && - error instanceof DOMException - ) { - // There are a few known circumstances where we can open IndexedDb but - // trying to read/write will fail (e.g. quota exceeded). For - // well-understood cases, we attempt to detect these and then gracefully - // fall back to memory persistence. - // NOTE: Rather than continue to add to this list, we could decide to - // always fall back, with the risk that we might accidentally hide errors - // representing actual SDK bugs. - return ( - // When the browser is out of quota we could get either quota exceeded - // or an aborted error depending on whether the error happened during - // schema migration. - error.code === DOM_EXCEPTION_QUOTA_EXCEEDED || - error.code === DOM_EXCEPTION_ABORTED || - // Firefox Private Browsing mode disables IndexedDb and returns - // INVALID_STATE for any usage. - error.code === DOM_EXCEPTION_INVALID_STATE - ); - } - - return true; -} - /** * Clears the persistent storage. This includes pending writes and cached * documents. diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 30440ac0798..615c29d67a2 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -59,7 +59,7 @@ import { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue'; import { BundleReader } from '../util/bundle_reader'; import { newBundleReader } from '../util/bundle_reader_impl'; import { Code, FirestoreError } from '../util/error'; -import { logDebug } from '../util/log'; +import { logDebug, logWarn } from '../util/log'; import { AutoId } from '../util/misc'; import { Deferred } from '../util/promise'; @@ -100,10 +100,15 @@ import { ViewSnapshot } from './view_snapshot'; const LOG_TAG = 'FirestoreClient'; export const MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100; +/** DOMException error code constants. */ +const DOM_EXCEPTION_INVALID_STATE = 11; +const DOM_EXCEPTION_ABORTED = 20; +const DOM_EXCEPTION_QUOTA_EXCEEDED = 22; + /** - * FirestoreClient is a top-level class that constructs and owns all of the - * pieces of the client SDK architecture. It is responsible for creating the - * async queue that is shared by all of the other components in the system. + * FirestoreClient is a top-level class that constructs and owns all of the // + * pieces of the client SDK architecture. It is responsible for creating the // + * async queue that is shared by all of the other components in the system. // */ export class FirestoreClient { private user = User.UNAUTHENTICATED; @@ -127,12 +132,12 @@ export class FirestoreClient { private authCredentials: CredentialsProvider, private appCheckCredentials: CredentialsProvider, /** - * Asynchronous queue responsible for all of our internal processing. When - * we get incoming work from the user (via public API) or the network - * (incoming GRPC messages), we should always schedule onto this queue. - * This ensures all of our work is properly serialized (e.g. we don't - * start processing a new operation while the previous one is waiting for - * an async I/O to complete). + * Asynchronous queue responsible for all of our internal processing. When // + * we get incoming work from the user (via public API) or the network // + * (incoming GRPC messages), we should always schedule onto this queue. // + * This ensures all of our work is properly serialized (e.g. we don't // + * start processing a new operation while the previous one is waiting for // + * an async I/O to complete). // */ public asyncQueue: AsyncQueue, private databaseInfo: DatabaseInfo @@ -171,8 +176,8 @@ export class FirestoreClient { } /** - * Checks that the client has not been terminated. Ensures that other methods on - * this class cannot be called after the client is terminated. + * Checks that the client has not been terminated. Ensures that other methods on // + * this class cannot be called after the client is terminated. // */ verifyNotTerminated(): void { if (this.asyncQueue.isShuttingDown) { @@ -268,16 +273,70 @@ export async function setOnlineComponentProvider( client._onlineComponents = onlineComponentProvider; } +/** + * Decides whether the provided error allows us to gracefully disable + * persistence (as opposed to crashing the client). + */ +export function canFallbackFromIndexedDbError( + error: FirestoreError | DOMException +): boolean { + if (error.name === 'FirebaseError') { + return ( + error.code === Code.FAILED_PRECONDITION || + error.code === Code.UNIMPLEMENTED + ); + } else if ( + typeof DOMException !== 'undefined' && + error instanceof DOMException + ) { + // There are a few known circumstances where we can open IndexedDb but + // trying to read/write will fail (e.g. quota exceeded). For + // well-understood cases, we attempt to detect these and then gracefully + // fall back to memory persistence. + // NOTE: Rather than continue to add to this list, we could decide to + // always fall back, with the risk that we might accidentally hide errors + // representing actual SDK bugs. + return ( + // When the browser is out of quota we could get either quota exceeded + // or an aborted error depending on whether the error happened during + // schema migration. + error.code === DOM_EXCEPTION_QUOTA_EXCEEDED || + error.code === DOM_EXCEPTION_ABORTED || + // Firefox Private Browsing mode disables IndexedDb and returns + // INVALID_STATE for any usage. + error.code === DOM_EXCEPTION_INVALID_STATE + ); + } + + return true; +} + async function ensureOfflineComponents( client: FirestoreClient ): Promise { if (!client._offlineComponents) { if (client._uninitializedComponentsProvider) { logDebug(LOG_TAG, 'Using user provided OfflineComponentProvider'); - await setOfflineComponentProvider( - client, - client._uninitializedComponentsProvider._offline - ); + try { + await setOfflineComponentProvider( + client, + client._uninitializedComponentsProvider._offline + ); + } catch (e) { + const error = e as FirestoreError | DOMException; + if (!canFallbackFromIndexedDbError(error)) { + throw error; + } + logWarn( + 'Error using user provided cache. Falling back to ' + + 'memory cache: ' + + error + ); + await setOfflineComponentProvider( + client, + new MemoryOfflineComponentProvider() + ); + } } else { logDebug(LOG_TAG, 'Using default OfflineComponentProvider'); await setOfflineComponentProvider( diff --git a/packages/firestore/test/integration/api/provider.test.ts b/packages/firestore/test/integration/api/provider.test.ts index 91a89cb6036..653e2f267e5 100644 --- a/packages/firestore/test/integration/api/provider.test.ts +++ b/packages/firestore/test/integration/api/provider.test.ts @@ -130,7 +130,10 @@ describe('Firestore Provider', () => { 'test-use-enablePersistence' ); const db = initializeFirestore(app, DEFAULT_SETTINGS); - await enableIndexedDbPersistence(db); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + expect(enableIndexedDbPersistence(db)).to.be.rejected; + + // SDK still functions. // eslint-disable-next-line @typescript-eslint/no-floating-promises setDoc(doc(db, 'coll/doc'), { field: 'foo' }); expect((await getDocFromCache(doc(db, 'coll/doc'))).data()).to.deep.equal({ diff --git a/packages/firestore/test/integration/browser/indexeddb.test.ts b/packages/firestore/test/integration/browser/indexeddb.test.ts index a63d5178118..b00fd7fbb83 100644 --- a/packages/firestore/test/integration/browser/indexeddb.test.ts +++ b/packages/firestore/test/integration/browser/indexeddb.test.ts @@ -26,7 +26,7 @@ import { } from '../util/firebase_export'; import { isPersistenceAvailable, withTestDb } from '../util/helpers'; -describe('where persistence is unsupported, enablePersistence', () => { +describe('where indexeddb is not available: ', () => { // Only test on platforms where persistence is *not* available (e.g. Edge, // Node.JS). if (isPersistenceAvailable()) { @@ -61,4 +61,14 @@ describe('where persistence is unsupported, enablePersistence', () => { ); }); }); + + it('fails back to memory cache with initializeFirestore too', () => { + // withTestDb will fail the test if persistence is requested but it fails + // so we'll enable persistence here instead. + return withTestDb(/* persistence= */ true, db => { + // Do the set immediately without waiting on the promise. + const testDoc = doc(collection(db, 'test-collection')); + return setDoc(testDoc, { foo: 'bar' }); + }); + }); }); diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 945f27600df..0564546c03d 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -25,7 +25,6 @@ import { terminate, indexedDbLocalCache, clearIndexedDbPersistence, - enableIndexedDbPersistence, CollectionReference, DocumentData, QuerySnapshot, @@ -220,10 +219,11 @@ export async function withNamedTestDbsOrSkipUnlessUsingEmulator( const app = newTestApp(DEFAULT_PROJECT_ID); const dbs: Firestore[] = []; for (const dbName of dbNames) { - const db = newTestFirestore(app, DEFAULT_SETTINGS, dbName); + const newSettings = { ...DEFAULT_SETTINGS }; if (persistence) { - await enableIndexedDbPersistence(db); + newSettings.cache = indexedDbLocalCache(); } + const db = newTestFirestore(app, newSettings, dbName); dbs.push(db); } From 04f85abbf05ba9c2ccffa1e56e62c05dea643695 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 2 Mar 2023 13:56:52 -0500 Subject: [PATCH 23/39] Fix node memory test --- .../firestore/test/unit/local/local_store_indexeddb.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts index f10fa13c1d7..530ca40b5e4 100644 --- a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts +++ b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { isIndexedDBAvailable } from '@firebase/util'; import { expect } from 'chai'; import { serverTimestamp, Timestamp } from '../../../src'; @@ -188,6 +189,10 @@ class AsyncLocalStoreTester { } describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { + if (!isIndexedDBAvailable()) { + return; + } + let persistence: Persistence; let test: AsyncLocalStoreTester; From 2489fc6862601000721c7003550b3aa6fb2f8d0a Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 3 Mar 2023 11:58:23 -0500 Subject: [PATCH 24/39] linting and exporting --- common/api-review/firestore.api.md | 30 ++++++++++++++++++- packages/firestore/src/api.ts | 8 ++++- packages/firestore/src/api/database.ts | 1 - .../test/integration/util/helpers.ts | 6 ++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 2d259d58601..39c4a0b9717 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -369,6 +369,23 @@ export interface LoadBundleTaskProgress { export { LogLevel } +// @public +export interface MemoryCacheSettings { + garbageCollector?: MemoryGarbageCollector; +} + +// @public +export interface MemoryEagerGarbageCollector { + // (undocumented) + kind: 'memoryEager'; +} + +// @public +export function memoryEagerGarbageCollector(): MemoryEagerGarbageCollector; + +// @public +export type MemoryGarbageCollector = MemoryEagerGarbageCollector | MemoryLruGarbageCollector; + // @public export interface MemoryLocalCache { // (undocumented) @@ -376,7 +393,18 @@ export interface MemoryLocalCache { } // @public -export function memoryLocalCache(): MemoryLocalCache; +export function memoryLocalCache(settings?: MemoryCacheSettings): MemoryLocalCache; + +// @public +export interface MemoryLruGarbageCollector { + // (undocumented) + kind: 'memoryLru'; +} + +// @public +export function memoryLruGarbageCollector(settings?: { + cacheSizeBytes?: number; +}): MemoryLruGarbageCollector; // @public export function namedQuery(firestore: Firestore, name: string): Promise; diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 8ce497997d5..1e57e34cf1d 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -38,9 +38,15 @@ export { indexedDbSingleTabManager, IndexedDbSingleTabManager, IndexedDbSingleTabManagerSettings, + IndexedDbTabManager, MemoryLocalCache, + MemoryCacheSettings, + MemoryGarbageCollector, + MemoryEagerGarbageCollector, + MemoryLruGarbageCollector, memoryLocalCache, - IndexedDbTabManager + memoryEagerGarbageCollector, + memoryLruGarbageCollector } from './api/cache_config'; export { FieldPath, documentId } from './api/field_path'; diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 81d5dbf9842..ce72a458d9f 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -26,7 +26,6 @@ import { deepEqual, getDefaultEmulatorHostnameAndPort } from '@firebase/util'; import { User } from '../auth/user'; import { IndexedDbOfflineComponentProvider, - LruGcMemoryOfflineComponentProvider, MultiTabOfflineComponentProvider, OfflineComponentProvider, OnlineComponentProvider diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index ff79e425e0c..e3a4f322661 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -16,7 +16,6 @@ */ import { isIndexedDBAvailable } from '@firebase/util'; -import { memoryLruGarbageCollector } from '../../../src/api/cache_config'; import { collection, @@ -34,7 +33,8 @@ import { SnapshotListenOptions, newTestFirestore, newTestApp, - memoryLocalCache + memoryLocalCache, + memoryLruGarbageCollector } from './firebase_export'; import { ALT_PROJECT_ID, @@ -161,7 +161,7 @@ export function withEnsuredLruGcTestDb( persistence: boolean, fn: (db: Firestore) => Promise ): Promise { - let newSettings = { ...DEFAULT_SETTINGS }; + const newSettings = { ...DEFAULT_SETTINGS }; if (persistence) { newSettings.cache = indexedDbLocalCache({ cacheSizeBytes: 1 * 1024 * 1024 From 70b742fd9dc9fda42832a60dd77b0bbf71eb6f4f Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 3 Mar 2023 14:57:45 -0500 Subject: [PATCH 25/39] rename cache to localCache --- packages/firestore/src/api/database.ts | 5 ++++- packages/firestore/src/api/settings.ts | 2 +- packages/firestore/src/lite-api/settings.ts | 4 ++-- packages/firestore/test/integration/api/provider.test.ts | 2 +- packages/firestore/test/integration/util/helpers.ts | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index ce72a458d9f..8e2c0a091cb 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -167,7 +167,10 @@ export function initializeFirestore( } } - if (settings.cacheSizeBytes !== undefined && settings.cache !== undefined) { + if ( + settings.cacheSizeBytes !== undefined && + settings.localCache !== undefined + ) { throw new FirestoreError( Code.INVALID_ARGUMENT, `cache and cacheSizeBytes cannot be specified at the same time as cacheSizeBytes will` + diff --git a/packages/firestore/src/api/settings.ts b/packages/firestore/src/api/settings.ts index ec0e26439aa..0a9dc9d7ebe 100644 --- a/packages/firestore/src/api/settings.ts +++ b/packages/firestore/src/api/settings.ts @@ -65,7 +65,7 @@ export interface FirestoreSettings extends LiteSettings { * exception during SDK initialization. Instead, using the configuration in * the `FirestoreLocalCache` object to specify the cache size. */ - cache?: FirestoreLocalCache; + localCache?: FirestoreLocalCache; /** * Forces the SDK’s underlying network transport (WebChannel) to use diff --git a/packages/firestore/src/lite-api/settings.ts b/packages/firestore/src/lite-api/settings.ts index a5f0a9325b7..5f395846720 100644 --- a/packages/firestore/src/lite-api/settings.ts +++ b/packages/firestore/src/lite-api/settings.ts @@ -62,7 +62,7 @@ export interface PrivateSettings extends FirestoreSettings { // Used in firestore@exp useFetchStreams?: boolean; - cache?: FirestoreLocalCache; + localCache?: FirestoreLocalCache; } /** @@ -109,7 +109,7 @@ export class FirestoreSettingsImpl { this.credentials = settings.credentials; this.ignoreUndefinedProperties = !!settings.ignoreUndefinedProperties; - this.cache = settings.cache; + this.cache = settings.localCache; if (settings.cacheSizeBytes === undefined) { this.cacheSizeBytes = LRU_DEFAULT_CACHE_SIZE_BYTES; diff --git a/packages/firestore/test/integration/api/provider.test.ts b/packages/firestore/test/integration/api/provider.test.ts index 653e2f267e5..04a148bf987 100644 --- a/packages/firestore/test/integration/api/provider.test.ts +++ b/packages/firestore/test/integration/api/provider.test.ts @@ -148,7 +148,7 @@ describe('Firestore Provider', () => { ); const db = initializeFirestore(app, { ...DEFAULT_SETTINGS, - cache: memoryLocalCache() + localCache: memoryLocalCache() }); expect(() => enableIndexedDbPersistence(db)).to.throw( 'SDK cache is already specified.' diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 0564546c03d..49beba0a4e1 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -186,7 +186,7 @@ export async function withTestDbsSettings( for (let i = 0; i < numDbs; i++) { const newSettings = { ...settings }; if (persistence) { - newSettings.cache = indexedDbLocalCache(); + newSettings.localCache = indexedDbLocalCache(); } const db = newTestFirestore(newTestApp(projectId), newSettings); dbs.push(db); @@ -221,7 +221,7 @@ export async function withNamedTestDbsOrSkipUnlessUsingEmulator( for (const dbName of dbNames) { const newSettings = { ...DEFAULT_SETTINGS }; if (persistence) { - newSettings.cache = indexedDbLocalCache(); + newSettings.localCache = indexedDbLocalCache(); } const db = newTestFirestore(app, newSettings, dbName); dbs.push(db); From 95f537e93798a57f7eaa6f162707084db0385f21 Mon Sep 17 00:00:00 2001 From: wu-hui Date: Fri, 3 Mar 2023 20:24:43 +0000 Subject: [PATCH 26/39] Update API reports --- common/api-review/firestore.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 2d259d58601..01f24078477 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -221,12 +221,12 @@ export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; // @public export interface FirestoreSettings { - cache?: FirestoreLocalCache; cacheSizeBytes?: number; experimentalAutoDetectLongPolling?: boolean; experimentalForceLongPolling?: boolean; host?: string; ignoreUndefinedProperties?: boolean; + localCache?: FirestoreLocalCache; ssl?: boolean; } From a49b8a5648218b24de2231116140cf6ee089038a Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 6 Mar 2023 10:25:45 -0500 Subject: [PATCH 27/39] Tech writer review --- packages/firestore/src/api/cache_config.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 04be3d6eacf..6bd9580008d 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -25,8 +25,8 @@ import { /* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */ /** - * Provides a in-memory cache to the SDK. This is the default cache unless explicitly - * specified otherwise. + * Provides an in-memory cache to the SDK. This is the default cache unless explicitly + * configured otherwise. * * To use, create an instance using the factory function {@link memoryLocalCache()}, then * set the instance to `FirestoreSettings.cache` and call `initializeFirestore` using @@ -120,7 +120,7 @@ export type FirestoreLocalCache = MemoryLocalCache | IndexedDbLocalCache; /** * Creates an instance of `MemoryLocalCache`. The instance can be set to - * `FirestoreSettings.cache` to tell the SDK what cache layer to use. + * `FirestoreSettings.cache` to tell the SDK which cache layer to use. */ export function memoryLocalCache(): MemoryLocalCache { return new MemoryLocalCacheImpl(); @@ -133,7 +133,7 @@ export type IndexedDbCacheSettings = { /** * 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 + * recently used. The SDK does not guarantee that the cache will stay below * that size, only that if the cache exceeds the given size, cleanup will be * attempted. * @@ -150,7 +150,7 @@ export type IndexedDbCacheSettings = { /** * Creates an instance of `IndexedDbLocalCache`. The instance can be set to - * `FirestoreSettings.cache` to tell the SDK what cache layer to use. + * `FirestoreSettings.cache` to tell the SDK which cache layer to use. */ export function indexedDbLocalCache( settings?: IndexedDbCacheSettings @@ -276,10 +276,10 @@ export type IndexedDbTabManager = */ export type IndexedDbSingleTabManagerSettings = { /** - * Whether to force enable indexeddb cache for the client. This cannot be used + * Whether to force-enable IndexedDB cache for the client. This cannot be used * 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. + * Workers. Setting this to `true` will enable IndexedDB, but cause other + * tabs using IndexedDB cache to fail. */ forceOwnership?: boolean; }; From 0c1c12e22767834b77decf4f5abe081c9851aa0e Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 6 Mar 2023 14:39:49 -0500 Subject: [PATCH 28/39] yarn docgen --- common/api-review/firestore.api.md | 4 ++-- packages/firestore/src/api/database.ts | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 01f24078477..da5d539ab09 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -160,10 +160,10 @@ export class DocumentSnapshot { export { EmulatorMockTokenOptions } -// @public @deprecated (undocumented) +// @public @deprecated export function enableIndexedDbPersistence(firestore: Firestore, persistenceSettings?: PersistenceSettings): Promise; -// @public @deprecated (undocumented) +// @public @deprecated export function enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise; // @public diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 8e2c0a091cb..93aab4880d9 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -303,11 +303,6 @@ export function configureFirestore(firestore: Firestore): void { } /** - * @deprecated This function will be removed in a future major release. Instead, set - * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to - * turn on IndexedDb cache. Calling this function when `FirestoreSettings.cache` - * is already specified will throw an exception. - * * Attempts to enable persistent storage, if possible. * * Must be called before any other functions (other than @@ -329,6 +324,10 @@ export function configureFirestore(firestore: Firestore): void { * @param persistenceSettings - Optional settings object to configure * persistence. * @returns A `Promise` that represents successfully enabling persistent storage. + * @deprecated This function will be removed in a future major release. Instead, set + * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to + * turn on IndexedDb cache. Calling this function when `FirestoreSettings.cache` + * is already specified will throw an exception. */ export function enableIndexedDbPersistence( firestore: Firestore, @@ -365,11 +364,6 @@ export function enableIndexedDbPersistence( } /** - * @deprecated This function will be removed in a future major release. Instead, set - * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to - * turn on indexeddb cache. Calling this function when `FirestoreSettings.cache` - * is already specified will throw an exception. - * * Attempts to enable multi-tab persistent storage, if possible. If enabled * across all tabs, all operations share access to local persistence, including * shared execution of queries and latency-compensated local document updates @@ -390,6 +384,10 @@ export function enableIndexedDbPersistence( * @param firestore - The {@link Firestore} instance to enable persistence for. * @returns A `Promise` that represents successfully enabling persistent * storage. + * @deprecated This function will be removed in a future major release. Instead, set + * `FirestoreSettings.cache` to an instance of `IndexedDbLocalCache` to + * turn on indexeddb cache. Calling this function when `FirestoreSettings.cache` + * is already specified will throw an exception. */ export function enableMultiTabIndexedDbPersistence( firestore: Firestore From 9771e744ff36462252c097cf01a3db1e18658df2 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 17 Mar 2023 11:33:24 -0400 Subject: [PATCH 29/39] Fix merge error and re-build devsite --- common/api-review/auth.api.md | 27 +- common/api-review/firestore.api.md | 850 ++++++------------ docs-devsite/firestore_.md | 70 +- .../firestore_.memorycachesettings.md | 35 + .../firestore_.memoryeagergarbagecollector.md | 37 + docs-devsite/firestore_.memorylocalcache.md | 4 +- .../firestore_.memorylrugarbagecollector.md | 37 + .../firestore_.persistentlocalcache.md | 4 +- ...firestore_.persistentmultipletabmanager.md | 4 +- .../firestore_.persistentsingletabmanager.md | 4 +- packages/auth/api-extractor.json | 2 +- packages/firestore/src/api.ts | 1 + .../test/integration/util/helpers.ts | 4 +- 13 files changed, 480 insertions(+), 599 deletions(-) create mode 100644 docs-devsite/firestore_.memorycachesettings.md create mode 100644 docs-devsite/firestore_.memoryeagergarbagecollector.md create mode 100644 docs-devsite/firestore_.memorylrugarbagecollector.md diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index 045b64bcf19..cf25749656c 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -103,14 +103,14 @@ export class AuthCredential { protected constructor( providerId: string, signInMethod: string); - // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _getIdTokenResponse(_auth: AuthInternal): Promise; // @internal (undocumented) _getReauthenticationResolver(_auth: AuthInternal): Promise; - // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _linkToIdToken(_auth: AuthInternal, _idToken: string): Promise; @@ -283,6 +283,9 @@ export function connectAuthEmulator(auth: Auth, url: string, options?: { disableWarnings: boolean; }): void; +// @public +export const cordovaPopupRedirectResolver: PopupRedirectResolver; + // @public export function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; @@ -346,7 +349,7 @@ export interface EmulatorConfig { export { ErrorFn } -// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.doc.d.ts // // @public export class FacebookAuthProvider extends BaseOAuthProvider { @@ -381,6 +384,9 @@ export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; @@ -490,7 +496,7 @@ export type NextOrObserver = NextFn | Observer; export class OAuthCredential extends AuthCredential { accessToken?: string; static fromJSON(json: string | object): OAuthCredential | null; - // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) static _fromParams(params: OAuthCredentialParams): OAuthCredential; @@ -568,7 +574,7 @@ export class PhoneAuthCredential extends AuthCredential { _getReauthenticationResolver(auth: AuthInternal): Promise; // @internal (undocumented) _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _makeVerificationRequest(): SignInWithPhoneNumberRequest; @@ -647,6 +653,9 @@ export interface ReactNativeAsyncStorage { setItem(key: string, value: string): Promise; } +// @public +export const reactNativeLocalPersistence: Persistence; + // @public export function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; @@ -665,13 +674,13 @@ export interface RecaptchaParameters { [key: string]: any; } -// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.doc.d.ts // // @public export class RecaptchaVerifier implements ApplicationVerifierInternal { constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth); clear(): void; - // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) readonly _recaptchaLoader: ReCaptchaLoader; @@ -685,7 +694,7 @@ export class RecaptchaVerifier implements ApplicationVerifierInternal { // @public export function reload(user: User): Promise; -// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.doc.d.ts // // @public export class SAMLAuthProvider extends FederatedAuthProvider { diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 6869bd4daaa..cc459698fb9 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -3,28 +3,23 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts + import { EmulatorMockTokenOptions } from '@firebase/util'; import { FirebaseApp } from '@firebase/app'; import { FirebaseError } from '@firebase/util'; import { LogLevelString as LogLevel } from '@firebase/logger'; // @public -export function addDoc( - reference: CollectionReference, - data: WithFieldValue -): Promise>; +export function addDoc(reference: CollectionReference, data: WithFieldValue): Promise>; // @public -export type AddPrefixToKeys< - Prefix extends string, - T extends Record -> = { - [K in keyof T & string as `${Prefix}.${K}`]+?: T[K]; +export type AddPrefixToKeys> = { + [K in keyof T & string as `${Prefix}.${K}`]+?: T[K]; }; // @public export class AggregateField { - readonly type = 'AggregateField'; + readonly type = "AggregateField"; } // @public @@ -32,32 +27,27 @@ export type AggregateFieldType = AggregateField; // @public export class AggregateQuerySnapshot { - data(): AggregateSpecData; - readonly query: Query; - readonly type = 'AggregateQuerySnapshot'; + data(): AggregateSpecData; + readonly query: Query; + readonly type = "AggregateQuerySnapshot"; } // @public -export function aggregateQuerySnapshotEqual( - left: AggregateQuerySnapshot, - right: AggregateQuerySnapshot -): boolean; +export function aggregateQuerySnapshotEqual(left: AggregateQuerySnapshot, right: AggregateQuerySnapshot): boolean; // @public export interface AggregateSpec { - // (undocumented) - [field: string]: AggregateFieldType; + // (undocumented) + [field: string]: AggregateFieldType; } // @public export type AggregateSpecData = { - [P in keyof T]: T[P] extends AggregateField ? U : never; + [P in keyof T]: T[P] extends AggregateField ? U : never; }; // @public -export function and( - ...queryConstraints: QueryFilterConstraint[] -): QueryCompositeFilterConstraint; +export function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; // @public export function arrayRemove(...elements: unknown[]): FieldValue; @@ -67,76 +57,49 @@ export function arrayUnion(...elements: unknown[]): FieldValue; // @public export class Bytes { - static fromBase64String(base64: string): Bytes; - static fromUint8Array(array: Uint8Array): Bytes; - isEqual(other: Bytes): boolean; - toBase64(): string; - toString(): string; - toUint8Array(): Uint8Array; + static fromBase64String(base64: string): Bytes; + static fromUint8Array(array: Uint8Array): Bytes; + isEqual(other: Bytes): boolean; + toBase64(): string; + toString(): string; + toUint8Array(): Uint8Array; } // @public export const CACHE_SIZE_UNLIMITED = -1; // @public -export type ChildUpdateFields = V extends Record< - string, - unknown -> - ? AddPrefixToKeys> - : never; +export type ChildUpdateFields = V extends Record ? AddPrefixToKeys> : never; // @public export function clearIndexedDbPersistence(firestore: Firestore): Promise; // @public -export function collection( - firestore: Firestore, - path: string, - ...pathSegments: string[] -): CollectionReference; +export function collection(firestore: Firestore, path: string, ...pathSegments: string[]): CollectionReference; // @public -export function collection( - reference: CollectionReference, - path: string, - ...pathSegments: string[] -): CollectionReference; +export function collection(reference: CollectionReference, path: string, ...pathSegments: string[]): CollectionReference; // @public -export function collection( - reference: DocumentReference, - path: string, - ...pathSegments: string[] -): CollectionReference; +export function collection(reference: DocumentReference, path: string, ...pathSegments: string[]): CollectionReference; // @public -export function collectionGroup( - firestore: Firestore, - collectionId: string -): Query; +export function collectionGroup(firestore: Firestore, collectionId: string): Query; // @public export class CollectionReference extends Query { - get id(): string; - get parent(): DocumentReference | null; - get path(): string; - readonly type = 'collection'; - withConverter( - converter: FirestoreDataConverter - ): CollectionReference; - withConverter(converter: null): CollectionReference; -} - -// @public -export function connectFirestoreEmulator( - firestore: Firestore, - host: string, - port: number, - options?: { + get id(): string; + get parent(): DocumentReference | null; + get path(): string; + readonly type = "collection"; + withConverter(converter: FirestoreDataConverter): CollectionReference; + withConverter(converter: null): CollectionReference; +} + +// @public +export function connectFirestoreEmulator(firestore: Firestore, host: string, port: number, options?: { mockUserToken?: EmulatorMockTokenOptions | string; - } -): void; +}): void; // @public export function deleteDoc(reference: DocumentReference): Promise; @@ -148,32 +111,20 @@ export function deleteField(): FieldValue; export function disableNetwork(firestore: Firestore): Promise; // @public -export function doc( - firestore: Firestore, - path: string, - ...pathSegments: string[] -): DocumentReference; +export function doc(firestore: Firestore, path: string, ...pathSegments: string[]): DocumentReference; // @public -export function doc( - reference: CollectionReference, - path?: string, - ...pathSegments: string[] -): DocumentReference; +export function doc(reference: CollectionReference, path?: string, ...pathSegments: string[]): DocumentReference; // @public -export function doc( - reference: DocumentReference, - path: string, - ...pathSegments: string[] -): DocumentReference; +export function doc(reference: DocumentReference, path: string, ...pathSegments: string[]): DocumentReference; // @public export interface DocumentChange { - readonly doc: QueryDocumentSnapshot; - readonly newIndex: number; - readonly oldIndex: number; - readonly type: DocumentChangeType; + readonly doc: QueryDocumentSnapshot; + readonly newIndex: number; + readonly oldIndex: number; + readonly type: DocumentChangeType; } // @public @@ -181,7 +132,7 @@ export type DocumentChangeType = 'added' | 'removed' | 'modified'; // @public export interface DocumentData { - [field: string]: any; + [field: string]: any; } // @public @@ -189,163 +140,124 @@ export function documentId(): FieldPath; // @public export class DocumentReference { - readonly converter: FirestoreDataConverter | null; - readonly firestore: Firestore; - get id(): string; - get parent(): CollectionReference; - get path(): string; - readonly type = 'document'; - withConverter(converter: FirestoreDataConverter): DocumentReference; - withConverter(converter: null): DocumentReference; + readonly converter: FirestoreDataConverter | null; + readonly firestore: Firestore; + get id(): string; + get parent(): CollectionReference; + get path(): string; + readonly type = "document"; + withConverter(converter: FirestoreDataConverter): DocumentReference; + withConverter(converter: null): DocumentReference; } // @public export class DocumentSnapshot { - protected constructor(); - data(options?: SnapshotOptions): T | undefined; - exists(): this is QueryDocumentSnapshot; - get(fieldPath: string | FieldPath, options?: SnapshotOptions): any; - get id(): string; - readonly metadata: SnapshotMetadata; - get ref(): DocumentReference; + protected constructor(); + data(options?: SnapshotOptions): T | undefined; + exists(): this is QueryDocumentSnapshot; + get(fieldPath: string | FieldPath, options?: SnapshotOptions): any; + get id(): string; + readonly metadata: SnapshotMetadata; + get ref(): DocumentReference; } -export { EmulatorMockTokenOptions }; +export { EmulatorMockTokenOptions } // @public @deprecated -export function enableIndexedDbPersistence( - firestore: Firestore, - persistenceSettings?: PersistenceSettings -): Promise; +export function enableIndexedDbPersistence(firestore: Firestore, persistenceSettings?: PersistenceSettings): Promise; // @public @deprecated -export function enableMultiTabIndexedDbPersistence( - firestore: Firestore -): Promise; +export function enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise; // @public export function enableNetwork(firestore: Firestore): Promise; // @public -export function endAt( - snapshot: DocumentSnapshot -): QueryEndAtConstraint; +export function endAt(snapshot: DocumentSnapshot): QueryEndAtConstraint; // @public export function endAt(...fieldValues: unknown[]): QueryEndAtConstraint; // @public -export function endBefore( - snapshot: DocumentSnapshot -): QueryEndAtConstraint; +export function endBefore(snapshot: DocumentSnapshot): QueryEndAtConstraint; // @public export function endBefore(...fieldValues: unknown[]): QueryEndAtConstraint; // @public export class FieldPath { - constructor(...fieldNames: string[]); - isEqual(other: FieldPath): boolean; + constructor(...fieldNames: string[]); + isEqual(other: FieldPath): boolean; } // @public export abstract class FieldValue { - abstract isEqual(other: FieldValue): boolean; + abstract isEqual(other: FieldValue): boolean; } // @public export class Firestore { - get app(): FirebaseApp; - toJSON(): object; - type: 'firestore-lite' | 'firestore'; + get app(): FirebaseApp; + toJSON(): object; + type: 'firestore-lite' | 'firestore'; } // @public export interface FirestoreDataConverter { - fromFirestore( - snapshot: QueryDocumentSnapshot, - options?: SnapshotOptions - ): T; - toFirestore(modelObject: WithFieldValue): DocumentData; - toFirestore( - modelObject: PartialWithFieldValue, - options: SetOptions - ): DocumentData; + fromFirestore(snapshot: QueryDocumentSnapshot, options?: SnapshotOptions): T; + toFirestore(modelObject: WithFieldValue): DocumentData; + toFirestore(modelObject: PartialWithFieldValue, options: SetOptions): DocumentData; } // @public export class FirestoreError extends FirebaseError { - readonly code: FirestoreErrorCode; - readonly message: string; - readonly stack?: string; -} - -// @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'; + readonly code: FirestoreErrorCode; + readonly message: string; + readonly stack?: string; +} + +// @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 | PersistentLocalCache; // @public export interface FirestoreSettings { - cacheSizeBytes?: number; - experimentalAutoDetectLongPolling?: boolean; - experimentalForceLongPolling?: boolean; - host?: string; - ignoreUndefinedProperties?: boolean; - localCache?: FirestoreLocalCache; - ssl?: boolean; + cacheSizeBytes?: number; + experimentalAutoDetectLongPolling?: boolean; + experimentalForceLongPolling?: boolean; + host?: string; + ignoreUndefinedProperties?: boolean; + localCache?: FirestoreLocalCache; + ssl?: boolean; } // @public export class GeoPoint { - constructor(latitude: number, longitude: number); - isEqual(other: GeoPoint): boolean; - get latitude(): number; - get longitude(): number; - toJSON(): { - latitude: number; - longitude: number; - }; + constructor(latitude: number, longitude: number); + isEqual(other: GeoPoint): boolean; + get latitude(): number; + get longitude(): number; + toJSON(): { + latitude: number; + longitude: number; + }; } // @public -export function getCountFromServer(query: Query): Promise< - AggregateQuerySnapshot<{ +export function getCountFromServer(query: Query): Promise; - }> ->; +}>>; // @public -export function getDoc( - reference: DocumentReference -): Promise>; +export function getDoc(reference: DocumentReference): Promise>; // @public -export function getDocFromCache( - reference: DocumentReference -): Promise>; +export function getDocFromCache(reference: DocumentReference): Promise>; // @public -export function getDocFromServer( - reference: DocumentReference -): Promise>; +export function getDocFromServer(reference: DocumentReference): Promise>; // @public export function getDocs(query: Query): Promise>; @@ -354,9 +266,7 @@ export function getDocs(query: Query): Promise>; export function getDocsFromCache(query: Query): Promise>; // @public -export function getDocsFromServer( - query: Query -): Promise>; +export function getDocsFromServer(query: Query): Promise>; // @public export function getFirestore(app: FirebaseApp): Firestore; @@ -369,287 +279,191 @@ export function increment(n: number): FieldValue; // @beta export interface Index { - // (undocumented) - [key: string]: unknown; - readonly collectionGroup: string; - readonly fields?: IndexField[]; + // (undocumented) + [key: string]: unknown; + readonly collectionGroup: string; + readonly fields?: IndexField[]; } // @beta export interface IndexConfiguration { - // (undocumented) - [key: string]: unknown; - readonly indexes?: Index[]; + // (undocumented) + [key: string]: unknown; + readonly indexes?: Index[]; } -// @public -export interface IndexedDbCacheSettings { - cacheSizeBytes?: number; - tabManager?: IndexedDbTabManager; +// @beta +export interface IndexField { + // (undocumented) + [key: string]: unknown; + readonly arrayConfig?: 'CONTAINS'; + readonly fieldPath: string; + readonly order?: 'ASCENDING' | 'DESCENDING'; } // @public -export interface IndexedDbLocalCache { - // (undocumented) - kind: 'indexeddb'; -} +export function initializeFirestore(app: FirebaseApp, settings: FirestoreSettings, databaseId?: string): Firestore; // @public -export function indexedDbLocalCache( - settings?: IndexedDbCacheSettings -): IndexedDbLocalCache; +export function limit(limit: number): QueryLimitConstraint; // @public -export interface IndexedDbMultipleTabManager { - // (undocumented) - kind: 'IndexedDbMultipleTab'; -} +export function limitToLast(limit: number): QueryLimitConstraint; // @public -export function indexedDbMultipleTabManager(): IndexedDbMultipleTabManager; +export function loadBundle(firestore: Firestore, bundleData: ReadableStream | ArrayBuffer | string): LoadBundleTask; // @public -export interface IndexedDbSingleTabManager { - // (undocumented) - kind: 'indexedDbSingleTab'; +export class LoadBundleTask implements PromiseLike { + catch(onRejected: (a: Error) => R | PromiseLike): Promise; + onProgress(next?: (progress: LoadBundleTaskProgress) => unknown, error?: (err: Error) => unknown, complete?: () => void): void; + then(onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike, onRejected?: (a: Error) => R | PromiseLike): Promise; } // @public -export function indexedDbSingleTabManager( - settings: IndexedDbSingleTabManagerSettings | undefined -): IndexedDbSingleTabManager; - -// @public -export interface IndexedDbSingleTabManagerSettings { - forceOwnership?: boolean; +export interface LoadBundleTaskProgress { + bytesLoaded: number; + documentsLoaded: number; + taskState: TaskState; + totalBytes: number; + totalDocuments: number; } -// @public -export type IndexedDbTabManager = - | IndexedDbSingleTabManager - | IndexedDbMultipleTabManager; - -// @beta -export interface IndexField { - // (undocumented) - [key: string]: unknown; - readonly arrayConfig?: 'CONTAINS'; - readonly fieldPath: string; - readonly order?: 'ASCENDING' | 'DESCENDING'; -} +export { LogLevel } // @public -export function initializeFirestore( - app: FirebaseApp, - settings: FirestoreSettings, - databaseId?: string -): Firestore; +export interface MemoryCacheSettings { + garbageCollector?: MemoryGarbageCollector; +} // @public -export function limit(limit: number): QueryLimitConstraint; +export interface MemoryEagerGarbageCollector { + // (undocumented) + kind: "memoryEager"; +} // @public -export function limitToLast(limit: number): QueryLimitConstraint; +export function memoryEagerGarbageCollector(): MemoryEagerGarbageCollector; // @public -export function loadBundle( - firestore: Firestore, - bundleData: ReadableStream | ArrayBuffer | string -): LoadBundleTask; +export type MemoryGarbageCollector = MemoryEagerGarbageCollector | MemoryLruGarbageCollector; // @public -export class LoadBundleTask implements PromiseLike { - catch( - onRejected: (a: Error) => R | PromiseLike - ): Promise; - onProgress( - next?: (progress: LoadBundleTaskProgress) => unknown, - error?: (err: Error) => unknown, - complete?: () => void - ): void; - then( - onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike, - onRejected?: (a: Error) => R | PromiseLike - ): Promise; +export interface MemoryLocalCache { + // (undocumented) + kind: "memory"; } // @public -export interface LoadBundleTaskProgress { - bytesLoaded: number; - documentsLoaded: number; - taskState: TaskState; - totalBytes: number; - totalDocuments: number; -} - -export { LogLevel }; +export function memoryLocalCache(settings?: MemoryCacheSettings): MemoryLocalCache; // @public -export interface MemoryLocalCache { - // (undocumented) - kind: 'memory'; +export interface MemoryLruGarbageCollector { + // (undocumented) + kind: "memoryLru"; } // @public -export function memoryLocalCache(): MemoryLocalCache; +export function memoryLruGarbageCollector(settings?: { + cacheSizeBytes?: number; +}): MemoryLruGarbageCollector; // @public -export function namedQuery( - firestore: Firestore, - name: string -): Promise; +export function namedQuery(firestore: Firestore, name: string): Promise; // @public -export type NestedUpdateFields> = - UnionToIntersection< - { - [K in keyof T & string]: ChildUpdateFields; - }[keyof T & string] - >; +export type NestedUpdateFields> = UnionToIntersection<{ + [K in keyof T & string]: ChildUpdateFields; +}[keyof T & string]>; // @public -export function onSnapshot( - reference: DocumentReference, - observer: { +export function onSnapshot(reference: DocumentReference, observer: { next?: (snapshot: DocumentSnapshot) => void; error?: (error: FirestoreError) => void; complete?: () => void; - } -): Unsubscribe; +}): Unsubscribe; // @public -export function onSnapshot( - reference: DocumentReference, - options: SnapshotListenOptions, - observer: { +export function onSnapshot(reference: DocumentReference, options: SnapshotListenOptions, observer: { next?: (snapshot: DocumentSnapshot) => void; error?: (error: FirestoreError) => void; complete?: () => void; - } -): Unsubscribe; +}): Unsubscribe; // @public -export function onSnapshot( - reference: DocumentReference, - onNext: (snapshot: DocumentSnapshot) => void, - onError?: (error: FirestoreError) => void, - onCompletion?: () => void -): Unsubscribe; +export function onSnapshot(reference: DocumentReference, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void): Unsubscribe; // @public -export function onSnapshot( - reference: DocumentReference, - options: SnapshotListenOptions, - onNext: (snapshot: DocumentSnapshot) => void, - onError?: (error: FirestoreError) => void, - onCompletion?: () => void -): Unsubscribe; +export function onSnapshot(reference: DocumentReference, options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void): Unsubscribe; // @public -export function onSnapshot( - query: Query, - observer: { +export function onSnapshot(query: Query, observer: { next?: (snapshot: QuerySnapshot) => void; error?: (error: FirestoreError) => void; complete?: () => void; - } -): Unsubscribe; +}): Unsubscribe; // @public -export function onSnapshot( - query: Query, - options: SnapshotListenOptions, - observer: { +export function onSnapshot(query: Query, options: SnapshotListenOptions, observer: { next?: (snapshot: QuerySnapshot) => void; error?: (error: FirestoreError) => void; complete?: () => void; - } -): Unsubscribe; +}): Unsubscribe; // @public -export function onSnapshot( - query: Query, - onNext: (snapshot: QuerySnapshot) => void, - onError?: (error: FirestoreError) => void, - onCompletion?: () => void -): Unsubscribe; +export function onSnapshot(query: Query, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void): Unsubscribe; // @public -export function onSnapshot( - query: Query, - options: SnapshotListenOptions, - onNext: (snapshot: QuerySnapshot) => void, - onError?: (error: FirestoreError) => void, - onCompletion?: () => void -): Unsubscribe; +export function onSnapshot(query: Query, options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void): Unsubscribe; // @public -export function onSnapshotsInSync( - firestore: Firestore, - observer: { +export function onSnapshotsInSync(firestore: Firestore, observer: { next?: (value: void) => void; error?: (error: FirestoreError) => void; complete?: () => void; - } -): Unsubscribe; +}): Unsubscribe; // @public -export function onSnapshotsInSync( - firestore: Firestore, - onSync: () => void -): Unsubscribe; +export function onSnapshotsInSync(firestore: Firestore, onSync: () => void): Unsubscribe; // @public -export function or( - ...queryConstraints: QueryFilterConstraint[] -): QueryCompositeFilterConstraint; +export function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; // @public -export function orderBy( - fieldPath: string | FieldPath, - directionStr?: OrderByDirection -): QueryOrderByConstraint; +export function orderBy(fieldPath: string | FieldPath, directionStr?: OrderByDirection): QueryOrderByConstraint; // @public export type OrderByDirection = 'desc' | 'asc'; // @public -export type PartialWithFieldValue = - | Partial - | (T extends Primitive - ? T - : T extends {} - ? { - [K in keyof T]?: PartialWithFieldValue | FieldValue; - } - : never); +export type PartialWithFieldValue = Partial | (T extends Primitive ? T : T extends {} ? { + [K in keyof T]?: PartialWithFieldValue | FieldValue; +} : never); // @public export interface PersistenceSettings { - forceOwnership?: boolean; + forceOwnership?: boolean; } // @public export interface PersistentCacheSettings { - cacheSizeBytes?: number; - tabManager?: PersistentTabManager; + cacheSizeBytes?: number; + tabManager?: PersistentTabManager; } // @public export interface PersistentLocalCache { - // (undocumented) - kind: 'persistent'; + // (undocumented) + kind: "persistent"; } // @public -export function persistentLocalCache( - settings?: PersistentCacheSettings -): PersistentLocalCache; +export function persistentLocalCache(settings?: PersistentCacheSettings): PersistentLocalCache; // @public export interface PersistentMultipleTabManager { - // (undocumented) - kind: 'PersistentMultipleTab'; + // (undocumented) + kind: "PersistentMultipleTab"; } // @public @@ -657,83 +471,62 @@ export function persistentMultipleTabManager(): PersistentMultipleTabManager; // @public export interface PersistentSingleTabManager { - // (undocumented) - kind: 'persistentSingleTab'; + // (undocumented) + kind: "persistentSingleTab"; } // @public -export function persistentSingleTabManager( - settings: PersistentSingleTabManagerSettings | undefined -): PersistentSingleTabManager; +export function persistentSingleTabManager(settings: PersistentSingleTabManagerSettings | undefined): PersistentSingleTabManager; // @public export interface PersistentSingleTabManagerSettings { - forceOwnership?: boolean; + forceOwnership?: boolean; } // @public -export type PersistentTabManager = - | PersistentSingleTabManager - | PersistentMultipleTabManager; +export type PersistentTabManager = PersistentSingleTabManager | PersistentMultipleTabManager; // @public export type Primitive = string | number | boolean | undefined | null; // @public export class Query { - protected constructor(); - readonly converter: FirestoreDataConverter | null; - readonly firestore: Firestore; - readonly type: 'query' | 'collection'; - withConverter(converter: null): Query; - withConverter(converter: FirestoreDataConverter): Query; + protected constructor(); + readonly converter: FirestoreDataConverter | null; + readonly firestore: Firestore; + readonly type: 'query' | 'collection'; + withConverter(converter: null): Query; + withConverter(converter: FirestoreDataConverter): Query; } // @public -export function query( - query: Query, - compositeFilter: QueryCompositeFilterConstraint, - ...queryConstraints: QueryNonFilterConstraint[] -): Query; +export function query(query: Query, compositeFilter: QueryCompositeFilterConstraint, ...queryConstraints: QueryNonFilterConstraint[]): Query; // @public -export function query( - query: Query, - ...queryConstraints: QueryConstraint[] -): Query; +export function query(query: Query, ...queryConstraints: QueryConstraint[]): Query; // @public export class QueryCompositeFilterConstraint { - readonly type: 'or' | 'and'; + readonly type: 'or' | 'and'; } // @public export abstract class QueryConstraint { - abstract readonly type: QueryConstraintType; + abstract readonly type: QueryConstraintType; } // @public -export type QueryConstraintType = - | 'where' - | 'orderBy' - | 'limit' - | 'limitToLast' - | 'startAt' - | 'startAfter' - | 'endAt' - | 'endBefore'; +export type QueryConstraintType = 'where' | 'orderBy' | 'limit' | 'limitToLast' | 'startAt' | 'startAfter' | 'endAt' | 'endBefore'; // @public -export class QueryDocumentSnapshot< - T = DocumentData -> extends DocumentSnapshot { - // @override - data(options?: SnapshotOptions): T; +export class QueryDocumentSnapshot extends DocumentSnapshot { + // @override + data(options?: SnapshotOptions): T; } // @public export class QueryEndAtConstraint extends QueryConstraint { - readonly type: 'endBefore' | 'endAt'; + readonly type: 'endBefore' | 'endAt'; } // @public @@ -741,138 +534,100 @@ export function queryEqual(left: Query, right: Query): boolean; // @public export class QueryFieldFilterConstraint extends QueryConstraint { - readonly type = 'where'; + readonly type = "where"; } // @public -export type QueryFilterConstraint = - | QueryFieldFilterConstraint - | QueryCompositeFilterConstraint; +export type QueryFilterConstraint = QueryFieldFilterConstraint | QueryCompositeFilterConstraint; // @public export class QueryLimitConstraint extends QueryConstraint { - readonly type: 'limit' | 'limitToLast'; + readonly type: 'limit' | 'limitToLast'; } // @public -export type QueryNonFilterConstraint = - | QueryOrderByConstraint - | QueryLimitConstraint - | QueryStartAtConstraint - | QueryEndAtConstraint; +export type QueryNonFilterConstraint = QueryOrderByConstraint | QueryLimitConstraint | QueryStartAtConstraint | QueryEndAtConstraint; // @public export class QueryOrderByConstraint extends QueryConstraint { - readonly type = 'orderBy'; + readonly type = "orderBy"; } // @public export class QuerySnapshot { - docChanges(options?: SnapshotListenOptions): Array>; - get docs(): Array>; - get empty(): boolean; - forEach( - callback: (result: QueryDocumentSnapshot) => void, - thisArg?: unknown - ): void; - readonly metadata: SnapshotMetadata; - readonly query: Query; - get size(): number; + docChanges(options?: SnapshotListenOptions): Array>; + get docs(): Array>; + get empty(): boolean; + forEach(callback: (result: QueryDocumentSnapshot) => void, thisArg?: unknown): void; + readonly metadata: SnapshotMetadata; + readonly query: Query; + get size(): number; } // @public export class QueryStartAtConstraint extends QueryConstraint { - readonly type: 'startAt' | 'startAfter'; + readonly type: 'startAt' | 'startAfter'; } // @public -export function refEqual( - left: DocumentReference | CollectionReference, - right: DocumentReference | CollectionReference -): boolean; +export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; // @public -export function runTransaction( - firestore: Firestore, - updateFunction: (transaction: Transaction) => Promise, - options?: TransactionOptions -): Promise; +export function runTransaction(firestore: Firestore, updateFunction: (transaction: Transaction) => Promise, options?: TransactionOptions): Promise; // @public export function serverTimestamp(): FieldValue; // @public -export function setDoc( - reference: DocumentReference, - data: WithFieldValue -): Promise; +export function setDoc(reference: DocumentReference, data: WithFieldValue): Promise; // @public -export function setDoc( - reference: DocumentReference, - data: PartialWithFieldValue, - options: SetOptions -): Promise; +export function setDoc(reference: DocumentReference, data: PartialWithFieldValue, options: SetOptions): Promise; // @beta -export function setIndexConfiguration( - firestore: Firestore, - configuration: IndexConfiguration -): Promise; +export function setIndexConfiguration(firestore: Firestore, configuration: IndexConfiguration): Promise; // @beta -export function setIndexConfiguration( - firestore: Firestore, - json: string -): Promise; +export function setIndexConfiguration(firestore: Firestore, json: string): Promise; // @public export function setLogLevel(logLevel: LogLevel): void; // @public -export type SetOptions = - | { - readonly merge?: boolean; - } - | { - readonly mergeFields?: Array; - }; +export type SetOptions = { + readonly merge?: boolean; +} | { + readonly mergeFields?: Array; +}; // @public -export function snapshotEqual( - left: DocumentSnapshot | QuerySnapshot, - right: DocumentSnapshot | QuerySnapshot -): boolean; +export function snapshotEqual(left: DocumentSnapshot | QuerySnapshot, right: DocumentSnapshot | QuerySnapshot): boolean; // @public export interface SnapshotListenOptions { - readonly includeMetadataChanges?: boolean; + readonly includeMetadataChanges?: boolean; } // @public export class SnapshotMetadata { - readonly fromCache: boolean; - readonly hasPendingWrites: boolean; - isEqual(other: SnapshotMetadata): boolean; + readonly fromCache: boolean; + readonly hasPendingWrites: boolean; + isEqual(other: SnapshotMetadata): boolean; } // @public export interface SnapshotOptions { - readonly serverTimestamps?: 'estimate' | 'previous' | 'none'; + readonly serverTimestamps?: 'estimate' | 'previous' | 'none'; } // @public -export function startAfter( - snapshot: DocumentSnapshot -): QueryStartAtConstraint; +export function startAfter(snapshot: DocumentSnapshot): QueryStartAtConstraint; // @public export function startAfter(...fieldValues: unknown[]): QueryStartAtConstraint; // @public -export function startAt( - snapshot: DocumentSnapshot -): QueryStartAtConstraint; +export function startAt(snapshot: DocumentSnapshot): QueryStartAtConstraint; // @public export function startAt(...fieldValues: unknown[]): QueryStartAtConstraint; @@ -885,138 +640,85 @@ export function terminate(firestore: Firestore): Promise; // @public export class Timestamp { - constructor(seconds: number, nanoseconds: number); - static fromDate(date: Date): Timestamp; - static fromMillis(milliseconds: number): Timestamp; - isEqual(other: Timestamp): boolean; - readonly nanoseconds: number; - static now(): Timestamp; - readonly seconds: number; - toDate(): Date; - toJSON(): { - seconds: number; - nanoseconds: number; - }; - toMillis(): number; - toString(): string; - valueOf(): string; + constructor( + seconds: number, + nanoseconds: number); + static fromDate(date: Date): Timestamp; + static fromMillis(milliseconds: number): Timestamp; + isEqual(other: Timestamp): boolean; + readonly nanoseconds: number; + static now(): Timestamp; + readonly seconds: number; + toDate(): Date; + toJSON(): { + seconds: number; + nanoseconds: number; + }; + toMillis(): number; + toString(): string; + valueOf(): string; } // @public export class Transaction { - delete(documentRef: DocumentReference): this; - get(documentRef: DocumentReference): Promise>; - set(documentRef: DocumentReference, data: WithFieldValue): this; - set( - documentRef: DocumentReference, - data: PartialWithFieldValue, - options: SetOptions - ): this; - update(documentRef: DocumentReference, data: UpdateData): this; - update( - documentRef: DocumentReference, - field: string | FieldPath, - value: unknown, - ...moreFieldsAndValues: unknown[] - ): this; + delete(documentRef: DocumentReference): this; + get(documentRef: DocumentReference): Promise>; + set(documentRef: DocumentReference, data: WithFieldValue): this; + set(documentRef: DocumentReference, data: PartialWithFieldValue, options: SetOptions): this; + update(documentRef: DocumentReference, data: UpdateData): this; + update(documentRef: DocumentReference, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): this; } // @public export interface TransactionOptions { - readonly maxAttempts?: number; + readonly maxAttempts?: number; } // @public -export type UnionToIntersection = ( - U extends unknown ? (k: U) => void : never -) extends (k: infer I) => void - ? I - : never; +export type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; // @public export interface Unsubscribe { - (): void; + (): void; } // @public -export type UpdateData = T extends Primitive - ? T - : T extends {} - ? { - [K in keyof T]?: UpdateData | FieldValue; - } & NestedUpdateFields - : Partial; +export type UpdateData = T extends Primitive ? T : T extends {} ? { + [K in keyof T]?: UpdateData | FieldValue; +} & NestedUpdateFields : Partial; // @public -export function updateDoc( - reference: DocumentReference, - data: UpdateData -): Promise; +export function updateDoc(reference: DocumentReference, data: UpdateData): Promise; // @public -export function updateDoc( - reference: DocumentReference, - field: string | FieldPath, - value: unknown, - ...moreFieldsAndValues: unknown[] -): Promise; +export function updateDoc(reference: DocumentReference, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): Promise; // @public export function waitForPendingWrites(firestore: Firestore): Promise; // @public -export function where( - fieldPath: string | FieldPath, - opStr: WhereFilterOp, - value: unknown -): QueryFieldFilterConstraint; +export function where(fieldPath: string | FieldPath, opStr: WhereFilterOp, value: unknown): QueryFieldFilterConstraint; // @public -export type WhereFilterOp = - | '<' - | '<=' - | '==' - | '!=' - | '>=' - | '>' - | 'array-contains' - | 'in' - | 'array-contains-any' - | 'not-in'; +export type WhereFilterOp = '<' | '<=' | '==' | '!=' | '>=' | '>' | 'array-contains' | 'in' | 'array-contains-any' | 'not-in'; // @public -export type WithFieldValue = - | T - | (T extends Primitive - ? T - : T extends {} - ? { - [K in keyof T]: WithFieldValue | FieldValue; - } - : never); +export type WithFieldValue = T | (T extends Primitive ? T : T extends {} ? { + [K in keyof T]: WithFieldValue | FieldValue; +} : never); // @public export class WriteBatch { - commit(): Promise; - delete(documentRef: DocumentReference): WriteBatch; - set( - documentRef: DocumentReference, - data: WithFieldValue - ): WriteBatch; - set( - documentRef: DocumentReference, - data: PartialWithFieldValue, - options: SetOptions - ): WriteBatch; - update(documentRef: DocumentReference, data: UpdateData): WriteBatch; - update( - documentRef: DocumentReference, - field: string | FieldPath, - value: unknown, - ...moreFieldsAndValues: unknown[] - ): WriteBatch; + commit(): Promise; + delete(documentRef: DocumentReference): WriteBatch; + set(documentRef: DocumentReference, data: WithFieldValue): WriteBatch; + set(documentRef: DocumentReference, data: PartialWithFieldValue, options: SetOptions): WriteBatch; + update(documentRef: DocumentReference, data: UpdateData): WriteBatch; + update(documentRef: DocumentReference, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): WriteBatch; } // @public export function writeBatch(firestore: Firestore): WriteBatch; + + ``` diff --git a/docs-devsite/firestore_.md b/docs-devsite/firestore_.md index 365635c434d..a7161309c8a 100644 --- a/docs-devsite/firestore_.md +++ b/docs-devsite/firestore_.md @@ -42,7 +42,7 @@ https://github.com/firebase/firebase-js-sdk | [deleteField()](./firestore_.md#deletefield) | Returns a sentinel for use with [updateDoc()](./firestore_lite.md#updatedoc) or [setDoc()](./firestore_lite.md#setdoc) with {merge: true} to mark a field for deletion. | | [documentId()](./firestore_.md#documentid) | Returns a special sentinel FieldPath to refer to the ID of a document. It can be used in queries to sort or filter by the document ID. | | [getFirestore()](./firestore_.md#getfirestore) | Returns the existing default [Firestore](./firestore_.firestore.md#firestore_class) instance that is associated with the default [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with default settings. | -| [memoryLocalCache()](./firestore_.md#memorylocalcache) | Creates an instance of MemoryLocalCache. The instance can be set to FirestoreSettings.cache to tell the SDK which cache layer to use. | +| [memoryEagerGarbageCollector()](./firestore_.md#memoryeagergarbagecollector) | Creates an instance of MemoryEagerGarbageCollector. This is also the default garbage collector unless it is explicitly specified otherwise. | | [persistentMultipleTabManager()](./firestore_.md#persistentmultipletabmanager) | Creates an instance of PersistentMultipleTabManager. | | [serverTimestamp()](./firestore_.md#servertimestamp) | Returns a sentinel used with [setDoc()](./firestore_lite.md#setdoc) or [updateDoc()](./firestore_lite.md#updatedoc) to include a server-generated timestamp in the written data. | | function(elements...) | @@ -101,6 +101,8 @@ https://github.com/firebase/firebase-js-sdk | [updateDoc(reference, data)](./firestore_.md#updatedoc) | Updates fields in the document referred to by the specified DocumentReference. The update will fail if applied to a document that does not exist. | | [updateDoc(reference, field, value, moreFieldsAndValues)](./firestore_.md#updatedoc) | Updates fields in the document referred to by the specified DocumentReference The update will fail if applied to a document that does not exist.Nested fields can be updated by providing dot-separated field path strings or by providing FieldPath objects. | | function(settings...) | +| [memoryLocalCache(settings)](./firestore_.md#memorylocalcache) | Creates an instance of MemoryLocalCache. The instance can be set to FirestoreSettings.cache to tell the SDK which cache layer to use. | +| [memoryLruGarbageCollector(settings)](./firestore_.md#memorylrugarbagecollector) | Creates an instance of MemoryLruGarbageCollector.A target size can be specified as part of the setting parameter. The collector will start deleting documents once the cache size exceeds the given size. The default cache size is 40MB (40 \* 1024 \* 1024 bytes). | | [persistentLocalCache(settings)](./firestore_.md#persistentlocalcache) | Creates an instance of PersistentLocalCache. The instance can be set to FirestoreSettings.cache to tell the SDK which cache layer to use. | | [persistentSingleTabManager(settings)](./firestore_.md#persistentsingletabmanager) | Creates an instance of PersistentSingleTabManager. | | function(snapshot...) | @@ -153,7 +155,10 @@ https://github.com/firebase/firebase-js-sdk | [IndexConfiguration](./firestore_.indexconfiguration.md#indexconfiguration_interface) | (BETA) A list of Firestore indexes to speed up local query execution.See [JSON Format](https://firebase.google.com/docs/reference/firestore/indexes/#json_format) for a description of the format of the index definition. | | [IndexField](./firestore_.indexfield.md#indexfield_interface) | (BETA) A single field element in an index configuration. | | [LoadBundleTaskProgress](./firestore_.loadbundletaskprogress.md#loadbundletaskprogress_interface) | Represents a progress update or a final state from loading bundles. | +| [MemoryCacheSettings](./firestore_.memorycachesettings.md#memorycachesettings_interface) | An settings object to configure an MemoryLocalCache instance. | +| [MemoryEagerGarbageCollector](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollector_interface) | A garbage collector deletes documents whenever they are not part of any active queries, and have no local mutations attached to them.This collector tries to ensure lowest memory footprints from the SDK, at the risk of querying backend repeated for a document it could have cached locally.Use factory function to create a instance of this collector. | | [MemoryLocalCache](./firestore_.memorylocalcache.md#memorylocalcache_interface) | Provides an in-memory cache to the SDK. This is the default cache unless explicitly configured otherwise.To use, create an instance using the factory function , then set the instance to FirestoreSettings.cache and call initializeFirestore using the settings object. | +| [MemoryLruGarbageCollector](./firestore_.memorylrugarbagecollector.md#memorylrugarbagecollector_interface) | A garbage collector deletes Least-Recently-Used documents in multiple batches.This collector is configured with a target size, and will only perform collection when the cached documents exceed the target size. It avoids querying backend repeated for the same query or document, at the risk of having a larger memory footprint.Use factory function to create a instance of this collector. | | [PersistenceSettings](./firestore_.persistencesettings.md#persistencesettings_interface) | Settings that can be passed to enableIndexedDbPersistence() to configure Firestore persistence. | | [PersistentCacheSettings](./firestore_.persistentcachesettings.md#persistentcachesettings_interface) | An settings object to configure an PersistentLocalCache instance. | | [PersistentLocalCache](./firestore_.persistentlocalcache.md#persistentlocalcache_interface) | Provides a persistent cache backed by IndexedDb to the SDK.To use, create an instance using the factory function , then set the instance to FirestoreSettings.cache and call initializeFirestore using the settings object. | @@ -182,6 +187,7 @@ https://github.com/firebase/firebase-js-sdk | [DocumentChangeType](./firestore_.md#documentchangetype) | The type of a DocumentChange may be 'added', 'removed', or 'modified'. | | [FirestoreErrorCode](./firestore_.md#firestoreerrorcode) | The set of Firestore status codes. The codes are the same at the ones exposed by gRPC here: https://github.com/grpc/grpc/blob/master/doc/statuscodes.mdPossible values: - 'cancelled': The operation was cancelled (typically by the caller). - 'unknown': Unknown error or an error from a different error domain. - 'invalid-argument': Client specified an invalid argument. Note that this differs from 'failed-precondition'. 'invalid-argument' indicates arguments that are problematic regardless of the state of the system (e.g. an invalid field name). - 'deadline-exceeded': Deadline expired before operation could complete. For operations that change the state of the system, this error may be returned even if the operation has completed successfully. For example, a successful response from a server could have been delayed long enough for the deadline to expire. - 'not-found': Some requested document was not found. - 'already-exists': Some document that we attempted to create already exists. - 'permission-denied': The caller does not have permission to execute the specified operation. - 'resource-exhausted': Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space. - 'failed-precondition': Operation was rejected because the system is not in a state required for the operation's execution. - 'aborted': The operation was aborted, typically due to a concurrency issue like transaction aborts, etc. - 'out-of-range': Operation was attempted past the valid range. - 'unimplemented': Operation is not implemented or not supported/enabled. - 'internal': Internal errors. Means some invariants expected by underlying system has been broken. If you see one of these errors, something is very broken. - 'unavailable': The service is currently unavailable. This is most likely a transient condition and may be corrected by retrying with a backoff. - 'data-loss': Unrecoverable data loss or corruption. - 'unauthenticated': The request does not have valid authentication credentials for the operation. | | [FirestoreLocalCache](./firestore_.md#firestorelocalcache) | Union type from all supported SDK cache layer. | +| [MemoryGarbageCollector](./firestore_.md#memorygarbagecollector) | Union type from all support gabage collectors for memory local cache. | | [NestedUpdateFields](./firestore_.md#nestedupdatefields) | For each field (e.g. 'bar'), find all nested keys (e.g. {'bar.baz': T1, 'bar.qux': T2}). Intersect them together to make a single map containing all possible keys that are all marked as optional | | [OrderByDirection](./firestore_.md#orderbydirection) | The direction of a [orderBy()](./firestore_.md#orderby) clause is specified as 'desc' or 'asc' (descending or ascending). | | [PartialWithFieldValue](./firestore_.md#partialwithfieldvalue) | Similar to Typescript's Partial<T>, but allows nested fields to be omitted and FieldValues to be passed in as property values. | @@ -807,18 +813,18 @@ export declare function getFirestore(): Firestore; The [Firestore](./firestore_.firestore.md#firestore_class) instance of the provided app. -## memoryLocalCache() +## memoryEagerGarbageCollector() -Creates an instance of `MemoryLocalCache`. The instance can be set to `FirestoreSettings.cache` to tell the SDK which cache layer to use. +Creates an instance of `MemoryEagerGarbageCollector`. This is also the default garbage collector unless it is explicitly specified otherwise. Signature: ```typescript -export declare function memoryLocalCache(): MemoryLocalCache; +export declare function memoryEagerGarbageCollector(): MemoryEagerGarbageCollector; ``` Returns: -[MemoryLocalCache](./firestore_.memorylocalcache.md#memorylocalcache_interface) +[MemoryEagerGarbageCollector](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollector_interface) ## persistentMultipleTabManager() @@ -1954,6 +1960,50 @@ Promise<void> A `Promise` resolved once the data has been successfully written to the backend (note that it won't resolve while you're offline). +## memoryLocalCache() + +Creates an instance of `MemoryLocalCache`. The instance can be set to `FirestoreSettings.cache` to tell the SDK which cache layer to use. + +Signature: + +```typescript +export declare function memoryLocalCache(settings?: MemoryCacheSettings): MemoryLocalCache; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| settings | [MemoryCacheSettings](./firestore_.memorycachesettings.md#memorycachesettings_interface) | | + +Returns: + +[MemoryLocalCache](./firestore_.memorylocalcache.md#memorylocalcache_interface) + +## memoryLruGarbageCollector() + +Creates an instance of `MemoryLruGarbageCollector`. + +A target size can be specified as part of the setting parameter. The collector will start deleting documents once the cache size exceeds the given size. The default cache size is 40MB (40 \* 1024 \* 1024 bytes). + +Signature: + +```typescript +export declare function memoryLruGarbageCollector(settings?: { + cacheSizeBytes?: number; +}): MemoryLruGarbageCollector; +``` + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| settings | { cacheSizeBytes?: number; } | | + +Returns: + +[MemoryLruGarbageCollector](./firestore_.memorylrugarbagecollector.md#memorylrugarbagecollector_interface) + ## persistentLocalCache() Creates an instance of `PersistentLocalCache`. The instance can be set to `FirestoreSettings.cache` to tell the SDK which cache layer to use. @@ -2172,6 +2222,16 @@ Union type from all supported SDK cache layer. export declare type FirestoreLocalCache = MemoryLocalCache | PersistentLocalCache; ``` +## MemoryGarbageCollector + +Union type from all support gabage collectors for memory local cache. + +Signature: + +```typescript +export declare type MemoryGarbageCollector = MemoryEagerGarbageCollector | MemoryLruGarbageCollector; +``` + ## NestedUpdateFields For each field (e.g. 'bar'), find all nested keys (e.g. {'bar.baz': T1, 'bar.qux': T2}). Intersect them together to make a single map containing all possible keys that are all marked as optional diff --git a/docs-devsite/firestore_.memorycachesettings.md b/docs-devsite/firestore_.memorycachesettings.md new file mode 100644 index 00000000000..69f46acdf7c --- /dev/null +++ b/docs-devsite/firestore_.memorycachesettings.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# MemoryCacheSettings interface +An settings object to configure an `MemoryLocalCache` instance. + +Signature: + +```typescript +export declare interface MemoryCacheSettings +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [garbageCollector](./firestore_.memorycachesettings.md#memorycachesettingsgarbagecollector) | [MemoryGarbageCollector](./firestore_.md#memorygarbagecollector) | The garbage collector to use, for the memory cache layer. A MemoryEagerGarbageCollector is used when this is undefined. | + +## MemoryCacheSettings.garbageCollector + +The garbage collector to use, for the memory cache layer. A `MemoryEagerGarbageCollector` is used when this is undefined. + +Signature: + +```typescript +garbageCollector?: MemoryGarbageCollector; +``` diff --git a/docs-devsite/firestore_.memoryeagergarbagecollector.md b/docs-devsite/firestore_.memoryeagergarbagecollector.md new file mode 100644 index 00000000000..fe6f86220fc --- /dev/null +++ b/docs-devsite/firestore_.memoryeagergarbagecollector.md @@ -0,0 +1,37 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# MemoryEagerGarbageCollector interface +A garbage collector deletes documents whenever they are not part of any active queries, and have no local mutations attached to them. + +This collector tries to ensure lowest memory footprints from the SDK, at the risk of querying backend repeated for a document it could have cached locally. + +Use factory function to create a instance of this collector. + +Signature: + +```typescript +export declare interface MemoryEagerGarbageCollector +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [kind](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollectorkind) | "memoryEager" | | + +## MemoryEagerGarbageCollector.kind + +Signature: + +```typescript +kind: "memoryEager"; +``` diff --git a/docs-devsite/firestore_.memorylocalcache.md b/docs-devsite/firestore_.memorylocalcache.md index 92b7d3a2c72..dd64730806c 100644 --- a/docs-devsite/firestore_.memorylocalcache.md +++ b/docs-devsite/firestore_.memorylocalcache.md @@ -24,12 +24,12 @@ export declare interface MemoryLocalCache | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.memorylocalcache.md#memorylocalcachekind) | 'memory' | | +| [kind](./firestore_.memorylocalcache.md#memorylocalcachekind) | "memory" | | ## MemoryLocalCache.kind Signature: ```typescript -kind: 'memory'; +kind: "memory"; ``` diff --git a/docs-devsite/firestore_.memorylrugarbagecollector.md b/docs-devsite/firestore_.memorylrugarbagecollector.md new file mode 100644 index 00000000000..8a8d51f5682 --- /dev/null +++ b/docs-devsite/firestore_.memorylrugarbagecollector.md @@ -0,0 +1,37 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# MemoryLruGarbageCollector interface +A garbage collector deletes Least-Recently-Used documents in multiple batches. + +This collector is configured with a target size, and will only perform collection when the cached documents exceed the target size. It avoids querying backend repeated for the same query or document, at the risk of having a larger memory footprint. + +Use factory function to create a instance of this collector. + +Signature: + +```typescript +export declare interface MemoryLruGarbageCollector +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [kind](./firestore_.memorylrugarbagecollector.md#memorylrugarbagecollectorkind) | "memoryLru" | | + +## MemoryLruGarbageCollector.kind + +Signature: + +```typescript +kind: "memoryLru"; +``` diff --git a/docs-devsite/firestore_.persistentlocalcache.md b/docs-devsite/firestore_.persistentlocalcache.md index 48d876d15bd..48a80eba342 100644 --- a/docs-devsite/firestore_.persistentlocalcache.md +++ b/docs-devsite/firestore_.persistentlocalcache.md @@ -24,12 +24,12 @@ export declare interface PersistentLocalCache | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.persistentlocalcache.md#persistentlocalcachekind) | 'persistent' | | +| [kind](./firestore_.persistentlocalcache.md#persistentlocalcachekind) | "persistent" | | ## PersistentLocalCache.kind Signature: ```typescript -kind: 'persistent'; +kind: "persistent"; ``` diff --git a/docs-devsite/firestore_.persistentmultipletabmanager.md b/docs-devsite/firestore_.persistentmultipletabmanager.md index b0c21b378a1..9674590c2d4 100644 --- a/docs-devsite/firestore_.persistentmultipletabmanager.md +++ b/docs-devsite/firestore_.persistentmultipletabmanager.md @@ -22,12 +22,12 @@ export declare interface PersistentMultipleTabManager | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.persistentmultipletabmanager.md#persistentmultipletabmanagerkind) | 'PersistentMultipleTab' | | +| [kind](./firestore_.persistentmultipletabmanager.md#persistentmultipletabmanagerkind) | "PersistentMultipleTab" | | ## PersistentMultipleTabManager.kind Signature: ```typescript -kind: 'PersistentMultipleTab'; +kind: "PersistentMultipleTab"; ``` diff --git a/docs-devsite/firestore_.persistentsingletabmanager.md b/docs-devsite/firestore_.persistentsingletabmanager.md index ee130b6fc6a..dd9e526cd42 100644 --- a/docs-devsite/firestore_.persistentsingletabmanager.md +++ b/docs-devsite/firestore_.persistentsingletabmanager.md @@ -22,12 +22,12 @@ export declare interface PersistentSingleTabManager | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.persistentsingletabmanager.md#persistentsingletabmanagerkind) | 'persistentSingleTab' | | +| [kind](./firestore_.persistentsingletabmanager.md#persistentsingletabmanagerkind) | "persistentSingleTab" | | ## PersistentSingleTabManager.kind Signature: ```typescript -kind: 'persistentSingleTab'; +kind: "persistentSingleTab"; ``` diff --git a/packages/auth/api-extractor.json b/packages/auth/api-extractor.json index bd0dda5cca2..06ff1e940cf 100644 --- a/packages/auth/api-extractor.json +++ b/packages/auth/api-extractor.json @@ -1,6 +1,6 @@ { "extends": "../../config/api-extractor.json", - "mainEntryPointFilePath": "/dist/esm5/index.d.ts", + "mainEntryPointFilePath": "/dist/esm5/index.doc.d.ts", "dtsRollup": { "enabled": true, "untrimmedFilePath": "/dist/.d.ts", diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index d62f4ec0697..7bcb80cd563 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -52,6 +52,7 @@ export { PersistentSingleTabManager, persistentSingleTabManager, PersistentSingleTabManagerSettings, + PersistentTabManager, } from "./api/cache_config"; export { documentId, FieldPath } from "./api/field_path"; diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index fa28bcde7d1..9657a71ac5b 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -163,11 +163,11 @@ export function withEnsuredLruGcTestDb( ): Promise { const newSettings = { ...DEFAULT_SETTINGS }; if (persistence) { - newSettings.cache = indexedDbLocalCache({ + newSettings.localCache = persistentLocalCache({ cacheSizeBytes: 1 * 1024 * 1024, }); } else { - newSettings.cache = memoryLocalCache({ + newSettings.localCache = memoryLocalCache({ garbageCollector: memoryLruGarbageCollector({ cacheSizeBytes: 1 * 1024 * 1024, }), From f05a45ea936f25ba10c0b757b0909324bbd642b7 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 17 Mar 2023 11:43:34 -0400 Subject: [PATCH 30/39] lint and prettier --- common/api-review/firestore.api.md | 12 +- .../firestore_.memoryeagergarbagecollector.md | 4 +- docs-devsite/firestore_.memorylocalcache.md | 4 +- .../firestore_.memorylrugarbagecollector.md | 4 +- .../firestore_.persistentlocalcache.md | 4 +- ...firestore_.persistentmultipletabmanager.md | 4 +- .../firestore_.persistentsingletabmanager.md | 4 +- packages/firestore/src/api.ts | 100 +++--- packages/firestore/src/api/cache_config.ts | 48 +-- .../firestore/src/core/firestore_client.ts | 294 +++++++++--------- .../test/integration/util/helpers.ts | 96 +++--- 11 files changed, 287 insertions(+), 287 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index cc459698fb9..53e8e60cab8 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -339,7 +339,7 @@ export interface MemoryCacheSettings { // @public export interface MemoryEagerGarbageCollector { // (undocumented) - kind: "memoryEager"; + kind: 'memoryEager'; } // @public @@ -351,7 +351,7 @@ export type MemoryGarbageCollector = MemoryEagerGarbageCollector | MemoryLruGarb // @public export interface MemoryLocalCache { // (undocumented) - kind: "memory"; + kind: 'memory'; } // @public @@ -360,7 +360,7 @@ export function memoryLocalCache(settings?: MemoryCacheSettings): MemoryLocalCac // @public export interface MemoryLruGarbageCollector { // (undocumented) - kind: "memoryLru"; + kind: 'memoryLru'; } // @public @@ -454,7 +454,7 @@ export interface PersistentCacheSettings { // @public export interface PersistentLocalCache { // (undocumented) - kind: "persistent"; + kind: 'persistent'; } // @public @@ -463,7 +463,7 @@ export function persistentLocalCache(settings?: PersistentCacheSettings): Persis // @public export interface PersistentMultipleTabManager { // (undocumented) - kind: "PersistentMultipleTab"; + kind: 'PersistentMultipleTab'; } // @public @@ -472,7 +472,7 @@ export function persistentMultipleTabManager(): PersistentMultipleTabManager; // @public export interface PersistentSingleTabManager { // (undocumented) - kind: "persistentSingleTab"; + kind: 'persistentSingleTab'; } // @public diff --git a/docs-devsite/firestore_.memoryeagergarbagecollector.md b/docs-devsite/firestore_.memoryeagergarbagecollector.md index fe6f86220fc..f373ced2dcb 100644 --- a/docs-devsite/firestore_.memoryeagergarbagecollector.md +++ b/docs-devsite/firestore_.memoryeagergarbagecollector.md @@ -26,12 +26,12 @@ export declare interface MemoryEagerGarbageCollector | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollectorkind) | "memoryEager" | | +| [kind](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollectorkind) | 'memoryEager' | | ## MemoryEagerGarbageCollector.kind Signature: ```typescript -kind: "memoryEager"; +kind: 'memoryEager'; ``` diff --git a/docs-devsite/firestore_.memorylocalcache.md b/docs-devsite/firestore_.memorylocalcache.md index dd64730806c..92b7d3a2c72 100644 --- a/docs-devsite/firestore_.memorylocalcache.md +++ b/docs-devsite/firestore_.memorylocalcache.md @@ -24,12 +24,12 @@ export declare interface MemoryLocalCache | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.memorylocalcache.md#memorylocalcachekind) | "memory" | | +| [kind](./firestore_.memorylocalcache.md#memorylocalcachekind) | 'memory' | | ## MemoryLocalCache.kind Signature: ```typescript -kind: "memory"; +kind: 'memory'; ``` diff --git a/docs-devsite/firestore_.memorylrugarbagecollector.md b/docs-devsite/firestore_.memorylrugarbagecollector.md index 8a8d51f5682..6e15513934a 100644 --- a/docs-devsite/firestore_.memorylrugarbagecollector.md +++ b/docs-devsite/firestore_.memorylrugarbagecollector.md @@ -26,12 +26,12 @@ export declare interface MemoryLruGarbageCollector | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.memorylrugarbagecollector.md#memorylrugarbagecollectorkind) | "memoryLru" | | +| [kind](./firestore_.memorylrugarbagecollector.md#memorylrugarbagecollectorkind) | 'memoryLru' | | ## MemoryLruGarbageCollector.kind Signature: ```typescript -kind: "memoryLru"; +kind: 'memoryLru'; ``` diff --git a/docs-devsite/firestore_.persistentlocalcache.md b/docs-devsite/firestore_.persistentlocalcache.md index 48a80eba342..48d876d15bd 100644 --- a/docs-devsite/firestore_.persistentlocalcache.md +++ b/docs-devsite/firestore_.persistentlocalcache.md @@ -24,12 +24,12 @@ export declare interface PersistentLocalCache | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.persistentlocalcache.md#persistentlocalcachekind) | "persistent" | | +| [kind](./firestore_.persistentlocalcache.md#persistentlocalcachekind) | 'persistent' | | ## PersistentLocalCache.kind Signature: ```typescript -kind: "persistent"; +kind: 'persistent'; ``` diff --git a/docs-devsite/firestore_.persistentmultipletabmanager.md b/docs-devsite/firestore_.persistentmultipletabmanager.md index 9674590c2d4..b0c21b378a1 100644 --- a/docs-devsite/firestore_.persistentmultipletabmanager.md +++ b/docs-devsite/firestore_.persistentmultipletabmanager.md @@ -22,12 +22,12 @@ export declare interface PersistentMultipleTabManager | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.persistentmultipletabmanager.md#persistentmultipletabmanagerkind) | "PersistentMultipleTab" | | +| [kind](./firestore_.persistentmultipletabmanager.md#persistentmultipletabmanagerkind) | 'PersistentMultipleTab' | | ## PersistentMultipleTabManager.kind Signature: ```typescript -kind: "PersistentMultipleTab"; +kind: 'PersistentMultipleTab'; ``` diff --git a/docs-devsite/firestore_.persistentsingletabmanager.md b/docs-devsite/firestore_.persistentsingletabmanager.md index dd9e526cd42..ee130b6fc6a 100644 --- a/docs-devsite/firestore_.persistentsingletabmanager.md +++ b/docs-devsite/firestore_.persistentsingletabmanager.md @@ -22,12 +22,12 @@ export declare interface PersistentSingleTabManager | Property | Type | Description | | --- | --- | --- | -| [kind](./firestore_.persistentsingletabmanager.md#persistentsingletabmanagerkind) | "persistentSingleTab" | | +| [kind](./firestore_.persistentsingletabmanager.md#persistentsingletabmanagerkind) | 'persistentSingleTab' | | ## PersistentSingleTabManager.kind Signature: ```typescript -kind: "persistentSingleTab"; +kind: 'persistentSingleTab'; ``` diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 7bcb80cd563..4ecfae7f0ad 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -22,8 +22,8 @@ export { count, getAggregateFromServer, getCountFromServer, - sum, -} from "./api/aggregate"; + sum +} from './api/aggregate'; export { AggregateField, @@ -31,8 +31,8 @@ export { AggregateQuerySnapshot, AggregateSpec, AggregateSpecData, - AggregateType, -} from "./lite-api/aggregate_types"; + AggregateType +} from './lite-api/aggregate_types'; export { FirestoreLocalCache, @@ -52,10 +52,10 @@ export { PersistentSingleTabManager, persistentSingleTabManager, PersistentSingleTabManagerSettings, - PersistentTabManager, -} from "./api/cache_config"; + PersistentTabManager +} from './api/cache_config'; -export { documentId, FieldPath } from "./api/field_path"; +export { documentId, FieldPath } from './api/field_path'; export { clearIndexedDbPersistence, @@ -72,16 +72,16 @@ export { loadBundle, namedQuery, terminate, - waitForPendingWrites, -} from "./api/database"; + waitForPendingWrites +} from './api/database'; export { LoadBundleTask, LoadBundleTaskProgress, - TaskState, -} from "./api/bundle"; + TaskState +} from './api/bundle'; -export { FirestoreSettings, PersistenceSettings } from "./api/settings"; +export { FirestoreSettings, PersistenceSettings } from './api/settings'; export { DocumentChange, @@ -92,8 +92,8 @@ export { QuerySnapshot, snapshotEqual, SnapshotMetadata, - SnapshotOptions, -} from "./api/snapshot"; + SnapshotOptions +} from './api/snapshot'; export { collection, @@ -108,8 +108,8 @@ export { refEqual, SetOptions, UpdateData, - WithFieldValue, -} from "./api/reference"; + WithFieldValue +} from './api/reference'; export { and, @@ -134,14 +134,14 @@ export { startAfter, startAt, where, - WhereFilterOp, -} from "./api/filter"; + WhereFilterOp +} from './api/filter'; -export { SnapshotListenOptions, Unsubscribe } from "./api/reference_impl"; +export { SnapshotListenOptions, Unsubscribe } from './api/reference_impl'; -export { TransactionOptions } from "./api/transaction_options"; +export { TransactionOptions } from './api/transaction_options'; -export { runTransaction, Transaction } from "./api/transaction"; +export { runTransaction, Transaction } from './api/transaction'; export { addDoc, @@ -156,64 +156,64 @@ export { onSnapshot, onSnapshotsInSync, setDoc, - updateDoc, -} from "./api/reference_impl"; + updateDoc +} from './api/reference_impl'; -export { FieldValue } from "./api/field_value"; +export { FieldValue } from './api/field_value'; export { arrayRemove, arrayUnion, deleteField, increment, - serverTimestamp, -} from "./api/field_value_impl"; + serverTimestamp +} from './api/field_value_impl'; -export { LogLevelString as LogLevel, setLogLevel } from "./util/log"; +export { LogLevelString as LogLevel, setLogLevel } from './util/log'; -export { Bytes } from "./api/bytes"; +export { Bytes } from './api/bytes'; -export { WriteBatch, writeBatch } from "./api/write_batch"; +export { WriteBatch, writeBatch } from './api/write_batch'; -export { GeoPoint } from "./api/geo_point"; +export { GeoPoint } from './api/geo_point'; -export { Timestamp } from "./api/timestamp"; +export { Timestamp } from './api/timestamp'; -export { CACHE_SIZE_UNLIMITED } from "./api/database"; +export { CACHE_SIZE_UNLIMITED } from './api/database'; -export { FirestoreError, FirestoreErrorCode } from "./util/error"; +export { FirestoreError, FirestoreErrorCode } from './util/error'; -export { AbstractUserDataWriter } from "./lite-api/user_data_writer"; +export { AbstractUserDataWriter } from './lite-api/user_data_writer'; export { AddPrefixToKeys, ChildUpdateFields, NestedUpdateFields, Primitive, - UnionToIntersection, -} from "../src/lite-api/types"; + UnionToIntersection +} from '../src/lite-api/types'; export { Index, IndexConfiguration, IndexField, - setIndexConfiguration, -} from "./api/index_configuration"; + setIndexConfiguration +} from './api/index_configuration'; /** * Internal exports */ -export { isBase64Available as _isBase64Available } from "./platform/base64"; -export { DatabaseId as _DatabaseId } from "./core/database_info"; +export { isBase64Available as _isBase64Available } from './platform/base64'; +export { DatabaseId as _DatabaseId } from './core/database_info'; export { cast as _cast, - validateIsNotUsedTogether as _validateIsNotUsedTogether, -} from "./util/input_validation"; -export { DocumentKey as _DocumentKey } from "./model/document_key"; -export { debugAssert as _debugAssert } from "./util/assert"; -export { FieldPath as _FieldPath } from "./model/path"; -export type { ResourcePath as _ResourcePath } from "./model/path"; -export type { ByteString as _ByteString } from "./util/byte_string"; -export { logWarn as _logWarn } from "./util/log"; -export { EmptyAuthCredentialsProvider as _EmptyAuthCredentialsProvider } from "./api/credentials"; -export { EmptyAppCheckTokenProvider as _EmptyAppCheckTokenProvider } from "./api/credentials"; + validateIsNotUsedTogether as _validateIsNotUsedTogether +} from './util/input_validation'; +export { DocumentKey as _DocumentKey } from './model/document_key'; +export { debugAssert as _debugAssert } from './util/assert'; +export { FieldPath as _FieldPath } from './model/path'; +export type { ResourcePath as _ResourcePath } from './model/path'; +export type { ByteString as _ByteString } from './util/byte_string'; +export { logWarn as _logWarn } from './util/log'; +export { EmptyAuthCredentialsProvider as _EmptyAuthCredentialsProvider } from './api/credentials'; +export { EmptyAppCheckTokenProvider as _EmptyAppCheckTokenProvider } from './api/credentials'; diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 8e1ffab6b13..d693881d95c 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -21,8 +21,8 @@ import { MemoryOfflineComponentProvider, MultiTabOfflineComponentProvider, OfflineComponentProvider, - OnlineComponentProvider, -} from "../core/component_provider"; + OnlineComponentProvider +} from '../core/component_provider'; /* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */ /** @@ -34,7 +34,7 @@ import { * the settings object. */ export type MemoryLocalCache = { - kind: "memory"; + kind: 'memory'; /** * @internal */ @@ -46,7 +46,7 @@ export type MemoryLocalCache = { }; class MemoryLocalCacheImpl implements MemoryLocalCache { - kind: "memory" = "memory"; + kind: 'memory' = 'memory'; /** * @internal */ @@ -79,7 +79,7 @@ class MemoryLocalCacheImpl implements MemoryLocalCache { * the settings object. */ export type PersistentLocalCache = { - kind: "persistent"; + kind: 'persistent'; /** * @internal */ @@ -91,7 +91,7 @@ export type PersistentLocalCache = { }; class PersistentLocalCacheImpl implements PersistentLocalCache { - kind: "persistent" = "persistent"; + kind: 'persistent' = 'persistent'; /** * @internal */ @@ -143,7 +143,7 @@ export type MemoryGarbageCollector = * instance of this collector. */ export type MemoryEagerGarbageCollector = { - kind: "memoryEager"; + kind: 'memoryEager'; /** * @internal */ @@ -163,7 +163,7 @@ export type MemoryEagerGarbageCollector = { * instance of this collector. */ export type MemoryLruGarbageCollector = { - kind: "memoryLru"; + kind: 'memoryLru'; /** * @internal */ @@ -171,7 +171,7 @@ export type MemoryLruGarbageCollector = { }; class MemoryEagerGabageCollectorImpl implements MemoryEagerGarbageCollector { - kind: "memoryEager" = "memoryEager"; + kind: 'memoryEager' = 'memoryEager'; /** * @internal */ @@ -187,7 +187,7 @@ class MemoryEagerGabageCollectorImpl implements MemoryEagerGarbageCollector { } class MemoryLruGabageCollectorImpl implements MemoryLruGarbageCollector { - kind: "memoryLru" = "memoryLru"; + kind: 'memoryLru' = 'memoryLru'; /** * @internal */ @@ -195,7 +195,7 @@ class MemoryLruGabageCollectorImpl implements MemoryLruGarbageCollector { constructor(cacheSize?: number) { this._offlineComponentProvider = new LruGcMemoryOfflineComponentProvider( - cacheSize, + cacheSize ); } @@ -241,7 +241,7 @@ export type MemoryCacheSettings = { * `FirestoreSettings.cache` to tell the SDK which cache layer to use. */ export function memoryLocalCache( - settings?: MemoryCacheSettings, + settings?: MemoryCacheSettings ): MemoryLocalCache { return new MemoryLocalCacheImpl(settings); } @@ -273,7 +273,7 @@ export type PersistentCacheSettings = { * `FirestoreSettings.cache` to tell the SDK which cache layer to use. */ export function persistentLocalCache( - settings?: PersistentCacheSettings, + settings?: PersistentCacheSettings ): PersistentLocalCache { return new PersistentLocalCacheImpl(settings); } @@ -283,12 +283,12 @@ export function persistentLocalCache( * performed across tabs. */ export type PersistentSingleTabManager = { - kind: "persistentSingleTab"; + kind: 'persistentSingleTab'; /** * @internal */ _initialize: ( - settings: Omit | undefined, + settings: Omit | undefined ) => void; /** * @internal @@ -301,7 +301,7 @@ export type PersistentSingleTabManager = { }; class SingleTabManagerImpl implements PersistentSingleTabManager { - kind: "persistentSingleTab" = "persistentSingleTab"; + kind: 'persistentSingleTab' = 'persistentSingleTab'; /** * @internal @@ -322,13 +322,13 @@ class SingleTabManagerImpl implements PersistentSingleTabManager { * @internal */ _initialize( - settings: Omit | undefined, + settings: Omit | undefined ): void { this._onlineComponentProvider = new OnlineComponentProvider(); this._offlineComponentProvider = new IndexedDbOfflineComponentProvider( this._onlineComponentProvider, settings?.cacheSizeBytes, - this.forceOwnership, + this.forceOwnership ); } } @@ -338,11 +338,11 @@ class SingleTabManagerImpl implements PersistentSingleTabManager { * mutations done across all tabs using the SDK. */ export type PersistentMultipleTabManager = { - kind: "PersistentMultipleTab"; + kind: 'PersistentMultipleTab'; /** * @internal */ - _initialize: (settings: Omit) => void; + _initialize: (settings: Omit) => void; /** * @internal */ @@ -355,7 +355,7 @@ export type PersistentMultipleTabManager = { }; class MultiTabManagerImpl implements PersistentMultipleTabManager { - kind: "PersistentMultipleTab" = "PersistentMultipleTab"; + kind: 'PersistentMultipleTab' = 'PersistentMultipleTab'; /** * @internal @@ -374,12 +374,12 @@ class MultiTabManagerImpl implements PersistentMultipleTabManager { * @internal */ _initialize( - settings: Omit | undefined, + settings: Omit | undefined ): void { this._onlineComponentProvider = new OnlineComponentProvider(); this._offlineComponentProvider = new MultiTabOfflineComponentProvider( this._onlineComponentProvider, - settings?.cacheSizeBytes, + settings?.cacheSizeBytes ); } } @@ -409,7 +409,7 @@ export type PersistentSingleTabManagerSettings = { * @param settings Configures the created tab manager. */ export function persistentSingleTabManager( - settings: PersistentSingleTabManagerSettings | undefined, + settings: PersistentSingleTabManagerSettings | undefined ): PersistentSingleTabManager { return new SingleTabManagerImpl(settings?.forceOwnership); } diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index f947485c1ea..96348ec394d 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -15,59 +15,59 @@ * limitations under the License. */ -import { GetOptions } from "@firebase/firestore-types"; +import { GetOptions } from '@firebase/firestore-types'; -import { LoadBundleTask } from "../api/bundle"; +import { LoadBundleTask } from '../api/bundle'; import { CredentialChangeListener, - CredentialsProvider, -} from "../api/credentials"; -import { User } from "../auth/user"; -import { LocalStore } from "../local/local_store"; + CredentialsProvider +} from '../api/credentials'; +import { User } from '../auth/user'; +import { LocalStore } from '../local/local_store'; import { localStoreConfigureFieldIndexes, localStoreExecuteQuery, localStoreGetNamedQuery, localStoreHandleUserChange, - localStoreReadDocument, -} from "../local/local_store_impl"; -import { Persistence } from "../local/persistence"; -import { Document } from "../model/document"; -import { DocumentKey } from "../model/document_key"; -import { FieldIndex } from "../model/field_index"; -import { Mutation } from "../model/mutation"; -import { ObjectValue } from "../model/object_value"; -import { toByteStreamReader } from "../platform/byte_stream_reader"; -import { newSerializer } from "../platform/serializer"; -import { newTextEncoder } from "../platform/text_serializer"; -import { Datastore, invokeRunAggregationQueryRpc } from "../remote/datastore"; + localStoreReadDocument +} from '../local/local_store_impl'; +import { Persistence } from '../local/persistence'; +import { Document } from '../model/document'; +import { DocumentKey } from '../model/document_key'; +import { FieldIndex } from '../model/field_index'; +import { Mutation } from '../model/mutation'; +import { ObjectValue } from '../model/object_value'; +import { toByteStreamReader } from '../platform/byte_stream_reader'; +import { newSerializer } from '../platform/serializer'; +import { newTextEncoder } from '../platform/text_serializer'; +import { Datastore, invokeRunAggregationQueryRpc } from '../remote/datastore'; import { canUseNetwork, RemoteStore, remoteStoreDisableNetwork, remoteStoreEnableNetwork, - remoteStoreHandleCredentialChange, -} from "../remote/remote_store"; -import { JsonProtoSerializer } from "../remote/serializer"; -import { debugAssert } from "../util/assert"; -import { AsyncObserver } from "../util/async_observer"; -import { AsyncQueue, wrapInUserErrorIfRecoverable } from "../util/async_queue"; -import { BundleReader } from "../util/bundle_reader"; -import { newBundleReader } from "../util/bundle_reader_impl"; -import { Code, FirestoreError } from "../util/error"; -import { logDebug, logWarn } from "../util/log"; -import { AutoId } from "../util/misc"; -import { Deferred } from "../util/promise"; - -import { Aggregate } from "./aggregate"; -import { NamedQuery } from "./bundle"; + remoteStoreHandleCredentialChange +} from '../remote/remote_store'; +import { JsonProtoSerializer } from '../remote/serializer'; +import { debugAssert } from '../util/assert'; +import { AsyncObserver } from '../util/async_observer'; +import { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue'; +import { BundleReader } from '../util/bundle_reader'; +import { newBundleReader } from '../util/bundle_reader_impl'; +import { Code, FirestoreError } from '../util/error'; +import { logDebug, logWarn } from '../util/log'; +import { AutoId } from '../util/misc'; +import { Deferred } from '../util/promise'; + +import { Aggregate } from './aggregate'; +import { NamedQuery } from './bundle'; import { ComponentConfiguration, MemoryOfflineComponentProvider, OfflineComponentProvider, - OnlineComponentProvider, -} from "./component_provider"; -import { DatabaseId, DatabaseInfo } from "./database_info"; + OnlineComponentProvider +} from './component_provider'; +import { DatabaseId, DatabaseInfo } from './database_info'; import { addSnapshotsInSyncListener, EventManager, @@ -76,24 +76,24 @@ import { ListenOptions, Observer, QueryListener, - removeSnapshotsInSyncListener, -} from "./event_manager"; -import { newQueryForPath, Query } from "./query"; -import { SyncEngine } from "./sync_engine"; + removeSnapshotsInSyncListener +} from './event_manager'; +import { newQueryForPath, Query } from './query'; +import { SyncEngine } from './sync_engine'; import { syncEngineListen, syncEngineLoadBundle, syncEngineRegisterPendingWritesCallback, syncEngineUnlisten, - syncEngineWrite, -} from "./sync_engine_impl"; -import { Transaction } from "./transaction"; -import { TransactionOptions } from "./transaction_options"; -import { TransactionRunner } from "./transaction_runner"; -import { View } from "./view"; -import { ViewSnapshot } from "./view_snapshot"; - -const LOG_TAG = "FirestoreClient"; + syncEngineWrite +} from './sync_engine_impl'; +import { Transaction } from './transaction'; +import { TransactionOptions } from './transaction_options'; +import { TransactionRunner } from './transaction_runner'; +import { View } from './view'; +import { ViewSnapshot } from './view_snapshot'; + +const LOG_TAG = 'FirestoreClient'; export const MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100; /** DOMException error code constants. */ @@ -113,11 +113,11 @@ export class FirestoreClient { Promise.resolve(); private appCheckCredentialListener: ( appCheckToken: string, - user: User, + user: User ) => Promise = () => Promise.resolve(); _uninitializedComponentsProvider?: { _offline: OfflineComponentProvider; - _offlineKind: "memory" | "persistent"; + _offlineKind: 'memory' | 'persistent'; _online: OnlineComponentProvider; }; @@ -136,15 +136,15 @@ export class FirestoreClient { * an async I/O to complete). // */ public asyncQueue: AsyncQueue, - private databaseInfo: DatabaseInfo, + private databaseInfo: DatabaseInfo ) { - this.authCredentials.start(asyncQueue, async (user) => { - logDebug(LOG_TAG, "Received user=", user.uid); + this.authCredentials.start(asyncQueue, async user => { + logDebug(LOG_TAG, 'Received user=', user.uid); await this.authCredentialListener(user); this.user = user; }); - this.appCheckCredentials.start(asyncQueue, (newAppCheckToken) => { - logDebug(LOG_TAG, "Received new app check token=", newAppCheckToken); + this.appCheckCredentials.start(asyncQueue, newAppCheckToken => { + logDebug(LOG_TAG, 'Received new app check token=', newAppCheckToken); return this.appCheckCredentialListener(newAppCheckToken, this.user); }); } @@ -157,7 +157,7 @@ export class FirestoreClient { authCredentials: this.authCredentials, appCheckCredentials: this.appCheckCredentials, initialUser: this.user, - maxConcurrentLimboResolutions: MAX_CONCURRENT_LIMBO_RESOLUTIONS, + maxConcurrentLimboResolutions: MAX_CONCURRENT_LIMBO_RESOLUTIONS }; } @@ -166,7 +166,7 @@ export class FirestoreClient { } setAppCheckTokenChangeListener( - listener: (appCheckToken: string, user: User) => Promise, + listener: (appCheckToken: string, user: User) => Promise ): void { this.appCheckCredentialListener = listener; } @@ -179,7 +179,7 @@ export class FirestoreClient { if (this.asyncQueue.isShuttingDown) { throw new FirestoreError( Code.FAILED_PRECONDITION, - "The client has already been terminated.", + 'The client has already been terminated.' ); } } @@ -205,7 +205,7 @@ export class FirestoreClient { } catch (e) { const firestoreError = wrapInUserErrorIfRecoverable( e as Error, - `Failed to shutdown persistence`, + `Failed to shutdown persistence` ); deferred.reject(firestoreError); } @@ -216,20 +216,20 @@ export class FirestoreClient { export async function setOfflineComponentProvider( client: FirestoreClient, - offlineComponentProvider: OfflineComponentProvider, + offlineComponentProvider: OfflineComponentProvider ): Promise { client.asyncQueue.verifyOperationInProgress(); - logDebug(LOG_TAG, "Initializing OfflineComponentProvider"); + logDebug(LOG_TAG, 'Initializing OfflineComponentProvider'); const configuration = await client.getConfiguration(); await offlineComponentProvider.initialize(configuration); let currentUser = configuration.initialUser; - client.setCredentialChangeListener(async (user) => { + client.setCredentialChangeListener(async user => { if (!currentUser.isEqual(user)) { await localStoreHandleUserChange( offlineComponentProvider.localStore, - user, + user ); currentUser = user; } @@ -246,21 +246,21 @@ export async function setOfflineComponentProvider( export async function setOnlineComponentProvider( client: FirestoreClient, - onlineComponentProvider: OnlineComponentProvider, + onlineComponentProvider: OnlineComponentProvider ): Promise { client.asyncQueue.verifyOperationInProgress(); const offlineComponentProvider = await ensureOfflineComponents(client); - logDebug(LOG_TAG, "Initializing OnlineComponentProvider"); + logDebug(LOG_TAG, 'Initializing OnlineComponentProvider'); const configuration = await client.getConfiguration(); await onlineComponentProvider.initialize( offlineComponentProvider, - configuration, + configuration ); // The CredentialChangeListener of the online component provider takes // precedence over the offline component provider. - client.setCredentialChangeListener((user) => + client.setCredentialChangeListener(user => remoteStoreHandleCredentialChange(onlineComponentProvider.remoteStore, user) ); client.setAppCheckTokenChangeListener((_, user) => @@ -274,15 +274,15 @@ export async function setOnlineComponentProvider( * persistence (as opposed to crashing the client). */ export function canFallbackFromIndexedDbError( - error: FirestoreError | DOMException, + error: FirestoreError | DOMException ): boolean { - if (error.name === "FirebaseError") { + if (error.name === 'FirebaseError') { return ( error.code === Code.FAILED_PRECONDITION || error.code === Code.UNIMPLEMENTED ); } else if ( - typeof DOMException !== "undefined" && + typeof DOMException !== 'undefined' && error instanceof DOMException ) { // There are a few known circumstances where we can open IndexedDb but @@ -308,15 +308,15 @@ export function canFallbackFromIndexedDbError( } async function ensureOfflineComponents( - client: FirestoreClient, + client: FirestoreClient ): Promise { if (!client._offlineComponents) { if (client._uninitializedComponentsProvider) { - logDebug(LOG_TAG, "Using user provided OfflineComponentProvider"); + logDebug(LOG_TAG, 'Using user provided OfflineComponentProvider'); try { await setOfflineComponentProvider( client, - client._uninitializedComponentsProvider._offline, + client._uninitializedComponentsProvider._offline ); } catch (e) { const error = e as FirestoreError | DOMException; @@ -324,20 +324,20 @@ async function ensureOfflineComponents( throw error; } logWarn( - "Error using user provided cache. Falling back to " + - "memory cache: " + - error, + 'Error using user provided cache. Falling back to ' + + 'memory cache: ' + + error ); await setOfflineComponentProvider( client, - new MemoryOfflineComponentProvider(), + new MemoryOfflineComponentProvider() ); } } else { - logDebug(LOG_TAG, "Using default OfflineComponentProvider"); + logDebug(LOG_TAG, 'Using default OfflineComponentProvider'); await setOfflineComponentProvider( client, - new MemoryOfflineComponentProvider(), + new MemoryOfflineComponentProvider() ); } } @@ -346,17 +346,17 @@ async function ensureOfflineComponents( } async function ensureOnlineComponents( - client: FirestoreClient, + client: FirestoreClient ): Promise { if (!client._onlineComponents) { if (client._uninitializedComponentsProvider) { - logDebug(LOG_TAG, "Using user provided OnlineComponentProvider"); + logDebug(LOG_TAG, 'Using user provided OnlineComponentProvider'); await setOnlineComponentProvider( client, - client._uninitializedComponentsProvider._online, + client._uninitializedComponentsProvider._online ); } else { - logDebug(LOG_TAG, "Using default OnlineComponentProvider"); + logDebug(LOG_TAG, 'Using default OnlineComponentProvider'); await setOnlineComponentProvider(client, new OnlineComponentProvider()); } } @@ -365,44 +365,44 @@ async function ensureOnlineComponents( } function getPersistence(client: FirestoreClient): Promise { - return ensureOfflineComponents(client).then((c) => c.persistence); + return ensureOfflineComponents(client).then(c => c.persistence); } export function getLocalStore(client: FirestoreClient): Promise { - return ensureOfflineComponents(client).then((c) => c.localStore); + return ensureOfflineComponents(client).then(c => c.localStore); } function getRemoteStore(client: FirestoreClient): Promise { - return ensureOnlineComponents(client).then((c) => c.remoteStore); + return ensureOnlineComponents(client).then(c => c.remoteStore); } export function getSyncEngine(client: FirestoreClient): Promise { - return ensureOnlineComponents(client).then((c) => c.syncEngine); + return ensureOnlineComponents(client).then(c => c.syncEngine); } function getDatastore(client: FirestoreClient): Promise { - return ensureOnlineComponents(client).then((c) => c.datastore); + return ensureOnlineComponents(client).then(c => c.datastore); } export async function getEventManager( - client: FirestoreClient, + client: FirestoreClient ): Promise { const onlineComponentProvider = await ensureOnlineComponents(client); const eventManager = onlineComponentProvider.eventManager; eventManager.onListen = syncEngineListen.bind( null, - onlineComponentProvider.syncEngine, + onlineComponentProvider.syncEngine ); eventManager.onUnlisten = syncEngineUnlisten.bind( null, - onlineComponentProvider.syncEngine, + onlineComponentProvider.syncEngine ); return eventManager; } /** Enables the network connection and re-enqueues all pending operations. */ export function firestoreClientEnableNetwork( - client: FirestoreClient, + client: FirestoreClient ): Promise { return client.asyncQueue.enqueue(async () => { const persistence = await getPersistence(client); @@ -414,7 +414,7 @@ export function firestoreClientEnableNetwork( /** Disables the network connection. Pending operations will not complete. */ export function firestoreClientDisableNetwork( - client: FirestoreClient, + client: FirestoreClient ): Promise { return client.asyncQueue.enqueue(async () => { const persistence = await getPersistence(client); @@ -430,7 +430,7 @@ export function firestoreClientDisableNetwork( * can be either acceptance or rejection. */ export function firestoreClientWaitForPendingWrites( - client: FirestoreClient, + client: FirestoreClient ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -444,7 +444,7 @@ export function firestoreClientListen( client: FirestoreClient, query: Query, options: ListenOptions, - observer: Partial>, + observer: Partial> ): () => void { const wrappedObserver = new AsyncObserver(observer); const listener = new QueryListener(query, wrappedObserver, options); @@ -463,7 +463,7 @@ export function firestoreClientListen( export function firestoreClientGetDocumentFromLocalCache( client: FirestoreClient, - docKey: DocumentKey, + docKey: DocumentKey ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -476,7 +476,7 @@ export function firestoreClientGetDocumentFromLocalCache( export function firestoreClientGetDocumentViaSnapshotListener( client: FirestoreClient, key: DocumentKey, - options: GetOptions = {}, + options: GetOptions = {} ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -486,7 +486,7 @@ export function firestoreClientGetDocumentViaSnapshotListener( client.asyncQueue, key, options, - deferred, + deferred ); }); return deferred.promise; @@ -494,7 +494,7 @@ export function firestoreClientGetDocumentViaSnapshotListener( export function firestoreClientGetDocumentsFromLocalCache( client: FirestoreClient, - query: Query, + query: Query ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -507,7 +507,7 @@ export function firestoreClientGetDocumentsFromLocalCache( export function firestoreClientGetDocumentsViaSnapshotListener( client: FirestoreClient, query: Query, - options: GetOptions = {}, + options: GetOptions = {} ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -517,7 +517,7 @@ export function firestoreClientGetDocumentsViaSnapshotListener( client.asyncQueue, query, options, - deferred, + deferred ); }); return deferred.promise; @@ -526,7 +526,7 @@ export function firestoreClientGetDocumentsViaSnapshotListener( export function firestoreClientRunAggregateQuery( client: FirestoreClient, query: Query, - aggregates: Aggregate[], + aggregates: Aggregate[] ): Promise { const deferred = new Deferred(); @@ -541,13 +541,13 @@ export function firestoreClientRunAggregateQuery( deferred.reject( new FirestoreError( Code.UNAVAILABLE, - "Failed to get aggregate result because the client is offline.", - ), + 'Failed to get aggregate result because the client is offline.' + ) ); } else { const datastore = await getDatastore(client); deferred.resolve( - invokeRunAggregationQueryRpc(datastore, query, aggregates), + invokeRunAggregationQueryRpc(datastore, query, aggregates) ); } } catch (e) { @@ -559,7 +559,7 @@ export function firestoreClientRunAggregateQuery( export function firestoreClientWrite( client: FirestoreClient, - mutations: Mutation[], + mutations: Mutation[] ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -571,7 +571,7 @@ export function firestoreClientWrite( export function firestoreClientAddSnapshotsInSyncListener( client: FirestoreClient, - observer: Partial>, + observer: Partial> ): () => void { const wrappedObserver = new AsyncObserver(observer); client.asyncQueue.enqueueAndForget(async () => { @@ -605,7 +605,7 @@ export function firestoreClientAddSnapshotsInSyncListener( export function firestoreClientTransaction( client: FirestoreClient, updateFunction: (transaction: Transaction) => Promise, - options: TransactionOptions, + options: TransactionOptions ): Promise { const deferred = new Deferred(); client.asyncQueue.enqueueAndForget(async () => { @@ -615,7 +615,7 @@ export function firestoreClientTransaction( datastore, options, updateFunction, - deferred, + deferred ).run(); }); return deferred.promise; @@ -624,7 +624,7 @@ export function firestoreClientTransaction( async function readDocumentFromCache( localStore: LocalStore, docKey: DocumentKey, - result: Deferred, + result: Deferred ): Promise { try { const document = await localStoreReadDocument(localStore, docKey); @@ -636,17 +636,17 @@ async function readDocumentFromCache( result.reject( new FirestoreError( Code.UNAVAILABLE, - "Failed to get document from cache. (However, this document may " + + 'Failed to get document from cache. (However, this document may ' + "exist on the server. Run again without setting 'source' in " + - "the GetOptions to attempt to retrieve the document from the " + - "server.)", - ), + 'the GetOptions to attempt to retrieve the document from the ' + + 'server.)' + ) ); } } catch (e) { const firestoreError = wrapInUserErrorIfRecoverable( e as Error, - `Failed to get document '${docKey} from cache`, + `Failed to get document '${docKey} from cache` ); result.reject(firestoreError); } @@ -661,7 +661,7 @@ function readDocumentViaSnapshotListener( asyncQueue: AsyncQueue, key: DocumentKey, options: GetOptions, - result: Deferred, + result: Deferred ): Promise { const wrappedObserver = new AsyncObserver({ next: (snap: ViewSnapshot) => { @@ -683,33 +683,33 @@ function readDocumentViaSnapshotListener( result.reject( new FirestoreError( Code.UNAVAILABLE, - "Failed to get document because the client is offline.", - ), + 'Failed to get document because the client is offline.' + ) ); } else if ( exists && snap.fromCache && options && - options.source === "server" + options.source === 'server' ) { result.reject( new FirestoreError( Code.UNAVAILABLE, - "Failed to get document from server. (However, this " + - "document does exist in the local cache. Run again " + + 'Failed to get document from server. (However, this ' + + 'document does exist in the local cache. Run again ' + 'without setting source to "server" to ' + - "retrieve the cached document.)", - ), + 'retrieve the cached document.)' + ) ); } else { debugAssert( snap.docs.size <= 1, - "Expected zero or a single result on a document-only query", + 'Expected zero or a single result on a document-only query' ); result.resolve(snap); } }, - error: (e) => result.reject(e), + error: e => result.reject(e) }); const listener = new QueryListener( @@ -717,8 +717,8 @@ function readDocumentViaSnapshotListener( wrappedObserver, { includeMetadataChanges: true, - waitForSyncWhenOnline: true, - }, + waitForSyncWhenOnline: true + } ); return eventManagerListen(eventManager, listener); } @@ -726,25 +726,25 @@ function readDocumentViaSnapshotListener( async function executeQueryFromCache( localStore: LocalStore, query: Query, - result: Deferred, + result: Deferred ): Promise { try { const queryResult = await localStoreExecuteQuery( localStore, query, - /* usePreviousResults= */ true, + /* usePreviousResults= */ true ); const view = new View(query, queryResult.remoteKeys); const viewDocChanges = view.computeDocChanges(queryResult.documents); const viewChange = view.applyChanges( viewDocChanges, - /* updateLimboDocuments= */ false, + /* updateLimboDocuments= */ false ); result.resolve(viewChange.snapshot!); } catch (e) { const firestoreError = wrapInUserErrorIfRecoverable( e as Error, - `Failed to execute query '${query} against cache`, + `Failed to execute query '${query} against cache` ); result.reject(firestoreError); } @@ -759,36 +759,36 @@ function executeQueryViaSnapshotListener( asyncQueue: AsyncQueue, query: Query, options: GetOptions, - result: Deferred, + result: Deferred ): Promise { const wrappedObserver = new AsyncObserver({ - next: (snapshot) => { + next: snapshot => { // Remove query first before passing event to user to avoid // user actions affecting the now stale query. asyncQueue.enqueueAndForget(() => eventManagerUnlisten(eventManager, listener) ); - if (snapshot.fromCache && options.source === "server") { + if (snapshot.fromCache && options.source === 'server') { result.reject( new FirestoreError( Code.UNAVAILABLE, - "Failed to get documents from server. (However, these " + - "documents may exist in the local cache. Run again " + + 'Failed to get documents from server. (However, these ' + + 'documents may exist in the local cache. Run again ' + 'without setting source to "server" to ' + - "retrieve the cached documents.)", - ), + 'retrieve the cached documents.)' + ) ); } else { result.resolve(snapshot); } }, - error: (e) => result.reject(e), + error: e => result.reject(e) }); const listener = new QueryListener(query, wrappedObserver, { includeMetadataChanges: true, - waitForSyncWhenOnline: true, + waitForSyncWhenOnline: true }); return eventManagerListen(eventManager, listener); } @@ -797,7 +797,7 @@ export function firestoreClientLoadBundle( client: FirestoreClient, databaseId: DatabaseId, data: ReadableStream | ArrayBuffer | string, - resultTask: LoadBundleTask, + resultTask: LoadBundleTask ): void { const reader = createBundleReader(data, newSerializer(databaseId)); client.asyncQueue.enqueueAndForget(async () => { @@ -807,7 +807,7 @@ export function firestoreClientLoadBundle( export function firestoreClientGetNamedQuery( client: FirestoreClient, - queryName: string, + queryName: string ): Promise { return client.asyncQueue.enqueue(async () => localStoreGetNamedQuery(await getLocalStore(client), queryName) @@ -816,10 +816,10 @@ export function firestoreClientGetNamedQuery( function createBundleReader( data: ReadableStream | ArrayBuffer | string, - serializer: JsonProtoSerializer, + serializer: JsonProtoSerializer ): BundleReader { let content: ReadableStream | ArrayBuffer; - if (typeof data === "string") { + if (typeof data === 'string') { content = newTextEncoder().encode(data); } else { content = data; @@ -829,12 +829,12 @@ function createBundleReader( export function firestoreClientSetIndexConfiguration( client: FirestoreClient, - indexes: FieldIndex[], + indexes: FieldIndex[] ): Promise { return client.asyncQueue.enqueue(async () => { return localStoreConfigureFieldIndexes( await getLocalStore(client), - indexes, + indexes ); }); } diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 9657a71ac5b..0c77a26196e 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { isIndexedDBAvailable } from "@firebase/util"; +import { isIndexedDBAvailable } from '@firebase/util'; import { clearIndexedDbPersistence, @@ -34,14 +34,14 @@ import { QuerySnapshot, setDoc, SnapshotListenOptions, - terminate, -} from "./firebase_export"; + terminate +} from './firebase_export'; import { ALT_PROJECT_ID, DEFAULT_PROJECT_ID, DEFAULT_SETTINGS, - USE_EMULATOR, -} from "./settings"; + USE_EMULATOR +} from './settings'; /* eslint-disable no-restricted-globals */ @@ -52,19 +52,19 @@ function isIeOrEdge(): boolean { const ua = window.navigator.userAgent; return ( - ua.indexOf("MSIE ") > 0 || - ua.indexOf("Trident/") > 0 || - ua.indexOf("Edge/") > 0 + ua.indexOf('MSIE ') > 0 || + ua.indexOf('Trident/') > 0 || + ua.indexOf('Edge/') > 0 ); } export function isPersistenceAvailable(): boolean { return ( - typeof window === "object" && + typeof window === 'object' && isIndexedDBAvailable() && !isIeOrEdge() && - (typeof process === "undefined" || - process.env?.INCLUDE_FIRESTORE_PERSISTENCE !== "false") + (typeof process === 'undefined' || + process.env?.INCLUDE_FIRESTORE_PERSISTENCE !== 'false') ); } @@ -75,7 +75,7 @@ export function isPersistenceAvailable(): boolean { function apiDescribeInternal( describeFn: Mocha.PendingSuiteFunction, message: string, - testSuite: (persistence: boolean) => void, + testSuite: (persistence: boolean) => void ): void { const persistenceModes = [false]; if (isPersistenceAvailable()) { @@ -89,7 +89,7 @@ function apiDescribeInternal( type ApiSuiteFunction = ( message: string, - testSuite: (persistence: boolean) => void, + testSuite: (persistence: boolean) => void ) => void; interface ApiDescribe { (message: string, testSuite: (persistence: boolean) => void): void; @@ -99,7 +99,7 @@ interface ApiDescribe { export const apiDescribe = apiDescribeInternal.bind( null, - describe, + describe ) as ApiDescribe; // eslint-disable-next-line no-restricted-properties apiDescribe.skip = apiDescribeInternal.bind(null, describe.skip); @@ -108,22 +108,22 @@ apiDescribe.only = apiDescribeInternal.bind(null, describe.only); /** Converts the documents in a QuerySnapshot to an array with the data of each document. */ export function toDataArray(docSet: QuerySnapshot): DocumentData[] { - return docSet.docs.map((d) => d.data()); + return docSet.docs.map(d => d.data()); } /** Converts the changes in a QuerySnapshot to an array with the data of each document. */ export function toChangesArray( docSet: QuerySnapshot, - options?: SnapshotListenOptions, + options?: SnapshotListenOptions ): DocumentData[] { - return docSet.docChanges(options).map((d) => d.doc.data()); + return docSet.docChanges(options).map(d => d.doc.data()); } export function toDataMap(docSet: QuerySnapshot): { [field: string]: DocumentData; } { const docsData: { [field: string]: DocumentData } = {}; - docSet.forEach((doc) => { + docSet.forEach(doc => { docsData[doc.id] = doc.data(); }); return docsData; @@ -131,12 +131,12 @@ export function toDataMap(docSet: QuerySnapshot): { /** Converts a DocumentSet to an array with the id of each document */ export function toIds(docSet: QuerySnapshot): string[] { - return docSet.docs.map((d) => d.id); + return docSet.docs.map(d => d.id); } export function withTestDb( persistence: boolean, - fn: (db: Firestore) => Promise, + fn: (db: Firestore) => Promise ): Promise { return withTestDbs(persistence, 1, ([db]) => { return fn(db); @@ -144,7 +144,7 @@ export function withTestDb( } export function withEnsuredEagerGcTestDb( - fn: (db: Firestore) => Promise, + fn: (db: Firestore) => Promise ): Promise { return withTestDbsSettings( false, @@ -153,24 +153,24 @@ export function withEnsuredEagerGcTestDb( 1, async ([db]) => { return fn(db); - }, + } ); } export function withEnsuredLruGcTestDb( persistence: boolean, - fn: (db: Firestore) => Promise, + fn: (db: Firestore) => Promise ): Promise { const newSettings = { ...DEFAULT_SETTINGS }; if (persistence) { newSettings.localCache = persistentLocalCache({ - cacheSizeBytes: 1 * 1024 * 1024, + cacheSizeBytes: 1 * 1024 * 1024 }); } else { newSettings.localCache = memoryLocalCache({ garbageCollector: memoryLruGarbageCollector({ - cacheSizeBytes: 1 * 1024 * 1024, - }), + cacheSizeBytes: 1 * 1024 * 1024 + }) }); } return withTestDbsSettings( @@ -180,14 +180,14 @@ export function withEnsuredLruGcTestDb( 1, async ([db]) => { return fn(db); - }, + } ); } /** Runs provided fn with a db for an alternate project id. */ export function withAlternateTestDb( persistence: boolean, - fn: (db: Firestore) => Promise, + fn: (db: Firestore) => Promise ): Promise { return withTestDbsSettings( persistence, @@ -196,21 +196,21 @@ export function withAlternateTestDb( 1, ([db]) => { return fn(db); - }, + } ); } export function withTestDbs( persistence: boolean, numDbs: number, - fn: (db: Firestore[]) => Promise, + fn: (db: Firestore[]) => Promise ): Promise { return withTestDbsSettings( persistence, DEFAULT_PROJECT_ID, DEFAULT_SETTINGS, numDbs, - fn, + fn ); } export async function withTestDbsSettings( @@ -218,7 +218,7 @@ export async function withTestDbsSettings( projectId: string, settings: PrivateSettings, numDbs: number, - fn: (db: Firestore[]) => Promise, + fn: (db: Firestore[]) => Promise ): Promise { if (numDbs === 0) { throw new Error("Can't test with no databases"); @@ -250,7 +250,7 @@ export async function withTestDbsSettings( export async function withNamedTestDbsOrSkipUnlessUsingEmulator( persistence: boolean, dbNames: string[], - fn: (db: Firestore[]) => Promise, + fn: (db: Firestore[]) => Promise ): Promise { // Tests with named DBs can only run on emulator for now. This is because the // emulator does not require DB to be created before use. @@ -284,17 +284,17 @@ export async function withNamedTestDbsOrSkipUnlessUsingEmulator( export function withTestDoc( persistence: boolean, - fn: (doc: DocumentReference, db: Firestore) => Promise, + fn: (doc: DocumentReference, db: Firestore) => Promise ): Promise { - return withTestDb(persistence, (db) => { - return fn(doc(collection(db, "test-collection")), db); + return withTestDb(persistence, db => { + return fn(doc(collection(db, 'test-collection')), db); }); } export function withTestDocAndSettings( persistence: boolean, settings: PrivateSettings, - fn: (doc: DocumentReference) => Promise, + fn: (doc: DocumentReference) => Promise ): Promise { return withTestDbsSettings( persistence, @@ -302,8 +302,8 @@ export function withTestDocAndSettings( settings, 1, ([db]) => { - return fn(doc(collection(db, "test-collection"))); - }, + return fn(doc(collection(db, 'test-collection'))); + } ); } @@ -315,10 +315,10 @@ export function withTestDocAndSettings( export function withTestDocAndInitialData( persistence: boolean, initialData: DocumentData | null, - fn: (doc: DocumentReference, db: Firestore) => Promise, + fn: (doc: DocumentReference, db: Firestore) => Promise ): Promise { - return withTestDb(persistence, (db) => { - const docRef: DocumentReference = doc(collection(db, "test-collection")); + return withTestDb(persistence, db => { + const docRef: DocumentReference = doc(collection(db, 'test-collection')); if (initialData) { return setDoc(docRef, initialData).then(() => fn(docRef, db)); } else { @@ -330,14 +330,14 @@ export function withTestDocAndInitialData( export function withTestCollection( persistence: boolean, docs: { [key: string]: DocumentData }, - fn: (collection: CollectionReference, db: Firestore) => Promise, + fn: (collection: CollectionReference, db: Firestore) => Promise ): Promise { return withTestCollectionSettings(persistence, DEFAULT_SETTINGS, docs, fn); } export function withEmptyTestCollection( persistence: boolean, - fn: (collection: CollectionReference, db: Firestore) => Promise, + fn: (collection: CollectionReference, db: Firestore) => Promise ): Promise { return withTestCollection(persistence, {}, fn); } @@ -348,7 +348,7 @@ export function withTestCollectionSettings( persistence: boolean, settings: PrivateSettings, docs: { [key: string]: DocumentData }, - fn: (collection: CollectionReference, db: Firestore) => Promise, + fn: (collection: CollectionReference, db: Firestore) => Promise ): Promise { return withTestDbsSettings( persistence, @@ -357,14 +357,14 @@ export function withTestCollectionSettings( 2, ([testDb, setupDb]) => { // Abuse .doc() to get a random ID. - const collectionId = "test-collection-" + doc(collection(testDb, "x")).id; + const collectionId = 'test-collection-' + doc(collection(testDb, 'x')).id; const testCollection = collection(testDb, collectionId); const setupCollection = collection(setupDb, collectionId); const sets: Array> = []; - Object.keys(docs).forEach((key) => { + Object.keys(docs).forEach(key => { sets.push(setDoc(doc(setupCollection, key), docs[key])); }); return Promise.all(sets).then(() => fn(testCollection, testDb)); - }, + } ); } From 8e62300c437993b95e833db598b2e97924cfe5ca Mon Sep 17 00:00:00 2001 From: wu-hui Date: Wed, 12 Apr 2023 13:51:39 +0000 Subject: [PATCH 31/39] Update API reports --- common/api-review/auth.api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index ca5a4b633e7..00ab8febacb 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -776,13 +776,13 @@ export class TotpSecret { readonly codeIntervalSeconds: number; readonly codeLength: number; readonly enrollmentCompletionDeadline: string; - // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) static _fromStartTotpMfaEnrollmentResponse(response: StartTotpMfaEnrollmentResponse, auth: AuthInternal): TotpSecret; generateQrCodeUrl(accountName?: string, issuer?: string): string; readonly hashingAlgorithm: string; - // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _makeTotpVerificationInfo(otp: string): TotpVerificationInfo; From b83a72323bd687799562e6369fcb8af30243e4d1 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 17 Apr 2023 10:13:11 -0400 Subject: [PATCH 32/39] addressing feedback --- packages/firestore/src/api/cache_config.ts | 6 +++--- packages/firestore/src/core/firestore_client.ts | 12 ++++++------ packages/firestore/src/local/memory_persistence.ts | 4 ++-- .../test/unit/specs/garbage_collection_spec.test.ts | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/firestore/src/api/cache_config.ts b/packages/firestore/src/api/cache_config.ts index 974cfa5c83a..57579d096a3 100644 --- a/packages/firestore/src/api/cache_config.ts +++ b/packages/firestore/src/api/cache_config.ts @@ -136,10 +136,10 @@ export type MemoryGarbageCollector = * active queries, and have no local mutations attached to them. * * This collector tries to ensure lowest memory footprints from the SDK, - * at the risk of querying backend repeated for a document it could have - * cached locally. + * at the risk of documents not being cached for offline queries or for + * direct queries to the cache. * - * Use factory function {@link memoryEagerGarbageCollector()} to create a + * Use factory function {@link memoryEagerGarbageCollector()} to create an * instance of this collector. */ export type MemoryEagerGarbageCollector = { diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 96348ec394d..607eaee06f0 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -128,12 +128,12 @@ export class FirestoreClient { private authCredentials: CredentialsProvider, private appCheckCredentials: CredentialsProvider, /** - * Asynchronous queue responsible for all of our internal processing. When // - * we get incoming work from the user (via public API) or the network // - * (incoming GRPC messages), we should always schedule onto this queue. // - * This ensures all of our work is properly serialized (e.g. we don't // - * start processing a new operation while the previous one is waiting for // - * an async I/O to complete). // + * Asynchronous queue responsible for all of our internal processing. When + * we get incoming work from the user (via public API) or the network + * (incoming GRPC messages), we should always schedule onto this queue. + * This ensures all of our work is properly serialized (e.g. we don't + * start processing a new operation while the previous one is waiting for + * an async I/O to complete). */ public asyncQueue: AsyncQueue, private databaseInfo: DatabaseInfo diff --git a/packages/firestore/src/local/memory_persistence.ts b/packages/firestore/src/local/memory_persistence.ts index 1fda77e9538..fe2c1c53d10 100644 --- a/packages/firestore/src/local/memory_persistence.ts +++ b/packages/firestore/src/local/memory_persistence.ts @@ -355,9 +355,9 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate { static factory( persistence: MemoryPersistence, - lruParams: LruParams | null + lruParams: LruParams ): MemoryLruDelegate { - return new MemoryLruDelegate(persistence, lruParams!!); + return new MemoryLruDelegate(persistence, lruParams); } // No-ops, present so memory persistence doesn't have to care which delegate diff --git a/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts b/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts index f9115727ee8..cf6e8d19172 100644 --- a/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts +++ b/packages/firestore/test/unit/specs/garbage_collection_spec.test.ts @@ -74,7 +74,7 @@ describeSpec('Garbage Collection:', [], () => { ); specTest( - 'Contents of query are kept after listen is removed, and GC threshold is reached', + 'Contents of query are removed after listen is removed, and GC threshold is reached', [], () => { const query1 = query('collection'); From c20f61fa1078eeac8edfd9b2fc9581205c7915a8 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 17 Apr 2023 10:21:49 -0400 Subject: [PATCH 33/39] docsite --- docs-devsite/firestore_.md | 2 +- docs-devsite/firestore_.memoryeagergarbagecollector.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs-devsite/firestore_.md b/docs-devsite/firestore_.md index 8146176e7d7..3fc6c253769 100644 --- a/docs-devsite/firestore_.md +++ b/docs-devsite/firestore_.md @@ -156,7 +156,7 @@ https://github.com/firebase/firebase-js-sdk | [IndexField](./firestore_.indexfield.md#indexfield_interface) | (BETA) A single field element in an index configuration. | | [LoadBundleTaskProgress](./firestore_.loadbundletaskprogress.md#loadbundletaskprogress_interface) | Represents a progress update or a final state from loading bundles. | | [MemoryCacheSettings](./firestore_.memorycachesettings.md#memorycachesettings_interface) | An settings object to configure an MemoryLocalCache instance. | -| [MemoryEagerGarbageCollector](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollector_interface) | A garbage collector deletes documents whenever they are not part of any active queries, and have no local mutations attached to them.This collector tries to ensure lowest memory footprints from the SDK, at the risk of querying backend repeated for a document it could have cached locally.Use factory function to create a instance of this collector. | +| [MemoryEagerGarbageCollector](./firestore_.memoryeagergarbagecollector.md#memoryeagergarbagecollector_interface) | A garbage collector deletes documents whenever they are not part of any active queries, and have no local mutations attached to them.This collector tries to ensure lowest memory footprints from the SDK, at the risk of documents not being cached for offline queries or for direct queries to the cache.Use factory function to create an instance of this collector. | | [MemoryLocalCache](./firestore_.memorylocalcache.md#memorylocalcache_interface) | Provides an in-memory cache to the SDK. This is the default cache unless explicitly configured otherwise.To use, create an instance using the factory function , then set the instance to FirestoreSettings.cache and call initializeFirestore using the settings object. | | [MemoryLruGarbageCollector](./firestore_.memorylrugarbagecollector.md#memorylrugarbagecollector_interface) | A garbage collector deletes Least-Recently-Used documents in multiple batches.This collector is configured with a target size, and will only perform collection when the cached documents exceed the target size. It avoids querying backend repeated for the same query or document, at the risk of having a larger memory footprint.Use factory function to create a instance of this collector. | | [PersistenceSettings](./firestore_.persistencesettings.md#persistencesettings_interface) | Settings that can be passed to enableIndexedDbPersistence() to configure Firestore persistence. | diff --git a/docs-devsite/firestore_.memoryeagergarbagecollector.md b/docs-devsite/firestore_.memoryeagergarbagecollector.md index f373ced2dcb..01e7341611a 100644 --- a/docs-devsite/firestore_.memoryeagergarbagecollector.md +++ b/docs-devsite/firestore_.memoryeagergarbagecollector.md @@ -12,9 +12,9 @@ https://github.com/firebase/firebase-js-sdk # MemoryEagerGarbageCollector interface A garbage collector deletes documents whenever they are not part of any active queries, and have no local mutations attached to them. -This collector tries to ensure lowest memory footprints from the SDK, at the risk of querying backend repeated for a document it could have cached locally. +This collector tries to ensure lowest memory footprints from the SDK, at the risk of documents not being cached for offline queries or for direct queries to the cache. -Use factory function to create a instance of this collector. +Use factory function to create an instance of this collector. Signature: From 68e2f21e62ab5ee6c4ef837472d45b1d077b7b83 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 17 Apr 2023 10:25:15 -0400 Subject: [PATCH 34/39] review auth.api.md --- common/api-review/auth.api.md | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index 67e141db053..0a83cc77820 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -103,14 +103,14 @@ export class AuthCredential { protected constructor( providerId: string, signInMethod: string); - // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.doc.d.ts - // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _getIdTokenResponse(_auth: AuthInternal): Promise; // @internal (undocumented) _getReauthenticationResolver(_auth: AuthInternal): Promise; - // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _linkToIdToken(_auth: AuthInternal, _idToken: string): Promise; @@ -291,9 +291,6 @@ export function connectAuthEmulator(auth: Auth, url: string, options?: { disableWarnings: boolean; }): void; -// @public -export const cordovaPopupRedirectResolver: PopupRedirectResolver; - // @public export function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; @@ -357,7 +354,7 @@ export interface EmulatorConfig { export { ErrorFn } -// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.doc.d.ts +// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.d.ts // // @public export class FacebookAuthProvider extends BaseOAuthProvider { @@ -393,9 +390,6 @@ export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; @@ -508,7 +502,7 @@ export type NextOrObserver = NextFn | Observer; export class OAuthCredential extends AuthCredential { accessToken?: string; static fromJSON(json: string | object): OAuthCredential | null; - // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.d.ts // // @internal (undocumented) static _fromParams(params: OAuthCredentialParams): OAuthCredential; @@ -586,7 +580,7 @@ export class PhoneAuthCredential extends AuthCredential { _getReauthenticationResolver(auth: AuthInternal): Promise; // @internal (undocumented) _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _makeVerificationRequest(): SignInWithPhoneNumberRequest; @@ -665,9 +659,6 @@ export interface ReactNativeAsyncStorage { setItem(key: string, value: string): Promise; } -// @public -export const reactNativeLocalPersistence: Persistence; - // @public export function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; @@ -686,13 +677,13 @@ export interface RecaptchaParameters { [key: string]: any; } -// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.doc.d.ts +// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.d.ts // // @public export class RecaptchaVerifier implements ApplicationVerifierInternal { constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth); clear(): void; - // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.d.ts // // @internal (undocumented) readonly _recaptchaLoader: ReCaptchaLoader; @@ -706,7 +697,7 @@ export class RecaptchaVerifier implements ApplicationVerifierInternal { // @public export function reload(user: User): Promise; -// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.doc.d.ts +// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.d.ts // // @public export class SAMLAuthProvider extends FederatedAuthProvider { @@ -787,13 +778,13 @@ export class TotpSecret { readonly codeIntervalSeconds: number; readonly codeLength: number; readonly enrollmentCompletionDeadline: string; - // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.d.ts // // @internal (undocumented) static _fromStartTotpMfaEnrollmentResponse(response: StartTotpMfaEnrollmentResponse, auth: AuthInternal): TotpSecret; generateQrCodeUrl(accountName?: string, issuer?: string): string; readonly hashingAlgorithm: string; - // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _makeTotpVerificationInfo(otp: string): TotpVerificationInfo; From 5303e16e96de07f8f0d2995c2040fa2206ec095e Mon Sep 17 00:00:00 2001 From: wu-hui Date: Mon, 17 Apr 2023 14:44:30 +0000 Subject: [PATCH 35/39] Update API reports --- common/api-review/auth.api.md | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index 0a83cc77820..67e141db053 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -103,14 +103,14 @@ export class AuthCredential { protected constructor( providerId: string, signInMethod: string); - // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _getIdTokenResponse(_auth: AuthInternal): Promise; // @internal (undocumented) _getReauthenticationResolver(_auth: AuthInternal): Promise; - // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _linkToIdToken(_auth: AuthInternal, _idToken: string): Promise; @@ -291,6 +291,9 @@ export function connectAuthEmulator(auth: Auth, url: string, options?: { disableWarnings: boolean; }): void; +// @public +export const cordovaPopupRedirectResolver: PopupRedirectResolver; + // @public export function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; @@ -354,7 +357,7 @@ export interface EmulatorConfig { export { ErrorFn } -// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.doc.d.ts // // @public export class FacebookAuthProvider extends BaseOAuthProvider { @@ -390,6 +393,9 @@ export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; @@ -502,7 +508,7 @@ export type NextOrObserver = NextFn | Observer; export class OAuthCredential extends AuthCredential { accessToken?: string; static fromJSON(json: string | object): OAuthCredential | null; - // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) static _fromParams(params: OAuthCredentialParams): OAuthCredential; @@ -580,7 +586,7 @@ export class PhoneAuthCredential extends AuthCredential { _getReauthenticationResolver(auth: AuthInternal): Promise; // @internal (undocumented) _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _makeVerificationRequest(): SignInWithPhoneNumberRequest; @@ -659,6 +665,9 @@ export interface ReactNativeAsyncStorage { setItem(key: string, value: string): Promise; } +// @public +export const reactNativeLocalPersistence: Persistence; + // @public export function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; @@ -677,13 +686,13 @@ export interface RecaptchaParameters { [key: string]: any; } -// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.doc.d.ts // // @public export class RecaptchaVerifier implements ApplicationVerifierInternal { constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth); clear(): void; - // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) readonly _recaptchaLoader: ReCaptchaLoader; @@ -697,7 +706,7 @@ export class RecaptchaVerifier implements ApplicationVerifierInternal { // @public export function reload(user: User): Promise; -// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.doc.d.ts // // @public export class SAMLAuthProvider extends FederatedAuthProvider { @@ -778,13 +787,13 @@ export class TotpSecret { readonly codeIntervalSeconds: number; readonly codeLength: number; readonly enrollmentCompletionDeadline: string; - // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) static _fromStartTotpMfaEnrollmentResponse(response: StartTotpMfaEnrollmentResponse, auth: AuthInternal): TotpSecret; generateQrCodeUrl(accountName?: string, issuer?: string): string; readonly hashingAlgorithm: string; - // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.doc.d.ts // // @internal (undocumented) _makeTotpVerificationInfo(otp: string): TotpVerificationInfo; From 5bf4ec815dbed492c708884e6303a2cdba763b75 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 17 Apr 2023 14:18:16 -0400 Subject: [PATCH 36/39] changeset and revert auth changes --- .changeset/olive-cycles-count.md | 6 ++++++ common/api-review/auth.api.md | 31 +++++++++++-------------------- packages/auth/api-extractor.json | 2 +- 3 files changed, 18 insertions(+), 21 deletions(-) create mode 100644 .changeset/olive-cycles-count.md diff --git a/.changeset/olive-cycles-count.md b/.changeset/olive-cycles-count.md new file mode 100644 index 00000000000..603b4fad3e7 --- /dev/null +++ b/.changeset/olive-cycles-count.md @@ -0,0 +1,6 @@ +--- +'firebase': patch +'@firebase/firestore': patch +--- + +Introduces a new LRU garbage document collector for memory cache. diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index 67e141db053..0a83cc77820 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -103,14 +103,14 @@ export class AuthCredential { protected constructor( providerId: string, signInMethod: string); - // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.doc.d.ts - // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _getIdTokenResponse(_auth: AuthInternal): Promise; // @internal (undocumented) _getReauthenticationResolver(_auth: AuthInternal): Promise; - // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _linkToIdToken(_auth: AuthInternal, _idToken: string): Promise; @@ -291,9 +291,6 @@ export function connectAuthEmulator(auth: Auth, url: string, options?: { disableWarnings: boolean; }): void; -// @public -export const cordovaPopupRedirectResolver: PopupRedirectResolver; - // @public export function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; @@ -357,7 +354,7 @@ export interface EmulatorConfig { export { ErrorFn } -// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.doc.d.ts +// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.d.ts // // @public export class FacebookAuthProvider extends BaseOAuthProvider { @@ -393,9 +390,6 @@ export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; @@ -508,7 +502,7 @@ export type NextOrObserver = NextFn | Observer; export class OAuthCredential extends AuthCredential { accessToken?: string; static fromJSON(json: string | object): OAuthCredential | null; - // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.d.ts // // @internal (undocumented) static _fromParams(params: OAuthCredentialParams): OAuthCredential; @@ -586,7 +580,7 @@ export class PhoneAuthCredential extends AuthCredential { _getReauthenticationResolver(auth: AuthInternal): Promise; // @internal (undocumented) _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _makeVerificationRequest(): SignInWithPhoneNumberRequest; @@ -665,9 +659,6 @@ export interface ReactNativeAsyncStorage { setItem(key: string, value: string): Promise; } -// @public -export const reactNativeLocalPersistence: Persistence; - // @public export function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; @@ -686,13 +677,13 @@ export interface RecaptchaParameters { [key: string]: any; } -// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.doc.d.ts +// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.d.ts // // @public export class RecaptchaVerifier implements ApplicationVerifierInternal { constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth); clear(): void; - // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.d.ts // // @internal (undocumented) readonly _recaptchaLoader: ReCaptchaLoader; @@ -706,7 +697,7 @@ export class RecaptchaVerifier implements ApplicationVerifierInternal { // @public export function reload(user: User): Promise; -// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.doc.d.ts +// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.d.ts // // @public export class SAMLAuthProvider extends FederatedAuthProvider { @@ -787,13 +778,13 @@ export class TotpSecret { readonly codeIntervalSeconds: number; readonly codeLength: number; readonly enrollmentCompletionDeadline: string; - // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "StartTotpMfaEnrollmentResponse" needs to be exported by the entry point index.d.ts // // @internal (undocumented) static _fromStartTotpMfaEnrollmentResponse(response: StartTotpMfaEnrollmentResponse, auth: AuthInternal): TotpSecret; generateQrCodeUrl(accountName?: string, issuer?: string): string; readonly hashingAlgorithm: string; - // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "TotpVerificationInfo" needs to be exported by the entry point index.d.ts // // @internal (undocumented) _makeTotpVerificationInfo(otp: string): TotpVerificationInfo; diff --git a/packages/auth/api-extractor.json b/packages/auth/api-extractor.json index 325fe12d507..ca7e1ee6983 100644 --- a/packages/auth/api-extractor.json +++ b/packages/auth/api-extractor.json @@ -1,6 +1,6 @@ { "extends": "../../config/api-extractor.json", - "mainEntryPointFilePath": "/dist/esm5/index.doc.d.ts", + "mainEntryPointFilePath": "/dist/esm5/index.d.ts", "dtsRollup": { "enabled": true, "untrimmedFilePath": "/dist/.d.ts", From fc4bb55cd692084497169ce124f09688c2972434 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 17 Apr 2023 14:20:36 -0400 Subject: [PATCH 37/39] fix error message --- packages/firestore/test/unit/specs/spec_builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 7b09dd6fe6c..6d4afd1041d 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -252,7 +252,7 @@ export class SpecBuilder { ensureManualLruGC(): this { debugAssert( !this.currentStep, - 'withGCEnabled() must be called before all spec steps.' + 'ensureManualLruGC() must be called before all spec steps.' ); this.config.useEagerGCForMemory = false; return this; From 85483bf20416313f4dcbff7d491120ea52828194 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Tue, 18 Apr 2023 07:23:03 -0400 Subject: [PATCH 38/39] minor bump --- .changeset/olive-cycles-count.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/olive-cycles-count.md b/.changeset/olive-cycles-count.md index 603b4fad3e7..96ebfaf2eb0 100644 --- a/.changeset/olive-cycles-count.md +++ b/.changeset/olive-cycles-count.md @@ -1,6 +1,6 @@ --- -'firebase': patch -'@firebase/firestore': patch +'firebase': minor +'@firebase/firestore': minor --- Introduces a new LRU garbage document collector for memory cache. From 4f81d93750f9ac03b6884a7c976bcd232529f4a7 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 19 Apr 2023 10:11:58 -0400 Subject: [PATCH 39/39] Remove gcEnabled for multab test --- packages/firestore/test/unit/specs/limbo_spec.test.ts | 4 ++-- packages/firestore/test/unit/specs/listen_spec.test.ts | 6 +++--- packages/firestore/test/unit/specs/spec_builder.ts | 5 +---- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/firestore/test/unit/specs/limbo_spec.test.ts b/packages/firestore/test/unit/specs/limbo_spec.test.ts index e1f8d50f77c..70f14ca666e 100644 --- a/packages/firestore/test/unit/specs/limbo_spec.test.ts +++ b/packages/firestore/test/unit/specs/limbo_spec.test.ts @@ -425,7 +425,7 @@ describeSpec('Limbo Documents:', [], () => { const docB = doc('collection/b', 1001, { key: 'b' }); const deletedDocB = deletedDoc('collection/b', 1005); - return client(0, false) + return client(0) .expectPrimaryState(true) .client(1) .userListens(query1) @@ -463,7 +463,7 @@ describeSpec('Limbo Documents:', [], () => { const deletedDocB = deletedDoc('collection/b', 1006); const deletedDocC = deletedDoc('collection/c', 1008); - return client(0, false) + return client(0) .expectPrimaryState(true) .userListens(query1) .watchAcksFull(query1, 1 * 1e6, docA, docB, docC) diff --git a/packages/firestore/test/unit/specs/listen_spec.test.ts b/packages/firestore/test/unit/specs/listen_spec.test.ts index 68efbebd6af..b94fea7011b 100644 --- a/packages/firestore/test/unit/specs/listen_spec.test.ts +++ b/packages/firestore/test/unit/specs/listen_spec.test.ts @@ -845,7 +845,7 @@ describeSpec('Listens:', [], () => { const docA = doc('collection/a', 1000, { key: 'a' }); const docB = doc('collection/b', 2000, { key: 'a' }); - return client(0, /* withGcEnabled= */ false) + return client(0) .becomeVisible() .client(1) .userListens(query1) @@ -1754,7 +1754,7 @@ describeSpec('Listens:', [], () => { () => { const query1 = query('collection'); return ( - client(0, /* withGcEnabled= */ false) + client(0) // Populate the cache with the empty query results. .userListens(query1) .watchAcksFull(query1, 1000) @@ -1785,7 +1785,7 @@ describeSpec('Listens:', [], () => { const doc1Deleted = deletedDoc('collection/a', 2000); return ( - client(0, /* withGcEnabled= */ false) + client(0) // Populate the cache with the empty query results. .userListens(query1) .watchAcksFull(query1, 1000, doc1) diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 6d4afd1041d..423aafb1a3b 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -1252,10 +1252,7 @@ export function spec(): SpecBuilder { /** Starts a new multi-client SpecTest. */ // PORTING NOTE: Only used by web multi-tab tests. -export function client( - num: number, - withGcEnabled?: boolean -): MultiClientSpecBuilder { +export function client(num: number): MultiClientSpecBuilder { const specBuilder = new MultiClientSpecBuilder(); specBuilder.ensureManualLruGC(); return specBuilder.client(num);