5
votes

Just starting to explore Firestore storage and first thing to do - read a simple small document in my Android app by document key (authenticated with Google, but probably that's not important). Here is a snippet:

public void readDoc(final String key) {
  final long start = System.currentTimeMillis();
  docsCollection.document(key).get().addOnCompleteListener(
      new OnCompleteListener<DocumentSnapshot>() {
        @Override public void onComplete(@NonNull Task<DocumentSnapshot> task) {
          long end = System.currentTimeMillis();
          Log.d("FirestoreStorage", "get() time: " + (end - start));
        }
      });
}

Here is what I see in LogCat:

10-10 22:30:06.026 D/FirestoreStorage: get() time: 1666
10-10 22:30:08.199 D/FirestoreStorage: get() time: 264

The first read is always very slow, subsequent reads are about 200ms. The document is really small, currently it's just 4 properties and only one (int) is non-null, so the size is not an issue. Running the app on a real phone, Nexus 6 on Android 7.1

Question: what am I doing wrong? I'm basically using a sample from "Getting data" of the How-to Guide.

A read like this should take 0 milliseconds. If there's no workaround, I guess I have to give up the idea of Realtime storage as the only storage for the app and get back to plain SQLite and use Firebase/Firestore as a separate cloud storage.

UPDATE Starting from version 16.0.0 DocumentReference.get() and Query.get() have a new parameter "source" that allows to control where the data is read from - only server, only cache or try server then cache.

PS Firestore storage initialization and corresponding logs, sorry not 500ms but 350, it's different, sometimes 400, sometimes 300:

  public FirestoreStorage(String userRef) {
    Log.i(TAG, "User ref: \"" + userRef + "\"");
    db = FirebaseFirestore.getInstance();
    Log.i(TAG, "Is persistence enabled: " + db.getFirestoreSettings().isPersistenceEnabled());
    DocumentReference userDoc = db.collection("users").document(userRef);
    prefsCollection = userDoc.collection("prefs");
    prefsCollection.addSnapshotListener(
        Executors.newFixedThreadPool(2),
        new EventListener<QuerySnapshot>() {
          @Override
          public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
            Log.d(TAG, "Prefs.onEvent");
         }
    });
    Log.i(TAG, "Snapshot listener added");

    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

Logs:

10-11 23:11:42.382 I/FirestoreStorage: User ref: "<cut>"
10-11 23:11:42.474 I/FirestoreStorage: Is persistence enabled: true
10-11 23:11:42.496 I/FirestoreStorage: Snapshot listener added
10-11 23:11:42.855 D/FirestoreStorage: Prefs.onEvent
1
How would you expect a network call to return the data in 0 milliseconds? - J. Doe
I don't :) I didn't expect the network to be involved there, I thought it would just return me the version it has cached locally. If that's what I get when I attach a listener, why would get() be different? That's very confusing. Ideally I would prefer a flag to say whether I want to try and get actual data from server or I just want local data fast - smok

1 Answers

6
votes

These get() requests are reading the data from the Cloud Firestore backend, over the network, so they'll necessarily be much slower than SQLite which is just reading locally from disk. The first read is also likely to be slower than subsequent ones since it has to initiate the network channel to the backend. We'll look at improving performance over time, but you can't expect 0 ms if you're retrieving data over the network.

You may want to enable offline persistence which would enable local caching of data you've previously read. Note though that get() calls will still try to hit the network first to give you as up-to-date data as possible. If you use addSnapshotListener() instead, we'll call you immediately with the cached data, without waiting for the network.