diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/AccessHelper.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/AccessHelper.java index 9fec3d209f2..d0800b5ce4a 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/AccessHelper.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/AccessHelper.java @@ -33,7 +33,6 @@ public static FirebaseFirestore newFirebaseFirestore( String persistenceKey, CredentialsProvider authProvider, CredentialsProvider appCheckProvider, - AsyncQueue asyncQueue, Function componentProviderFactory, FirebaseApp firebaseApp, FirebaseFirestore.InstanceRegistry instanceRegistry) { @@ -43,7 +42,6 @@ public static FirebaseFirestore newFirebaseFirestore( persistenceKey, authProvider, appCheckProvider, - asyncQueue, componentProviderFactory, firebaseApp, instanceRegistry, @@ -51,6 +49,6 @@ public static FirebaseFirestore newFirebaseFirestore( } public static AsyncQueue getAsyncQueue(FirebaseFirestore firestore) { - return firestore.getAsyncQueue(); + return firestore.clientProvider.getAsyncQueue(); } } diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CompositeIndexQueryTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CompositeIndexQueryTest.java index ce5903f4891..aa9be3bcf01 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CompositeIndexQueryTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/CompositeIndexQueryTest.java @@ -742,7 +742,7 @@ public void testMultipleInequalityReadFromCacheWhenOffline() { assertEquals(2L, snapshot1.size()); assertFalse(snapshot1.getMetadata().isFromCache()); - waitFor(collection.firestore.getClient().disableNetwork()); + waitFor(collection.firestore.disableNetwork()); QuerySnapshot snapshot2 = waitFor(query.get()); assertEquals(2L, snapshot2.size()); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java index 631dcec4dd8..958e5b58c15 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java @@ -49,6 +49,7 @@ import com.google.firebase.firestore.FirebaseFirestoreException.Code; import com.google.firebase.firestore.Query.Direction; import com.google.firebase.firestore.auth.User; +import com.google.firebase.firestore.core.FirestoreClient; import com.google.firebase.firestore.model.DatabaseId; import com.google.firebase.firestore.testutil.EventAccumulator; import com.google.firebase.firestore.testutil.IntegrationTestUtil; @@ -1110,7 +1111,7 @@ public void testRestartFirestoreLeadsToNewInstance() { assertSame(instance, sameInstance); waitFor(instance.document("abc/123").set(Collections.singletonMap("field", 100L))); - instance.terminate(); + waitFor(instance.terminate()); FirebaseFirestore newInstance = FirebaseFirestore.getInstance(app); newInstance.setFirestoreSettings(newTestSettings()); @@ -1132,7 +1133,7 @@ public void testAppDeleteLeadsToFirestoreTerminate() { app.delete(); - assertTrue(instance.getClient().isTerminated()); + assertTrue(instance.callClient(FirestoreClient::isTerminated)); } @Test diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/QueryTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/QueryTest.java index b6ec3d61a51..29ca658515e 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/QueryTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/QueryTest.java @@ -536,12 +536,12 @@ public void testQueriesFireFromCacheWhenOffline() { assertFalse(querySnapshot.getMetadata().isFromCache()); // offline event with fromCache=true - waitFor(collection.firestore.getClient().disableNetwork()); + waitFor(collection.firestore.disableNetwork()); querySnapshot = accum.await(); assertTrue(querySnapshot.getMetadata().isFromCache()); // back online event with fromCache=false - waitFor(collection.firestore.getClient().enableNetwork()); + waitFor(collection.firestore.enableNetwork()); querySnapshot = accum.await(); assertFalse(querySnapshot.getMetadata().isFromCache()); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java index 4fcc6f4f6f4..fb38935ddc2 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java @@ -214,7 +214,7 @@ public void testServerTimestampsCanReturnPreviousValueOfDifferentType() { @Test public void testServerTimestampsCanRetainPreviousValueThroughConsecutiveUpdates() { writeInitialData(); - waitFor(docRef.getFirestore().getClient().disableNetwork()); + waitFor(docRef.getFirestore().disableNetwork()); accumulator.awaitRemoteEvent(); docRef.update("a", FieldValue.serverTimestamp()); @@ -226,7 +226,7 @@ public void testServerTimestampsCanRetainPreviousValueThroughConsecutiveUpdates( localSnapshot = accumulator.awaitLocalEvent(); assertEquals(42L, localSnapshot.get("a", ServerTimestampBehavior.PREVIOUS)); - waitFor(docRef.getFirestore().getClient().enableNetwork()); + waitFor(docRef.getFirestore().enableNetwork()); DocumentSnapshot remoteSnapshot = accumulator.awaitRemoteEvent(); assertThat(remoteSnapshot.get("a")).isInstanceOf(Timestamp.class); @@ -235,7 +235,7 @@ public void testServerTimestampsCanRetainPreviousValueThroughConsecutiveUpdates( @Test public void testServerTimestampsUsesPreviousValueFromLocalMutation() { writeInitialData(); - waitFor(docRef.getFirestore().getClient().disableNetwork()); + waitFor(docRef.getFirestore().disableNetwork()); accumulator.awaitRemoteEvent(); docRef.update("a", FieldValue.serverTimestamp()); @@ -249,7 +249,7 @@ public void testServerTimestampsUsesPreviousValueFromLocalMutation() { localSnapshot = accumulator.awaitLocalEvent(); assertEquals(1337L, localSnapshot.get("a", ServerTimestampBehavior.PREVIOUS)); - waitFor(docRef.getFirestore().getClient().enableNetwork()); + waitFor(docRef.getFirestore().enableNetwork()); DocumentSnapshot remoteSnapshot = accumulator.awaitRemoteEvent(); assertThat(remoteSnapshot.get("a")).isInstanceOf(Timestamp.class); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/TransactionTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/TransactionTest.java index f83289d3d5a..bd90a4b3bb4 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/TransactionTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/TransactionTest.java @@ -371,7 +371,7 @@ public void testIncrementTransactionally() { AtomicInteger started = new AtomicInteger(0); FirebaseFirestore firestore = testFirestore(); - firestore.getAsyncQueue().skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); + AccessHelper.getAsyncQueue(firestore).skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); DocumentReference doc = firestore.collection("counters").document(); waitFor(doc.set(map("count", 5.0))); @@ -437,7 +437,7 @@ public void testUpdateTransactionally() { AtomicInteger counter = new AtomicInteger(0); FirebaseFirestore firestore = testFirestore(); - firestore.getAsyncQueue().skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); + AccessHelper.getAsyncQueue(firestore).skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); DocumentReference doc = firestore.collection("counters").document(); waitFor(doc.set(map("count", 5.0, "other", "yes"))); @@ -532,7 +532,7 @@ public void testUpdatePOJOTransactionally() { AtomicInteger started = new AtomicInteger(0); FirebaseFirestore firestore = testFirestore(); - firestore.getAsyncQueue().skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); + AccessHelper.getAsyncQueue(firestore).skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); DocumentReference doc = firestore.collection("counters").document(); waitFor(doc.set(new POJO(5.0, "no", "clean"))); @@ -601,7 +601,7 @@ public void testRetriesWhenDocumentThatWasReadWithoutBeingWrittenChanges() { @Test public void testReadingADocTwiceWithDifferentVersions() { FirebaseFirestore firestore = testFirestore(); - firestore.getAsyncQueue().skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); + AccessHelper.getAsyncQueue(firestore).skipDelaysForTimerId(TimerId.RETRY_TRANSACTION); DocumentReference doc = firestore.collection("counters").document(); waitFor(doc.set(map("count", 15.0))); AtomicInteger counter = new AtomicInteger(0); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ValidationTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ValidationTest.java index d433fe0597c..0fd261fefe3 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ValidationTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ValidationTest.java @@ -453,7 +453,7 @@ public void queriesCannotBeSortedByAnUncommittedServerTimestamp() { CollectionReference collection = testCollection(); // Ensure the server timestamp stays uncommitted for the first half of the test - waitFor(collection.firestore.getClient().disableNetwork()); + waitFor(collection.firestore.disableNetwork()); TaskCompletionSource offlineCallbackDone = new TaskCompletionSource<>(); TaskCompletionSource onlineCallbackDone = new TaskCompletionSource<>(); @@ -497,7 +497,7 @@ public void queriesCannotBeSortedByAnUncommittedServerTimestamp() { document.set(map("timestamp", FieldValue.serverTimestamp())); waitFor(offlineCallbackDone.getTask()); - waitFor(collection.firestore.getClient().enableNetwork()); + waitFor(collection.firestore.enableNetwork()); waitFor(onlineCallbackDone.getTask()); listenerRegistration.remove(); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java index b54db620470..a7417d96563 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java @@ -320,7 +320,6 @@ public static FirebaseFirestore testFirestore( persistenceKey, MockCredentialsProvider.instance(), new EmptyAppCheckTokenProvider(), - asyncQueue, ComponentProvider::defaultFactory, /* firebaseApp= */ null, /* instanceRegistry= */ (dbId) -> {}); diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/AggregateQuery.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/AggregateQuery.java index db4015d6386..0472975d6c7 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/AggregateQuery.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/AggregateQuery.java @@ -66,8 +66,7 @@ public Task get(@NonNull AggregateSource source) { TaskCompletionSource tcs = new TaskCompletionSource<>(); query .firestore - .getClient() - .runAggregateQuery(query.query, aggregateFieldList) + .callClient(client -> client.runAggregateQuery(query.query, aggregateFieldList)) .continueWith( Executors.DIRECT_EXECUTOR, (task) -> { diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentReference.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentReference.java index e117ceeefdf..e3097d32b00 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentReference.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentReference.java @@ -29,7 +29,6 @@ import com.google.firebase.firestore.core.ActivityScope; import com.google.firebase.firestore.core.AsyncEventListener; import com.google.firebase.firestore.core.EventManager.ListenOptions; -import com.google.firebase.firestore.core.ListenerRegistrationImpl; import com.google.firebase.firestore.core.QueryListener; import com.google.firebase.firestore.core.UserData.ParsedSetData; import com.google.firebase.firestore.core.UserData.ParsedUpdateData; @@ -38,11 +37,12 @@ import com.google.firebase.firestore.model.DocumentKey; import com.google.firebase.firestore.model.ResourcePath; import com.google.firebase.firestore.model.mutation.DeleteMutation; +import com.google.firebase.firestore.model.mutation.Mutation; import com.google.firebase.firestore.model.mutation.Precondition; import com.google.firebase.firestore.util.Assert; import com.google.firebase.firestore.util.Executors; import com.google.firebase.firestore.util.Util; -import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -165,9 +165,9 @@ public Task set(@NonNull Object data, @NonNull SetOptions options) { options.isMerge() ? firestore.getUserDataReader().parseMergeData(data, options.getFieldMask()) : firestore.getUserDataReader().parseSetData(data); + List mutations = singletonList(parsed.toMutation(key, Precondition.NONE)); return firestore - .getClient() - .write(Collections.singletonList(parsed.toMutation(key, Precondition.NONE))) + .callClient(client -> client.write(mutations)) .continueWith(Executors.DIRECT_EXECUTOR, voidErrorTransformer()); } @@ -229,9 +229,9 @@ public Task update( } private Task update(@NonNull ParsedUpdateData parsedData) { + List mutations = singletonList(parsedData.toMutation(key, Precondition.exists(true))); return firestore - .getClient() - .write(Collections.singletonList(parsedData.toMutation(key, Precondition.exists(true)))) + .callClient(client -> client.write(mutations)) .continueWith(Executors.DIRECT_EXECUTOR, voidErrorTransformer()); } @@ -242,9 +242,9 @@ private Task update(@NonNull ParsedUpdateData parsedData) { */ @NonNull public Task delete() { + List mutations = singletonList(new DeleteMutation(key, Precondition.NONE)); return firestore - .getClient() - .write(singletonList(new DeleteMutation(key, Precondition.NONE))) + .callClient(client -> client.write(mutations)) .continueWith(Executors.DIRECT_EXECUTOR, voidErrorTransformer()); } @@ -274,8 +274,7 @@ public Task get() { public Task get(@NonNull Source source) { if (source == Source.CACHE) { return firestore - .getClient() - .getDocumentFromLocalCache(key) + .callClient(client -> client.getDocumentFromLocalCache(key)) .continueWith( Executors.DIRECT_EXECUTOR, (Task task) -> { @@ -531,11 +530,17 @@ private ListenerRegistration addSnapshotListenerInternal( new AsyncEventListener<>(userExecutor, viewListener); com.google.firebase.firestore.core.Query query = asQuery(); - QueryListener queryListener = firestore.getClient().listen(query, options, asyncListener); - return ActivityScope.bind( - activity, - new ListenerRegistrationImpl(firestore.getClient(), queryListener, asyncListener)); + return firestore.callClient( + client -> { + QueryListener queryListener = client.listen(query, options, asyncListener); + return ActivityScope.bind( + activity, + () -> { + asyncListener.mute(); + client.stopListening(queryListener); + }); + }); } @Override diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java index f304bfff974..c1218829b8a 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java @@ -32,7 +32,6 @@ import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider; import com.google.firebase.auth.internal.InternalAuthProvider; import com.google.firebase.emulators.EmulatedServiceSettings; -import com.google.firebase.firestore.FirebaseFirestoreException.Code; import com.google.firebase.firestore.auth.CredentialsProvider; import com.google.firebase.firestore.auth.FirebaseAppCheckTokenProvider; import com.google.firebase.firestore.auth.FirebaseAuthCredentialsProvider; @@ -96,7 +95,6 @@ public interface InstanceRegistry { private final String persistenceKey; private final CredentialsProvider authProvider; private final CredentialsProvider appCheckProvider; - private final AsyncQueue asyncQueue; private final FirebaseApp firebaseApp; private final UserDataReader userDataReader; // When user requests to terminate, use this to notify `FirestoreMultiDbComponent` to deregister @@ -104,7 +102,7 @@ public interface InstanceRegistry { private final InstanceRegistry instanceRegistry; @Nullable private EmulatedServiceSettings emulatorSettings; private FirebaseFirestoreSettings settings; - private volatile FirestoreClient client; + final FirestoreClientProvider clientProvider; private final GrpcMetadataProvider metadataProvider; @Nullable private PersistentCacheIndexManager persistentCacheIndexManager; @@ -195,8 +193,6 @@ static FirebaseFirestore newInstance( } DatabaseId databaseId = DatabaseId.forDatabase(projectId, database); - AsyncQueue queue = new AsyncQueue(); - CredentialsProvider authProvider = new FirebaseAuthCredentialsProvider(deferredAuthProvider); CredentialsProvider appCheckProvider = @@ -214,7 +210,6 @@ static FirebaseFirestore newInstance( persistenceKey, authProvider, appCheckProvider, - queue, ComponentProvider::defaultFactory, app, instanceRegistry, @@ -228,7 +223,6 @@ static FirebaseFirestore newInstance( String persistenceKey, CredentialsProvider authProvider, CredentialsProvider appCheckProvider, - AsyncQueue asyncQueue, @NonNull Function componentProviderFactory, @Nullable FirebaseApp firebaseApp, InstanceRegistry instanceRegistry, @@ -239,8 +233,8 @@ static FirebaseFirestore newInstance( this.persistenceKey = checkNotNull(persistenceKey); this.authProvider = checkNotNull(authProvider); this.appCheckProvider = checkNotNull(appCheckProvider); - this.asyncQueue = checkNotNull(asyncQueue); this.componentProviderFactory = checkNotNull(componentProviderFactory); + this.clientProvider = new FirestoreClientProvider(this::newClient); // NOTE: We allow firebaseApp to be null in tests only. this.firebaseApp = firebaseApp; this.instanceRegistry = instanceRegistry; @@ -266,7 +260,7 @@ public void setFirestoreSettings(@NonNull FirebaseFirestoreSettings settings) { // As a special exception, don't throw if the same settings are passed repeatedly. This // should make it simpler to get a Firestore instance in an activity. - if (client != null && !this.settings.equals(settings)) { + if (clientProvider.isConfigured() && !this.settings.equals(settings)) { throw new IllegalStateException( "FirebaseFirestore has already been started and its settings can no longer be changed. " + "You can only call setFirestoreSettings() before calling any other methods on a " @@ -286,37 +280,30 @@ public void setFirestoreSettings(@NonNull FirebaseFirestoreSettings settings) { * @param port the emulator port (for example, 8080) */ public void useEmulator(@NonNull String host, int port) { - if (this.client != null) { - throw new IllegalStateException( - "Cannot call useEmulator() after instance has already been initialized."); - } - - emulatorSettings = new EmulatedServiceSettings(host, port); - settings = mergeEmulatorSettings(settings, emulatorSettings); - } + synchronized (clientProvider) { + if (clientProvider.isConfigured()) { + throw new IllegalStateException( + "Cannot call useEmulator() after instance has already been initialized."); + } - private void ensureClientConfigured() { - if (client != null) { - return; + emulatorSettings = new EmulatedServiceSettings(host, port); + settings = mergeEmulatorSettings(settings, emulatorSettings); } + } - synchronized (databaseId) { - if (client != null) { - return; - } + private FirestoreClient newClient(AsyncQueue asyncQueue) { + synchronized (clientProvider) { DatabaseInfo databaseInfo = new DatabaseInfo(databaseId, persistenceKey, settings.getHost(), settings.isSslEnabled()); - client = - new FirestoreClient( - context, - databaseInfo, - settings, - authProvider, - appCheckProvider, - asyncQueue, - metadataProvider, - componentProviderFactory.apply(settings)); + return new FirestoreClient( + context, + databaseInfo, + authProvider, + appCheckProvider, + asyncQueue, + metadataProvider, + componentProviderFactory.apply(settings)); } } @@ -367,7 +354,7 @@ public FirebaseApp getApp() { @PreviewApi @NonNull public Task setIndexConfiguration(@NonNull String json) { - ensureClientConfigured(); + clientProvider.ensureConfigured(); Preconditions.checkState( settings.isPersistenceEnabled(), "Cannot enable indexes when persistence is disabled"); @@ -410,7 +397,7 @@ public Task setIndexConfiguration(@NonNull String json) { throw new IllegalArgumentException("Failed to parse index configuration", e); } - return client.configureFieldIndexes(parsedIndexes); + return clientProvider.call(client -> client.configureFieldIndexes(parsedIndexes)); } /** @@ -424,12 +411,12 @@ public Task setIndexConfiguration(@NonNull String json) { * not in use. */ @Nullable - public synchronized PersistentCacheIndexManager getPersistentCacheIndexManager() { - ensureClientConfigured(); + public PersistentCacheIndexManager getPersistentCacheIndexManager() { + clientProvider.ensureConfigured(); if (persistentCacheIndexManager == null && (settings.isPersistenceEnabled() || settings.getCacheSettings() instanceof PersistentCacheSettings)) { - persistentCacheIndexManager = new PersistentCacheIndexManager(client); + persistentCacheIndexManager = new PersistentCacheIndexManager(clientProvider); } return persistentCacheIndexManager; } @@ -444,7 +431,7 @@ public synchronized PersistentCacheIndexManager getPersistentCacheIndexManager() @NonNull public CollectionReference collection(@NonNull String collectionPath) { checkNotNull(collectionPath, "Provided collection path must not be null."); - ensureClientConfigured(); + clientProvider.ensureConfigured(); return new CollectionReference(ResourcePath.fromString(collectionPath), this); } @@ -458,7 +445,7 @@ public CollectionReference collection(@NonNull String collectionPath) { @NonNull public DocumentReference document(@NonNull String documentPath) { checkNotNull(documentPath, "Provided document path must not be null."); - ensureClientConfigured(); + clientProvider.ensureConfigured(); return DocumentReference.forPath(ResourcePath.fromString(documentPath), this); } @@ -479,7 +466,7 @@ public Query collectionGroup(@NonNull String collectionId) { "Invalid collectionId '%s'. Collection IDs must not contain '/'.", collectionId)); } - ensureClientConfigured(); + clientProvider.ensureConfigured(); return new Query( new com.google.firebase.firestore.core.Query(ResourcePath.EMPTY, collectionId), this); } @@ -501,7 +488,7 @@ public Query collectionGroup(@NonNull String collectionId) { */ private Task runTransaction( TransactionOptions options, Transaction.Function updateFunction, Executor executor) { - ensureClientConfigured(); + clientProvider.ensureConfigured(); // We wrap the function they provide in order to // 1. Use internal implementation classes for Transaction, @@ -515,7 +502,7 @@ private Task runTransaction( updateFunction.apply( new Transaction(internalTransaction, FirebaseFirestore.this))); - return client.transaction(options, wrappedUpdateFunction); + return clientProvider.call(client -> client.transaction(options, wrappedUpdateFunction)); } /** @@ -566,7 +553,7 @@ public Task runTransaction( */ @NonNull public WriteBatch batch() { - ensureClientConfigured(); + clientProvider.ensureConfigured(); return new WriteBatch(this); } @@ -608,10 +595,7 @@ public Task runBatch(@NonNull WriteBatch.Function batchFunction) { @NonNull public Task terminate() { instanceRegistry.remove(this.getDatabaseId().getDatabaseId()); - - // The client must be initialized to ensure that all subsequent API usage throws an exception. - this.ensureClientConfigured(); - return client.terminate(); + return clientProvider.terminate(); } /** @@ -630,13 +614,7 @@ public Task terminate() { */ @NonNull public Task waitForPendingWrites() { - ensureClientConfigured(); - return client.waitForPendingWrites(); - } - - @VisibleForTesting - AsyncQueue getAsyncQueue() { - return asyncQueue; + return clientProvider.call(FirestoreClient::waitForPendingWrites); } /** @@ -646,8 +624,7 @@ AsyncQueue getAsyncQueue() { */ @NonNull public Task enableNetwork() { - ensureClientConfigured(); - return client.enableNetwork(); + return clientProvider.call(FirestoreClient::enableNetwork); } /** @@ -659,8 +636,7 @@ public Task enableNetwork() { */ @NonNull public Task disableNetwork() { - ensureClientConfigured(); - return client.disableNetwork(); + return clientProvider.call(FirestoreClient::disableNetwork); } /** Globally enables / disables Cloud Firestore logging for the SDK. */ @@ -692,15 +668,21 @@ public static void setLoggingEnabled(boolean loggingEnabled) { */ @NonNull public Task clearPersistence() { + return clientProvider.executeIfShutdown( + this::clearPersistence, + executor -> + Tasks.forException( + new FirebaseFirestoreException( + "Persistence cannot be cleared while the firestore instance is running.", + FirebaseFirestoreException.Code.FAILED_PRECONDITION))); + } + + @NonNull + private Task clearPersistence(Executor executor) { final TaskCompletionSource source = new TaskCompletionSource<>(); - asyncQueue.enqueueAndForgetEvenAfterShutdown( + executor.execute( () -> { try { - if (client != null && !client.isTerminated()) { - throw new FirebaseFirestoreException( - "Persistence cannot be cleared while the firestore instance is running.", - Code.FAILED_PRECONDITION); - } SQLitePersistence.clearPersistence(context, databaseId, persistenceKey); source.setResult(null); } catch (FirebaseFirestoreException e) { @@ -709,6 +691,7 @@ public Task clearPersistence() { }); return source.getTask(); } + ; /** * Attaches a listener for a snapshots-in-sync event. The snapshots-in-sync event indicates that @@ -780,9 +763,8 @@ public ListenerRegistration addSnapshotsInSyncListener( */ @NonNull public LoadBundleTask loadBundle(@NonNull InputStream bundleData) { - ensureClientConfigured(); LoadBundleTask resultTask = new LoadBundleTask(); - client.loadBundle(bundleData, resultTask); + clientProvider.procedure(client -> client.loadBundle(bundleData, resultTask)); return resultTask; } @@ -820,9 +802,8 @@ public LoadBundleTask loadBundle(@NonNull ByteBuffer bundleData) { // TODO(b/261013682): Use an explicit executor in continuations. @SuppressLint("TaskMainThread") public @NonNull Task getNamedQuery(@NonNull String name) { - ensureClientConfigured(); - return client - .getNamedQuery(name) + return clientProvider + .call(client -> client.getNamedQuery(name)) .continueWith( task -> { com.google.firebase.firestore.core.Query query = task.getResult(); @@ -847,25 +828,26 @@ public LoadBundleTask loadBundle(@NonNull ByteBuffer bundleData) { */ private ListenerRegistration addSnapshotsInSyncListener( Executor userExecutor, @Nullable Activity activity, @NonNull Runnable runnable) { - ensureClientConfigured(); EventListener eventListener = (Void v, FirebaseFirestoreException error) -> { hardAssert(error == null, "snapshots-in-sync listeners should never get errors."); runnable.run(); }; - AsyncEventListener asyncListener = - new AsyncEventListener(userExecutor, eventListener); - client.addSnapshotsInSyncListener(asyncListener); - return ActivityScope.bind( - activity, - () -> { - asyncListener.mute(); - client.removeSnapshotsInSyncListener(asyncListener); + AsyncEventListener asyncListener = new AsyncEventListener<>(userExecutor, eventListener); + return clientProvider.call( + client -> { + client.addSnapshotsInSyncListener(asyncListener); + return ActivityScope.bind( + activity, + () -> { + asyncListener.mute(); + client.removeSnapshotsInSyncListener(asyncListener); + }); }); } - FirestoreClient getClient() { - return client; + T callClient(Function call) { + return clientProvider.call(call); } DatabaseId getDatabaseId() { diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/FirestoreClientProvider.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/FirestoreClientProvider.java new file mode 100644 index 00000000000..bebd57e3160 --- /dev/null +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/FirestoreClientProvider.java @@ -0,0 +1,158 @@ +// Copyright 2024 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. + +package com.google.firebase.firestore; + +import androidx.annotation.GuardedBy; +import androidx.annotation.VisibleForTesting; +import androidx.core.util.Consumer; +import com.google.android.gms.tasks.Task; +import com.google.firebase.firestore.core.FirestoreClient; +import com.google.firebase.firestore.util.AsyncQueue; +import com.google.firebase.firestore.util.Function; +import java.util.concurrent.Executor; + +/** + * The `FirestoreClientProvider` handles the life cycle of `FirestoreClient`s within a `Firestore` + * instance. + * + * The instantiation of `FirestoreClient` is delayed until there is a need for the client. This + * delay affords changes to configuration through the `Firestore` instance prior to performing a + * query. After instantiation of the `FirestoreClient`, the `Firestore` instance is considered + * configured, and any subsequent attempt to modify configuration will throw anexception. + * + * Access to `FirestoreClient` is via synchronized indirection to ensure the `FirestoreClient` is + * configured, instantiated and current. The `FirestoreClient` should be considered ephemeral, such + * that no reference to `FirestoreClient` should be retained outside of this provider. + * + * All calls to the `FirestoreClient` should be done through access methods in the + * `FirestoreClientProvider`. Access methods take a functional block of code as a parameter. The + * most current `FirestoreClient` instance will be applied to the functional block of code. + * Execution of the functional block of code will be synchronous to ensure the `FirestoreClient` + * instance remains current during execution. + * + * Retaining a reference to `FirestoreClient` outside of `FirestoreClientProvider` risks calling a + * no longer current `FirestoreClient`. Internally, the `FirestoreClient` may self reference, but + * this is with intent to couple internal logic with a specific `FirestoreClient` instance. + * + * The life of a `FirestoreClient` is tightly coupled to the life the internal `AsyncQueue`. The + * `AsyncQueue` is associated with exactly one `FirestoreClient`, and when that `FirestoreClient` is + * terminated, the `AsyncQueue` is shutdown. Internal coupling within `FirestoreClient` relies on + * `AsyncQueue` to stop processing upon shutdown. A terminated `FirestoreClient` will also rely on + * `AsyncQueue` to safeguard against external access. + */ +final class FirestoreClientProvider { + + private final Function clientFactory; + + @GuardedBy("this") + private FirestoreClient client; + + @GuardedBy("this") + private AsyncQueue asyncQueue; + + FirestoreClientProvider(Function clientFactory) { + this.clientFactory = clientFactory; + this.asyncQueue = new AsyncQueue(); + } + + /** + * Indicates whether `FirestoreClient` has been instantiated thereby preventing change to + * configuration. + */ + boolean isConfigured() { + return client != null; + } + + /** + * Prevents further change to configuration, and instantiates the `FirestoreClient` instance + * to be ready for use. + */ + synchronized void ensureConfigured() { + if (!isConfigured()) { + client = clientFactory.apply(asyncQueue); + } + } + + /** + * To facilitate calls to FirestoreClient without risk of FirestoreClient being terminated + * or restarted mid call. + */ + synchronized T call(Function call) { + ensureConfigured(); + return call.apply(client); + } + + /** + * To facilitate calls to FirestoreClient without risk of FirestoreClient being terminated + * or restarted mid call. + */ + synchronized void procedure(Consumer call) { + ensureConfigured(); + call.accept(client); + } + + /** + * Conditional execution based on whether `FirestoreClient` is up and running. + * + * Handling the conditional logic as part of `FirestoreClientProvider` prevents possible race + * condition between condition check and execution functional block of code. + * + * Example, clearing the cache can only be done while `FirestoreClient` is not running. Checking + * whether `FirestoreClient` is running and then performing clearing of cache outside of a + * synchronized code block, risks another thread instantiating `FirestoreClient` after check, but + * before running code to clear cache. + * + * @param callIf Executes if client is shutdown or client hasn't been started yet. + * @param callElse Executes if client is running. + * @return Result of execution. + */ + synchronized T executeIfShutdown( + Function callIf, Function callElse) { + Executor executor = command -> asyncQueue.enqueueAndForgetEvenAfterShutdown(command); + if (client == null || client.isTerminated()) { + return callIf.apply(executor); + } else { + return callElse.apply(executor); + } + } + + /** + * Shuts down the AsyncQueue and releases resources after which no progress will ever be made + * again. + */ + synchronized Task terminate() { + // The client must be initialized to ensure that all subsequent API usage throws an exception. + ensureConfigured(); + + Task terminate = client.terminate(); + + // Will cause the executor to de-reference all threads, the best we can do + asyncQueue.shutdown(); + + return terminate; + } + + /** + * Direct access to internal AsyncQueue. + * + * The danger of using this method is retaining non-synchronized direct access to AsyncQueue. + * + * @return internal AsyncQueue + */ + @VisibleForTesting + AsyncQueue getAsyncQueue() { + return asyncQueue; + } +} diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java index a529acbc8bc..d9927c63489 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/PersistentCacheIndexManager.java @@ -28,11 +28,11 @@ *

To get an instance, call {@link FirebaseFirestore#getPersistentCacheIndexManager()}. */ public final class PersistentCacheIndexManager { - @NonNull private FirestoreClient client; + @NonNull private FirestoreClientProvider client; /** @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY) - PersistentCacheIndexManager(FirestoreClient client) { + PersistentCacheIndexManager(FirestoreClientProvider client) { this.client = client; } @@ -43,7 +43,7 @@ public final class PersistentCacheIndexManager { *

This feature is disabled by default. */ public void enableIndexAutoCreation() { - client.setIndexAutoCreationEnabled(true); + client.procedure(client -> client.setIndexAutoCreationEnabled(true)); } /** @@ -51,7 +51,7 @@ public void enableIndexAutoCreation() { * which have been created by calling {@link #enableIndexAutoCreation()} still take effect. */ public void disableIndexAutoCreation() { - client.setIndexAutoCreationEnabled(false); + client.procedure(client -> client.setIndexAutoCreationEnabled(false)); } /** @@ -59,6 +59,6 @@ public void disableIndexAutoCreation() { * {@link FirebaseFirestore#setIndexConfiguration(String)}, which is deprecated. */ public void deleteAllIndexes() { - client.deleteAllFieldIndexes(); + client.procedure(FirestoreClient::deleteAllFieldIndexes); } } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/Query.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/Query.java index f8ea633c54c..d5fb8a4399b 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/Query.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/Query.java @@ -33,7 +33,6 @@ import com.google.firebase.firestore.core.EventManager.ListenOptions; import com.google.firebase.firestore.core.FieldFilter; import com.google.firebase.firestore.core.FieldFilter.Operator; -import com.google.firebase.firestore.core.ListenerRegistrationImpl; import com.google.firebase.firestore.core.OrderBy; import com.google.firebase.firestore.core.QueryListener; import com.google.firebase.firestore.core.ViewSnapshot; @@ -965,8 +964,7 @@ public Task get(@NonNull Source source) { validateHasExplicitOrderByForLimitToLast(); if (source == Source.CACHE) { return firestore - .getClient() - .getDocumentsFromLocalCache(query) + .callClient(client -> client.getDocumentsFromLocalCache(query)) .continueWith( Executors.DIRECT_EXECUTOR, (Task viewSnap) -> @@ -1182,10 +1180,16 @@ private ListenerRegistration addSnapshotListenerInternal( AsyncEventListener asyncListener = new AsyncEventListener<>(executor, viewListener); - QueryListener queryListener = firestore.getClient().listen(query, options, asyncListener); - return ActivityScope.bind( - activity, - new ListenerRegistrationImpl(firestore.getClient(), queryListener, asyncListener)); + return firestore.callClient( + client -> { + QueryListener queryListener = client.listen(query, options, asyncListener); + return ActivityScope.bind( + activity, + () -> { + asyncListener.mute(); + client.stopListening(queryListener); + }); + }); } private void validateHasExplicitOrderByForLimitToLast() { diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/WriteBatch.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/WriteBatch.java index b8c6e9d1651..af886e759e0 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/WriteBatch.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/WriteBatch.java @@ -190,8 +190,8 @@ public WriteBatch delete(@NonNull DocumentReference documentRef) { public Task commit() { verifyNotCommitted(); committed = true; - if (mutations.size() > 0) { - return firestore.getClient().write(mutations); + if (!mutations.isEmpty()) { + return firestore.callClient(client -> client.write(mutations)); } else { return Tasks.forResult(null); } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ComponentProvider.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ComponentProvider.java index b642569c1fc..ea1654d605f 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ComponentProvider.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ComponentProvider.java @@ -43,6 +43,7 @@ */ public abstract class ComponentProvider { + protected final FirebaseFirestoreSettings settings; private RemoteComponenetProvider remoteProvider = new RemoteComponenetProvider(); private Persistence persistence; private LocalStore localStore; @@ -52,11 +53,15 @@ public abstract class ComponentProvider { @Nullable private IndexBackfiller indexBackfiller; @Nullable private Scheduler garbageCollectionScheduler; + public ComponentProvider(FirebaseFirestoreSettings settings) { + this.settings = settings; + } + @NonNull public static ComponentProvider defaultFactory(@NonNull FirebaseFirestoreSettings settings) { return settings.isPersistenceEnabled() - ? new SQLiteComponentProvider() - : new MemoryComponentProvider(); + ? new SQLiteComponentProvider(settings) + : new MemoryComponentProvider(settings); } /** Configuration options for the component provider. */ @@ -67,7 +72,6 @@ public static final class Configuration { public final DatabaseInfo databaseInfo; public final User initialUser; public final int maxConcurrentLimboResolutions; - public final FirebaseFirestoreSettings settings; public final CredentialsProvider authProvider; public final CredentialsProvider appCheckProvider; @@ -79,7 +83,6 @@ public Configuration( DatabaseInfo databaseInfo, User initialUser, int maxConcurrentLimboResolutions, - FirebaseFirestoreSettings settings, CredentialsProvider authProvider, CredentialsProvider appCheckProvider, @Nullable GrpcMetadataProvider metadataProvider) { @@ -88,7 +91,6 @@ public Configuration( this.databaseInfo = databaseInfo; this.initialUser = initialUser; this.maxConcurrentLimboResolutions = maxConcurrentLimboResolutions; - this.settings = settings; this.authProvider = authProvider; this.appCheckProvider = appCheckProvider; this.metadataProvider = metadataProvider; diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java index 6d411982207..6e2d9b87b84 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java @@ -26,7 +26,6 @@ import com.google.firebase.firestore.EventListener; import com.google.firebase.firestore.FirebaseFirestoreException; import com.google.firebase.firestore.FirebaseFirestoreException.Code; -import com.google.firebase.firestore.FirebaseFirestoreSettings; import com.google.firebase.firestore.LoadBundleTask; import com.google.firebase.firestore.TransactionOptions; import com.google.firebase.firestore.auth.CredentialsProvider; @@ -84,7 +83,6 @@ public final class FirestoreClient { public FirestoreClient( final Context context, DatabaseInfo databaseInfo, - FirebaseFirestoreSettings settings, CredentialsProvider authProvider, CredentialsProvider appCheckProvider, AsyncQueue asyncQueue, @@ -108,7 +106,7 @@ public FirestoreClient( try { // Block on initial user being available User initialUser = Tasks.await(firstUser.getTask()); - initialize(context, initialUser, settings, componentProvider, metadataProvider); + initialize(context, initialUser, componentProvider, metadataProvider); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } @@ -181,11 +179,7 @@ public QueryListener listen( /** Stops listening to a query previously listened to. */ public void stopListening(QueryListener listener) { - // Checks for terminate but does not raise error, allowing it to be a no-op if client is already - // terminated. - if (this.isTerminated()) { - return; - } + // `enqueueAndForget` will no-op if client is already terminated. asyncQueue.enqueueAndForget(() -> eventManager.removeQueryListener(listener)); } @@ -270,7 +264,6 @@ public Task waitForPendingWrites() { private void initialize( Context context, User user, - FirebaseFirestoreSettings settings, ComponentProvider provider, GrpcMetadataProvider metadataProvider) { // Note: The initialization work must all be synchronous (we can't dispatch more work) since @@ -285,7 +278,6 @@ private void initialize( databaseInfo, user, MAX_CONCURRENT_LIMBO_RESOLUTIONS, - settings, authProvider, appCheckProvider, metadataProvider); @@ -360,10 +352,7 @@ public void deleteAllFieldIndexes() { } public void removeSnapshotsInSyncListener(EventListener listener) { - // Checks for shutdown but does not raise error, allowing remove after shutdown to be a no-op. - if (isTerminated()) { - return; - } + // `enqueueAndForget` will no-op if client is already terminated. asyncQueue.enqueueAndForget(() -> eventManager.removeSnapshotsInSyncListener(listener)); } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ListenerRegistrationImpl.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ListenerRegistrationImpl.java deleted file mode 100644 index b949319962f..00000000000 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/ListenerRegistrationImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 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. - -package com.google.firebase.firestore.core; - -import com.google.firebase.firestore.ListenerRegistration; - -/** Implements the ListenerRegistration interface by removing a query from the listener. */ -public class ListenerRegistrationImpl implements ListenerRegistration { - - private final FirestoreClient client; - - /** The internal query listener object that is used to unlisten from the query. */ - private final QueryListener queryListener; - - /** The event listener for the query that raises events asynchronously. */ - private final AsyncEventListener asyncEventListener; - - public ListenerRegistrationImpl( - FirestoreClient client, - QueryListener queryListener, - AsyncEventListener asyncEventListener) { - this.client = client; - this.queryListener = queryListener; - this.asyncEventListener = asyncEventListener; - } - - @Override - public void remove() { - asyncEventListener.mute(); - client.stopListening(queryListener); - } -} diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/MemoryComponentProvider.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/MemoryComponentProvider.java index a235c027e22..431423eeba8 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/MemoryComponentProvider.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/MemoryComponentProvider.java @@ -39,6 +39,10 @@ */ public class MemoryComponentProvider extends ComponentProvider { + public MemoryComponentProvider(FirebaseFirestoreSettings settings) { + super(settings); + } + @Override @Nullable protected Scheduler createGarbageCollectionScheduler(Configuration configuration) { @@ -73,10 +77,10 @@ private boolean isMemoryLruGcEnabled(FirebaseFirestoreSettings settings) { @Override protected Persistence createPersistence(Configuration configuration) { - if (isMemoryLruGcEnabled(configuration.settings)) { + if (isMemoryLruGcEnabled(settings)) { LocalSerializer serializer = new LocalSerializer(getRemoteSerializer()); LruGarbageCollector.Params params = - LruGarbageCollector.Params.WithCacheSizeBytes(configuration.settings.getCacheSizeBytes()); + LruGarbageCollector.Params.WithCacheSizeBytes(settings.getCacheSizeBytes()); return MemoryPersistence.createLruGcMemoryPersistence(params, serializer); } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/SQLiteComponentProvider.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/SQLiteComponentProvider.java index 94a374bd42b..66b5b5cc30a 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/core/SQLiteComponentProvider.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/core/SQLiteComponentProvider.java @@ -14,6 +14,7 @@ package com.google.firebase.firestore.core; +import com.google.firebase.firestore.FirebaseFirestoreSettings; import com.google.firebase.firestore.local.IndexBackfiller; import com.google.firebase.firestore.local.LocalSerializer; import com.google.firebase.firestore.local.LruDelegate; @@ -25,6 +26,10 @@ /** Provides all components needed for Firestore with SQLite persistence. */ public class SQLiteComponentProvider extends MemoryComponentProvider { + public SQLiteComponentProvider(FirebaseFirestoreSettings settings) { + super(settings); + } + @Override protected Scheduler createGarbageCollectionScheduler(Configuration configuration) { LruDelegate lruDelegate = ((SQLitePersistence) getPersistence()).getReferenceDelegate(); @@ -41,7 +46,7 @@ protected IndexBackfiller createIndexBackfiller(Configuration configuration) { protected Persistence createPersistence(Configuration configuration) { LocalSerializer serializer = new LocalSerializer(getRemoteSerializer()); LruGarbageCollector.Params params = - LruGarbageCollector.Params.WithCacheSizeBytes(configuration.settings.getCacheSizeBytes()); + LruGarbageCollector.Params.WithCacheSizeBytes(settings.getCacheSizeBytes()); return new SQLitePersistence( configuration.context, configuration.databaseInfo.getPersistenceKey(), diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/FirebaseFirestoreIntegrationTestFactory.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/FirebaseFirestoreIntegrationTestFactory.java index e9dc7b4a5d7..a8afe3d52d3 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/FirebaseFirestoreIntegrationTestFactory.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/FirebaseFirestoreIntegrationTestFactory.java @@ -30,7 +30,6 @@ import com.google.firebase.firestore.remote.RemoteComponenetProvider; import com.google.firebase.firestore.testutil.EmptyAppCheckTokenProvider; import com.google.firebase.firestore.testutil.EmptyCredentialsProvider; -import com.google.firebase.firestore.util.AsyncQueue; import com.google.firestore.v1.FirestoreGrpc; import com.google.firestore.v1.ListenRequest; import com.google.firestore.v1.ListenResponse; @@ -125,7 +124,6 @@ public FirebaseFirestoreIntegrationTestFactory(DatabaseId databaseId) { "k", new EmptyCredentialsProvider(), new EmptyAppCheckTokenProvider(), - new AsyncQueue(), this::componentProvider, null, instanceRegistry, diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/MemorySpecTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/MemorySpecTest.java index 2272aa6fc77..d07e3c6c886 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/MemorySpecTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/MemorySpecTest.java @@ -14,6 +14,7 @@ package com.google.firebase.firestore.spec; +import com.google.firebase.firestore.FirebaseFirestoreSettings; import com.google.firebase.firestore.core.ComponentProvider; import com.google.firebase.firestore.core.MemoryComponentProvider; import com.google.firebase.firestore.local.LocalSerializer; @@ -43,7 +44,7 @@ protected MemoryComponentProvider initializeComponentProvider( ComponentProvider.Configuration configuration, boolean useEagerGc) { MemoryComponentProvider provider = - new MemoryComponentProvider() { + new MemoryComponentProvider(new FirebaseFirestoreSettings.Builder().build()) { @Override protected Persistence createPersistence(Configuration configuration) { if (useEagerGc) { diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SQLiteSpecTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SQLiteSpecTest.java index 8d717a506fd..959a106ddff 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SQLiteSpecTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SQLiteSpecTest.java @@ -14,6 +14,7 @@ package com.google.firebase.firestore.spec; +import com.google.firebase.firestore.FirebaseFirestoreSettings; import com.google.firebase.firestore.core.ComponentProvider; import com.google.firebase.firestore.core.SQLiteComponentProvider; import com.google.firebase.firestore.remote.RemoteComponenetProvider; @@ -39,7 +40,8 @@ protected SQLiteComponentProvider initializeComponentProvider( RemoteComponenetProvider remoteProvider, ComponentProvider.Configuration configuration, boolean garbageCollectionEnabled) { - SQLiteComponentProvider provider = new SQLiteComponentProvider(); + SQLiteComponentProvider provider = + new SQLiteComponentProvider(new FirebaseFirestoreSettings.Builder().build()); provider.setRemoteProvider(remoteProvider); provider.initialize(configuration); return provider; diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java index ce4749090c3..8814b42baef 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java @@ -41,7 +41,6 @@ import com.google.firebase.firestore.EventListener; import com.google.firebase.firestore.FirebaseFirestore; import com.google.firebase.firestore.FirebaseFirestoreException; -import com.google.firebase.firestore.FirebaseFirestoreSettings; import com.google.firebase.firestore.ListenSource; import com.google.firebase.firestore.LoadBundleTask; import com.google.firebase.firestore.auth.User; @@ -330,7 +329,6 @@ private void initClient() { databaseInfo, currentUser, maxConcurrentLimboResolutions, - new FirebaseFirestoreSettings.Builder().build(), new EmptyCredentialsProvider(), new EmptyAppCheckTokenProvider(), null);