1

According to the description here I'd expect snapshot.metadata.fromCache to be true when the document being listened to is modified in the same client as the listener, e.g.

  1. The local .update(...) immediately triggers the onSnapshot handler (and is handed a snapshot with fromCache set to true)
  2. The data is sent to the db
  3. The firebase client receives the return message and does nothing (does not trigger a onSnapshot) because the server data agrees with the cache.

Ergo, fromCache should always be true when onSnapshot is trigged by a local change.

However, this only appears to be the case on the first two to three onSnapshot responses, after-which fromCache appears to always be false.

Example Test:


// ... firestore init w/ a test project and with persistence enabled. 

const db = firebase.firestore();
db.settings({
    ignoreUndefinedProperties:true
})

// Where "_test" is an empty collection with full allowance for read/write
await db.collection("_test").doc("deleteme").set({}); 

let doc = db.collection("_test").doc("deleteme") 

// ?! Expect this to be true but after the first one or two occurrences it is always false.
doc.onSnapshot(s=>{console.log("test snapshot change from cache? ",s.metadata.fromCache)}) 

let x = 0;      
let poke = async ()=>{
  doc.update({
    n:Math.random()
  })
  await sleep(3000); // generic custom delay 
  window.requestAnimationFrame(poke)
};
window.requestAnimationFrame(poke);

Edit: The question here is due to similarly missing knowledge as in this other question: Is Firestore onSnapshot update event due to local client Set?

Ian
  • 592
  • 5
  • 21
  • I suggest editing the question to be more clear about the description of the behavior of your program over time, in response to **specific** inputs you give it. Saying "on the first two or three onSnapshot response" is not very specific. There should be enough information in the question that anyone can reproduce your specific observations. – Doug Stevenson Aug 24 '20 at 19:49
  • 1
    "Ergo, `fromCache` should always be true when onSnapshot is trigged by a local change." I don't think that is how it is defined; it is more whether the client knows if its local snapshot is up-to-date with the server. You maybe confusing `fromCache` with `hasPendingWrites`. And indeed as Doug commented: it'd be useful to see what you're trying to accomplish with this test, as there may be other ways to do what you want. Also see: [what is the XY problem?](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Frank van Puffelen Aug 24 '20 at 19:50
  • "You maybe confusing fromCache with hasPendingWrites" [*facepalm*] That's it! I have to say though that there is no small overlap in the Venn diagram of situations that fit the descriptions for both of those items. "...the snapshot contains the result of local writes (for example, set() or update() calls) that have not yet been committed to the backend" vs. " the snapshot was created from cached data" – Ian Aug 24 '20 at 20:42
  • @DougStevenson, I've made small edits to hopefully clarify that the phenomena of note in the provided code is that over successive writes to a document, fromCache becomes false, and stays that way, and that this is a significant discrepancy from expected behavior (that it is never false) regardless of the number of the specific number times that fromCache is initially true. – Ian Aug 24 '20 at 21:40
  • @FrankvanPuffelen The example highlights a break between my understanding of the tool (and/or my reading of its documentation) and its actual behavior. I'm trying to be better able to reason opportunities and pitfalls the tool presents generally. – Ian Aug 24 '20 at 21:41
  • The edits don't really clarify for me. So, how are you able to track which update was responsible for which callback? Your callback simply looks at fromCache. It doesn't at all illustrate what change triggered it. – Doug Stevenson Aug 24 '20 at 22:08
  • 1
    The `fromCache` name is indeed confusing. I understand `fromCache` as "may not contain all data from the server yet", while the `isPending` is "may contain data that the server doesn't know about yet". – Frank van Puffelen Aug 24 '20 at 22:10
  • @FrankvanPuffelen, I think I follow. So in my code above, where repeat doc:set() trigger onSnapsot updates where the fromCache eventually start coming back false, false, false... Is Firebase _not_ kicking off an instantaneous onSnapshot update locally after each write? – Ian Aug 25 '20 at 00:18
  • @DougStevenson Are you asking how we know which specific call to `doc.update(...)` triggered which specific call to the `onSnapshot` handler? If so, I'm not sure I see the relevance... My thinking is this: If Firebase calls `doc.onSnapshot(...)` immediately, locally, when `doc.update(..)` is called and provides the snapshot handler with local data, e.g. not-from the server, e.g. from the local cache, and the subsequent responses from the server are always swallowed, then every response is essentially from the cache, and so I here expect `fromCache` to be always `true` and never `false`. – Ian Aug 25 '20 at 00:28
  • It's relevant, because the listener can get invoked multiple times per update. I suggest adding some more logging including the contents of the snapshot at each turn so you can better observe the behavior. This is what I was trying to get you to do in my first comment. Right now, we just can't see what exactly you see, and that's a problem for us to be able to understand what **exactly** is happening. – Doug Stevenson Aug 25 '20 at 00:37
  • Sorry if I'm slow, but the snapshots are very far from shallow and contain everything from garbage collection flags to local storage keys and server settings. I assume you're not asking for a rundown on the entirety of each snapshot... – Ian Aug 25 '20 at 01:39
  • It might be easier if you simplify your example using small documents in a testing area that you can isolate for debugging. It might be helpful to read the advice in: https://stackoverflow.com/help/minimal-reproducible-example – Doug Stevenson Aug 25 '20 at 15:44
  • I clarified the example to make the presumed existence of a small test document explicit, and to clarify that the code is presumed to run on a test project / database instance. (I also removed the sloppy Typescript copy on my part.) Regardless, I feel the test example (even in its original form) was sufficient to elucidate the problem given that it revealed a significant and primary misunderstanding on my part regarding the distinction between `fromCache` and `isPending` as described by @FrankvanPuffelen. – Ian Aug 26 '20 at 02:16
  • 1
    @FrankvanPuffelen, if you submit your notes about the distinction between `fromCache` and `isPending` as an answer I can select it as correct. – Ian Aug 26 '20 at 02:23

1 Answers1

3

Ergo, fromCache should always be true when onSnapshot is trigged by a local change.

I don't think that is how it is defined; it is more whether the client knows if its local snapshot is up-to-date with the server.

You maybe confusing fromCache with hasPendingWrites. The fromCache name is indeed confusing. I understand fromCache as "may not contain all data from the server yet", while the isPending is "may contain data that the server doesn't know about yet".

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • So this makes more sense and is helpful. Thinking about it a little more though I feel like I’m missing some nuance; In my mind, given that it’s possible that server data can change from elsewhere at any time, it’s _always_ the case that the data in hand may not include all data from the server, even if it just came from there. So would it also be accurate to say that fromCache being true indicates that ‘a server response is expected though it may be swallowed if there is no difference’. – Ian Aug 26 '20 at 13:03
  • 2
    "fromCache" flips from true to false once the backend has send us all documents that matched the query at the time when the query was issued. It indicates that the server has caught up with our request. Afterwards, the server will send us updates in real-time and we don't flip back to "fromCache: true" (unless the network connection is lost). – Sebastian Schmidt Aug 26 '20 at 15:08
  • 2
    @Ian Sebastian is one of the engineers on Firebase who works on this SDK, and I asked him to chime in. ;-) – Frank van Puffelen Aug 26 '20 at 15:11