To Realtime or Not? | Get to know Cloud Firestore #10

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
So I know for a lot of you developers out there, the idea of a database automatically sending you synchronized updated values in near realtime is like weird and strange. Like when a lot of developers first started working with a Firebase Realtime Database, they kind of pined for the good ol' days when you could just make a simple GET request and kind of skip that whole realtime thing. Turns out though that was hard to do in Realtime Database land. There was a solution, but it was kind of clunky, and we generally steered people away from it. Luckily, with Cloud Firestore, we've redesigned the SDK from the ground up so that one-time document fetches and queries are a lot easier. They're more like first-class citizens in the Cloud Firestore API, and you can go ahead and use them with impunity. Now that we've gone through the trouble of making the one-time fetching of data easier, I'm here to tell you, eh, maybe don't do it. ♪ (rock music) ♪ So the nearly realtime data synchronization capabilities of Cloud Firestore, where data just automagically updates from the Cloud is pretty awesome. I would generally encourage anybody who's using Cloud Firestore to look at adding this kind of behavior to their app. But when I've talked to developers, a lot of them are hesitant to go down this realtime route and it's generally for two reasons. One, realtiminess is weird and strange and how am I supposed to organize my app around this kind of behavior? And two, general concerns around battery usage and/or price. So let's see if we can address some of these. I think the best way to get over your fear of the unknown here is to give you an overview of how these realtime capabilities work and then go over how you might want to use them in your app. Now when you ask to listen to a single document or a bunch of documents at once for a query, that sets a number of things in motion. On the client, you're going to create a listener. This is essentially a callback that the SDK fires when there's new data available from your database. And over on the Cloud, you're going to be kicking off a process on the database itself that monitors this specific query to see if there's any new data available. Now the first time you ask to listen to a document or a query, you're going to get back all the data you request. I mean it's basically all new data if you think about it. So if you ask for one specific document, you're going to get back the contents of that document. If you ask for the top 20 Japanese restaurants in San Francisco, you're going to get back those 20 restaurants. But from that point on, your client will only get notified when there's a change in those 20 documents, whether that's a change in the document itself, a restaurant gets added or removed or they change their order in that query. Now usually for the sake of protecting your users' data plan and battery, only the documents that have changed will be sent over. So if Ray's Ramen suddenly changes their name to Gary's Gyoza, what I'll get back under the hood is data that looks like this, one document representing the restaurant that's changed. But over on the client, the Cloud Firestore library will combine this updated document with the rest of the cache data, and in the listener, it will still present me with the entire set of 20 documents. And I know that might seem weird at first-- why is it sending me all these documents again?-- but this is usually what you want as a developer. If you think about it, the code in your listener is already set up to read in and display a full set of 20 documents because that's what happens the first time it's run. So when we get back a full set of 20 documents for all future changes I can probably reuse this code over and over again and not have to worry about special-casing it depending on whether this is my initial data grab or not. By the way, I think this is one reason developers might freak out about pricing at first. They see this list of 20 documents showing up again and again in their listener and they think that Cloud Firestore is charging them for 20 reads every time there's an update, but that's not the case. Yes, you get back all 20 documents in these callbacks but you're usually only getting charged for the one document that is changed. Now if you really do care to know exactly what's changed in your callback listener, you can do that. Every snapshot contains a document changes object, which will give you a list of every document that's been added, removed, changed or appears in a different order. I think most often developers use this information if they want to provide a visual cue that something has changed. For instance, we could have an item turn red and fade out if we know that it's been deleted or add in some gold sparklies when we know that it's been added. Or maybe you're performing some very expensive processing on your data. In that case, maybe it also makes sense to only do this on the documents that have changed. It's kind of up to you. I know we talked about this in the last episode, but remember that when you perform a write, those writes are essentially applied immediately on your local copy of the data. Now when that happens, any listeners on your client are also notified. Those listeners are fired off using the updated local client data as your snapshot, so then you can act on those changes right away. This is all happening while your data is traveling to the database in the Cloud in the background. So, several milliseconds later when your database comes back with the updated data, Cloud Firestore is smart enough to realize that this updated data matches what it has locally, and by default, it will quietly ignore this update from the Cloud. So what does this all mean if you want to construct an app that behaves nicely in the land of Realtime updates? First, your listener is going to be the place where you primarily read in your Cloud Firestore data and do stuff with it. It will be the place where you convert your database documents into custom local objects. You'll probably populate whatever local model you're using, and then you can tell your table view or labels or what have you to refresh themselves based on this local model. But then whenever you're looking to make changes, you're going to want to make those writes directly to the database documents themselves. That's right. You don't want to change your local model. You want to make the change to the original database document. Now that will kick off the change locally right away, and your listener can once again update its local model based on the data you're getting back from the database. Now most cases, this process is probably simple enough that you'll just kind of blow away your old model and reconstruct it from scratch with a new set of data. But you can always get fancier if you want by looking at just the objects that have changed. But I think the main takeaway here is that we're seeing the database as the source of truth and the place we want to make any kind of write operations with our data. Now let's talk performance, both when it comes to data usage and battery drain, because I think some developers assume that keeping open a realtime connection is as much of a battery killer as streaming down a video, and that is not the case. So yes, anytime there is a listener present, your user's device will have an open socket connection to the database and while that has a greater than zero impact on your user's battery, it's probably not going to be the biggest factor. That's going to be much more influenced by the amount of actual data you're downloading or having the screen on on the phone. So don't think that if you shut down the realtime connection and have your app perform a bunch of manual one-time fetches instead, you're going to save any battery. In fact, if you keep turning your radio on and off to make these fetches, there's a good chance it might even be worse that way, For that matter, having several active listeners at once isn't by itself going to be a big drain on resources either. What is going to be a bigger drain on your user's battery and their data plan is the downloading of bytes. To oversimplify things a little bit, the more bytes you download, the more it'll hit your user's battery. So what does that mean for you? Well, if you're concerned about battery or data usage and you should be, follow these two rules. First don't download more data than you need. I know you can put a lot of data into a single document but if you find that you end up downloading a lot of data that you almost never use, stick that extra data into a sub collection and then only download those extra documents when necessary. Second, make sure you deactivate your listeners when they're not needed. Generally speaking, when you add a realtime listener in your <i>ViewController</i> or your <i>Activity</i>s life cycle, you want to remove it at the complementary place. Meaning that if you create a listener in your viewWillAppear handler on iOS, deactivate it in your viewWillDisappear. If you create a listener in your onStart on Android, deactivate it in your onStop or attach it to the <i>Activity</i> which will automatically unregister it. In a PWA, if you're adding a listener when a component is created, make sure you deactivate it when the component is de-initialized. And yes this does mean that when your user comes back to a screen they might have visited earlier, it might take a moment to refresh your latest data because that listener hasn't been running in the background. But it also means that if the same document were to change several times in the back end while your user is somewhere else in your app, their device won't be going through the trouble of downloading all that intermediate data that they don't really need, and you won't get billed for those reads. So when would you want to use Realtime listeners versus a single fetch? Well, I'll be honest. I'd say that, in general, realtime should probably be your default choice. For starters, and I know this is counterintuitive, using realtime data makes your app perform better in areas of spotty network coverage by default. Remember the nice thing about these realtime listeners is that they're designed to be run multiple times. So we can do things like give you the cache data right away then give you updated data a few hundred milliseconds later when the network actually follows through. Fetches don't work that way. These one-time fetch callbacks are designed to only be called once, so you only have one chance to fetch that data, which means if your network is slow, we have to wait those three seconds until we can get back that data or wait several more seconds until that connection times out before we can serve the cache data, and, you know, that's not a great experience. There are ways to improve this, of course. You can manually run two one-time fetches-- one that's explicitly set to fetch from the cache, and then another that's explicitly set to fetch from the server-- but, again, this is work you wouldn't need to do if you just used a realtime listener. Plus, having realtime listeners means that when your client makes changes to a document, you don't have to add that extra step of manually refetching the data that's just changed. Again, it will happen automagically when your listener gets reactivated with sort of this changed cache data. And, of course, there's the whole realtimey thing which can be a pretty magical experience. If you're powering a turn-based game or something, it's pretty great to have that data update right away when the other player makes their move. If you didn't have this happen, you'd need to ask your player to pull to refresh a bunch of times or send a Cloud data message to your device to tell it to perform an update, and if you're doing all that work anyway, why not just simplify things and let Cloud Firestore handle it for you. So maybe the better question is in what situations would it make sense to not go realtime and use one-time fetch calls? And I think the answer's probably less a technical one and more of a user experience one. Are there situations in your app where it would be, say, distracting for new data to arrive? Think about my restaurant review app. Would it be weird if new reviews popped in or headlines changed while my user was perusing the reviews for a particular restaurant? It might. Of course, on the other hand it might not. Honestly, this is something I'd want to look at on a case-by-case basis. Also we talked about this in an earlier video, but sometimes pagination can be a little weird if you're trying to combine paginated data with realtime listeners for one of those infinite scrolling table view dealies. In these cases, it might sometimes be better to go with some one-time calls. Finally, there will probably be situations where data is changing more often than your user will care about it. Think about a popular news site like Reddit, where you've got stories that are constantly getting upvoted and downvoted. Hey look at that! Your post on Leroy Jenkins got two votes. Oh, and it's gone negative. I'm sorry. Now admittedly, I think it'd be kind of neat to see those votes change in realtime and maybe see them switch places, but that constant stream of data might be kind of expensive in terms of battery and data usage all to deliver an experience that, while kind of neat, my user might not care about that much. So in these situations, a one-time fetch might make more sense. Finally, when it comes to things like Cloud functions, you're generally looking at calls that perform a discrete action and then terminate. So there's not much of a point in setting up my realtime listeners there. Same goes for probably 90 percent of your use cases involving the server SDKs. But remember the main takeaway here is that you should start thinking of realtime as your default behavior, then only switch to a one-time fetch call when you have a good reason to make that change rather than vice versa. So there you have it. Hopefully, now this world of near realtimey experiences isn't quite so weird and strange and frightening and you can start adding more of these magical moments into your app. Thanks so much for watching, and I will see you soon on another episode of "Get to Know Cloud Firestore." ♪ (music) ♪
Info
Channel: Firebase
Views: 65,511
Rating: 4.9707389 out of 5
Keywords: Get to know Cloud Firestore, know Cloud Firestore, to real time or not, to real-time or not, Firebase realtime database, Firebase real-time database, Cloud Firestore API, offline mode Firestore, how do I enable offline mode, offline mode cached data, enable offline mode, SDK, redesigned SDK, Cloud Firestore, Firebase, Firebase developers, Google developers, Firebase devs, Todd Kerpelman, GDS: Yes;
Id: 3aoxOtMM2rc
Channel Id: undefined
Length: 11min 44sec (704 seconds)
Published: Thu Aug 01 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.