1

As I know Firebase on Flutter will automatically read from cache first but I noticed while development that an app just play around with almost 1 stream that has 10 or 15 document I have reach reads above 4000 reads! By this way if 10 users has used my app I will pay all I have, so I have recheck every query, snapshot and put this code to know

print(itemDoc.metadata.isFromCache ? "itemDoc NOT FROM NETWORK" : "itemDoc FROM NETWORK");

Edit write the code and more explain

I noticed this issue appear in IOS when I update,add,delete a document console print all stored documents.

I have Main Stream that I get all users lists then every list I create stream for it to listen list's items

userListsStream(uid){
    Stream<QuerySnapshot> shoppingListsStream = 
    Firestore.instance.collection('lists').where('owner', arrayContains: uid).snapshots();

    shoppingListsStream.listen(
        (QuerySnapshot listsQuery) async {

          List<DocumentSnapshot> listsDocs = listsQuery.documents;
          if (listsDocs.length != 0) {
            //? foreach on lists Documents
            listsDocs.forEach(
              (DocumentSnapshot listDoc) async {
              print(listDoc.metadata.isFromCache ? "listDoc NOT FROM 
              NETWORK" : "listDoc FROM NETWORK");
              listItemsStream(listDoc.documentID);
         }
        )
       }
    })
}

listItemsStream(lid){
shoppingItemsRef =  Firestore.instance.collection('lists').document(lid).collection('items');

shoppingItemsRef.snapshots().listen(
        (QuerySnapshot itemsQuery) async {
          List<DocumentSnapshot> itemsDocs = itemsQuery.documents;
          if (itemsDocs.length != 0) {
            itemsDocs.forEach(
              (DocumentSnapshot itemDoc) async {
              print(itemDoc.metadata.isFromCache ? "itemDoc NOT FROM 
              NETWORK" : "itemDoc FROM NETWORK");
             }
           )
          }
}

those two methods in Provider that I call the main function in Home.dart initState

 @override
  void initState() {
    Provider.of<ListsProvider>(context, listen: false)
        .userListsStream(uid);
  }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Mahmoud Niypoo
  • 1,598
  • 5
  • 24
  • 41
  • Related question: https://stackoverflow.com/questions/57386003/how-to-cache-firebase-data-in-flutter – dev0experiment Jan 07 '20 at 12:25
  • 1
    Please edit your question to include the minimal, complete code that produces the output. Right now it's not clear how you're reading, which makes it hard to reason on. – Frank van Puffelen Jan 07 '20 at 14:28
  • @FrankvanPuffelen I have added some extra data if you want any other data please let me know – Mahmoud Niypoo Jan 07 '20 at 18:28
  • In your code in the initial call to your listener the documents should all come from the server. But in updates, only the updates should come from the server, and any unmodified documents should come from the cache. Is that not what you're seeing? – Frank van Puffelen Jan 07 '20 at 18:30
  • @FrankvanPuffelen That what I'm seeing in android device but in IOS no every update I make all data re-load from server – Mahmoud Niypoo Jan 07 '20 at 18:50
  • I'm unable to reproduce this problem in an iOS simulator with `messagesSnapshot.data.documents.map((DocumentSnapshot doc) { print(doc.metadata.isFromCache ? "doc FROM SERVER" : "doc from CACHE");...`. The first set of data it prints `flutter: doc FROM SERVER`. Then when I add a new document, or update an existing one, it's all `flutter: doc from CACHE`. I don't see how what I do is different from what you seem to be doing. – Frank van Puffelen Jan 07 '20 at 21:45
  • 1
    Woops, never mind... I had inverted my print statements. You're right, for me too it's printing `FROM SERVER` all the time. That definitely seems wrong. Let me see if I can find something about why that's happening. – Frank van Puffelen Jan 07 '20 at 21:55
  • 1
    And I just learned that `isFromCache` works completely different from what I thought. I'm going to run a few more tests, and will then write up an answer. – Frank van Puffelen Jan 07 '20 at 22:29

1 Answers1

6

The isFromCache flag indicates whether the document is guaranteed to be up to date with the server, or that it may be a stale result from the cache. If it's false that doesn't necessarily means the document was read from the server, but that you can be guaranteed that the document is up to date with the server.

To show what this means, I can this snippet

var snapshotsStream = Firestore.instance.collection("chat").orderBy("timestamp", descending: true).limit(3).snapshots();
snapshotsStream.listen((querySnapshot) {
  print("We got a new QuerySnapshot");
  querySnapshot.documents.forEach((doc) {
    print(doc.documentID+": "+(doc.metadata.isFromCache ? "from CACHE " : "from SERVER "));
  });
  querySnapshot.documentChanges.forEach((docChange) {
    print(docChange.type.toString()+(docChange.document.metadata.isFromCache ? "doc from CACHE " : "doc from SERVER "));
  });
}, onError: (error) {
  print(error);
});

The output I get initially is:

flutter: We got a new QuerySnapshot

flutter: 5nAr5pYgwXJ0n3pWZkLw: from SERVER

flutter: moysGY7Ea7TCf28fcEVC: from SERVER

flutter: PuNnPaiLMIE7704R9NuL: from SERVER

flutter: 5nAr5pYgwXJ0n3pWZkLw: DocumentChangeType.addeddoc from SERVER

flutter: moysGY7Ea7TCf28fcEVC: DocumentChangeType.addeddoc from SERVER

flutter: PuNnPaiLMIE7704R9NuL: DocumentChangeType.addeddoc from SERVER

Then when I make a change on the server, it prints:

flutter: We got a new QuerySnapshot

flutter: 5nAr5pYgwXJ0n3pWZkLw: from SERVER

flutter: moysGY7Ea7TCf28fcEVC: from SERVER

flutter: PuNnPaiLMIE7704R9NuL: from SERVER

flutter: 5nAr5pYgwXJ0n3pWZkLw: DocumentChangeType.modifieddoc from SERVER

So using the isFromCache property is not meant to determine whether the document was a charged read on the server, but whether the document is guaranteed to be up to date with the server.

To know what documents have changed, you can iterate over the documentChanged collection, as shown in the code above.


As to having more reads than you expected, one of the more common causes of this is keeping Firestore panel open in the Firebase console. Reads performed by the console are charged towards your project. For more on this, see:

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807