40

I read somewhere a claim that Firebase caches the data.

So I ran this test that reads a semi large volume of data (about 400KB).

Here is the relevant code.

firebase.initializeApp(config);

var counter = 0;

console.time('firebase answered in');
firebase.database().ref('texts').once('value',onData);

function onData(snapshot){
  console.timeEnd('firebase answered in');

  counter ++;
  if(counter > 20) return;

  setTimeout(function(){
    console.time('firebase answered in');
    firebase.database().ref('texts').once('value',onData);
  },2000);
}

As you can see, the first time it loads data it takes a while, and subsequent calls take much less time.

firebase answered in: 1279.422ms

firebase answered in: 236.378ms

firebase answered in: 228.595ms

firebase answered in: 202.700ms

firebase answered in: 208.371ms

firebase answered in: 214.807ms

etc

But, still, if the data is cached locally ~200ms (sometimes more) seems like a lot of time to access local data. Enough for the user to perceive a delay when rendering the UI.

So is Firebase caching the data? What is happening in those ~200ms?

Pier
  • 10,298
  • 17
  • 67
  • 113

1 Answers1

58

Firebase caches the data (in memory) for as long as there is an active listener for that data.

Since your code uses only once() listener, the listener is detached immediately when the data is received (before your callback is invoked) and the data is cleared from the cache. That means that is has to get the data from the servers for each once(), which apparently is a 200ms round-trip in your case. The first load is slower, because the connection is likely established in that call.

A quick trick to verify this is to add a permanent listener before starting your loop:

firebase.initializeApp(config);

var counter = 0;

console.time('firebase answered in');
firebase.database().ref('texts').on('value',function() {});
firebase.database().ref('texts').once('value',onData);

function onData(snapshot){
  console.timeEnd('firebase answered in');

  counter ++;
  if(counter > 20) return;

  setTimeout(function(){
    console.time('firebase answered in');
    firebase.database().ref('texts').once('value',onData);
  },2000);
}

With that simple change, the logging turns into:

firebase answered in: 580.575ms

firebase answered in: 4.040ms

firebase answered in: 7.569ms

firebase answered in: 5.739ms

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Good to know. It would be nice if the data remained cached for a bit longer since sometimes the same data will be needed some seconds later. Where can I make a feature request? – Pier Jul 17 '16 at 18:23
  • 1
    Also, do you think using an empty function to keep the cache active is a viable strategy? – Pier Jul 17 '16 at 18:32
  • Keeping an empty listener is what `keepSynced(true)` in the native mobile SDKs does. – Frank van Puffelen Jul 18 '16 at 02:49
  • So you'd recommend it for JS? – Pier Jul 18 '16 at 03:38
  • 4
    All I did was explain the behavior. Whether that is what you need is for you to decide. In general I'm not a big fan of `once()` in general, it's typically an anti-pattern when using Firebase. – Frank van Puffelen Jul 18 '16 at 03:52
  • My question is rather if using an empty listener would be a problem or not with FB. I don't use `once()` myself, but in React I have to do `on()` and `off()` when the components mount and unmount. – Pier Jul 18 '16 at 03:56
  • 1
    That sounds like a perfectly valid flow. We recommend similar flows for Android and iOS. – Frank van Puffelen Jul 18 '16 at 03:57
  • Awesome! Thanks a lot for your help Frank! – Pier Jul 18 '16 at 04:01
  • 7
    @FrankvanPuffelen, you said that we need an active listener to keep things cached. However, your first listener is a `once`. This isn't an active listener right? Shouldn't it be `on` or am I missing something? – Mikko Paderes Feb 14 '17 at 04:19
  • @FrankvanPuffelen I can't replicate the speedup you're demonstrating here with only a `once` no-op listener. Can you verify that commenting out the no-op line, then uncommenting produces different results? You point out that `once` listeners detach immediately. So why would this do anything? Don't we need a `.on` no-op listener so that it keeps the connection? This is why I proposed the edit twice.. Is a `once` listener a permanent listener, or isn't it? Please clarify. – cdock May 24 '17 at 22:47
  • Yeah, that probably should be an `on()` (hint: comment before proposing this type of edit). But in general, I'm having a hard time right now reproducing the behavior I describe: http://jsbin.com/soxozoj/edit?js,console. I'm not really sure what I'm doing wrong right now, because clearly it ran before. – Frank van Puffelen May 24 '17 at 23:06
  • Makes sense. I'm new to Stack Overflow so didn't know the right process ;). Your post actually helped me do the right think months ago, and I had just assumed without reading too closely that `.on` was what I needed to use because of your explanation. Wasn't till later I saw the typo.. Tests matching your results logically could only be achieved with `.on` because of detaching of `.once` right? – cdock May 28 '17 at 19:00
  • Yup. Although as said: I can't really reproduce it now. I'm not sure why. :-/ – Frank van Puffelen May 28 '17 at 21:37