Skip to content

Commit

Permalink
feat(firestore): add support for multiple database instances (#11310)
Browse files Browse the repository at this point in the history
  • Loading branch information
russellwheatley committed Aug 22, 2023
1 parent da1eb58 commit ce6efcc
Show file tree
Hide file tree
Showing 30 changed files with 3,330 additions and 113 deletions.
2 changes: 1 addition & 1 deletion melos.yaml
Expand Up @@ -160,7 +160,7 @@ scripts:
test:e2e:cloud_firestore:
run: |
cd packages/cloud_firestore/cloud_firestore/example
flutter test integration_test/cloud_firestore_e2e_test.dart
flutter test integration_test/e2e_test.dart
description: |
Run all e2e tests for cloud_firestore.
Expand Down
@@ -0,0 +1,25 @@
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.firebase.firestore;

import com.google.firebase.firestore.FirebaseFirestore;

public class FlutterFirebaseFirestoreExtension {
private final FirebaseFirestore instance;
private final String databaseURL;

public FlutterFirebaseFirestoreExtension(FirebaseFirestore instance, String databaseURL) {
this.instance = instance;
this.databaseURL = databaseURL;
}

public FirebaseFirestore getInstance() {
return instance;
}

public String getDatabaseURL() {
return databaseURL;
}
}
Expand Up @@ -72,8 +72,18 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) {
writeDouble(stream, ((GeoPoint) value).getLongitude());
} else if (value instanceof DocumentReference) {
stream.write(DATA_TYPE_DOCUMENT_REFERENCE);
writeValue(stream, ((DocumentReference) value).getFirestore().getApp().getName());
FirebaseFirestore firestore = ((DocumentReference) value).getFirestore();
String appName = firestore.getApp().getName();
writeValue(stream, appName);
writeValue(stream, ((DocumentReference) value).getPath());
String databaseURL;
// There is no way of getting database URL from Firebase android SDK API so we cache it ourselves
synchronized (FlutterFirebaseFirestorePlugin.firestoreInstanceCache) {
databaseURL =
FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(firestore)
.getDatabaseURL();
}
writeValue(stream, databaseURL);
} else if (value instanceof DocumentSnapshot) {
writeDocumentSnapshot(stream, (DocumentSnapshot) value);
} else if (value instanceof QuerySnapshot) {
Expand Down Expand Up @@ -277,20 +287,22 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) {

private FirebaseFirestore readFirestoreInstance(ByteBuffer buffer) {
String appName = (String) readValue(buffer);
String databaseURL = (String) readValue(buffer);
FirebaseFirestoreSettings settings = (FirebaseFirestoreSettings) readValue(buffer);

synchronized (FlutterFirebaseFirestorePlugin.firestoreInstanceCache) {
if (FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(appName)
if (FlutterFirebaseFirestorePlugin.getFirestoreInstanceByNameAndDatabaseUrl(
appName, databaseURL)
!= null) {
return FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(appName);
return FlutterFirebaseFirestorePlugin.getFirestoreInstanceByNameAndDatabaseUrl(
appName, databaseURL);
}

FirebaseApp app = FirebaseApp.getInstance(appName);
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);

FirebaseFirestore firestore = FirebaseFirestore.getInstance(app, databaseURL);
firestore.setFirestoreSettings(settings);

FlutterFirebaseFirestorePlugin.setCachedFirebaseFirestoreInstanceForKey(firestore, appName);
FlutterFirebaseFirestorePlugin.setCachedFirebaseFirestoreInstanceForKey(
firestore, databaseURL);
return firestore;
}
}
Expand Down
Expand Up @@ -54,8 +54,8 @@

public class FlutterFirebaseFirestorePlugin
implements FlutterFirebasePlugin, MethodCallHandler, FlutterPlugin, ActivityAware {
protected static final HashMap<String, FirebaseFirestore> firestoreInstanceCache =
new HashMap<>();
protected static final HashMap<FirebaseFirestore, FlutterFirebaseFirestoreExtension>
firestoreInstanceCache = new HashMap<>();

public static final String DEFAULT_ERROR_CODE = "firebase_firestore";

Expand All @@ -79,27 +79,41 @@ public class FlutterFirebaseFirestorePlugin
public static final Map<Integer, DocumentSnapshot.ServerTimestampBehavior>
serverTimestampBehaviorHashMap = new HashMap<>();

protected static FirebaseFirestore getCachedFirebaseFirestoreInstanceForKey(String key) {
protected static FlutterFirebaseFirestoreExtension getCachedFirebaseFirestoreInstanceForKey(
FirebaseFirestore firestore) {
synchronized (firestoreInstanceCache) {
return firestoreInstanceCache.get(key);
return firestoreInstanceCache.get(firestore);
}
}

protected static void setCachedFirebaseFirestoreInstanceForKey(
FirebaseFirestore firestore, String key) {
FirebaseFirestore firestore, String databaseURL) {
synchronized (firestoreInstanceCache) {
FirebaseFirestore existingInstance = firestoreInstanceCache.get(key);
FlutterFirebaseFirestoreExtension existingInstance = firestoreInstanceCache.get(firestore);
if (existingInstance == null) {
firestoreInstanceCache.put(key, firestore);
firestoreInstanceCache.put(
firestore, new FlutterFirebaseFirestoreExtension(firestore, databaseURL));
}
}
}

private static void destroyCachedFirebaseFirestoreInstanceForKey(String key) {
protected static FirebaseFirestore getFirestoreInstanceByNameAndDatabaseUrl(
String appName, String databaseURL) {
for (Map.Entry<FirebaseFirestore, FlutterFirebaseFirestoreExtension> entry :
firestoreInstanceCache.entrySet()) {
if (entry.getValue().getInstance().getApp().getName().equals(appName)
&& entry.getValue().getDatabaseURL().equals(databaseURL)) {
return entry.getKey();
}
}
return null;
}

private static void destroyCachedFirebaseFirestoreInstanceForKey(FirebaseFirestore firestore) {
synchronized (firestoreInstanceCache) {
FirebaseFirestore existingInstance = firestoreInstanceCache.get(key);
FlutterFirebaseFirestoreExtension existingInstance = firestoreInstanceCache.get(firestore);
if (existingInstance != null) {
firestoreInstanceCache.remove(key);
firestoreInstanceCache.remove(firestore);
}
}
}
Expand Down Expand Up @@ -511,7 +525,7 @@ private Task<Void> terminate(Map<String, Object> arguments) {
FirebaseFirestore firestore =
(FirebaseFirestore) Objects.requireNonNull(arguments.get("firestore"));
Tasks.await(firestore.terminate());
destroyCachedFirebaseFirestoreInstanceForKey(firestore.getApp().getName());
destroyCachedFirebaseFirestoreInstanceForKey(firestore);

taskCompletionSource.setResult(null);
} catch (Exception e) {
Expand Down Expand Up @@ -742,12 +756,12 @@ public Task<Void> didReinitializeFirebaseCore() {
() -> {
try {
// Context is ignored by API so we don't send it over even though annotated non-null.

for (FirebaseApp app : FirebaseApp.getApps(null)) {
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
for (Map.Entry<FirebaseFirestore, FlutterFirebaseFirestoreExtension> entry :
firestoreInstanceCache.entrySet()) {
FirebaseFirestore firestore = entry.getKey();
Tasks.await(firestore.terminate());
FlutterFirebaseFirestorePlugin.destroyCachedFirebaseFirestoreInstanceForKey(
app.getName());
firestore);
}

removeEventListeners();
Expand Down
Expand Up @@ -21,6 +21,7 @@ import 'snapshot_metadata_e2e.dart';
import 'timestamp_e2e.dart';
import 'transaction_e2e.dart';
import 'write_batch_e2e.dart';
import 'second_database.dart';

bool kUseFirestoreEmulator = true;

Expand Down Expand Up @@ -52,5 +53,6 @@ void main() {
runWriteBatchTests();
runLoadBundleTests();
runSecondAppTests();
runSecondDatabaseTests();
});
}

0 comments on commit ce6efcc

Please sign in to comment.