I'm building a chat using Firebase, using offline feature (keepSynced(true))
It works very well except that I cant find a way to know if the DataSnapshot message has indeed been saved server side, as if I query for the message Firebase will tell me that it exists (it does locally!).
I found about transactions but it does not save offline.
I also found about checking the connection status of Firebase, but it is a global status, and does not reflect one by one snapshot status.
The only solution that I found is adding a property "persisted" on my message objected, this property being set by a server side function, but that seems overkill for this purpose. I'm pretty sure Firebase does know locally which values are not persisted yet ?
On Android, when you perform any operation that writes, you will either get a Task object in return, or you may specify a CompletionListener as an argument. Either of these will indicate when the data is received by the server. If you don't use these, you have no other indication.
Using a Cloud Function to tag the location upon write is certainly another way to do it, if you can't hold on to the Task or CompletionListener.
Related
I was building a chat application in Android using Firebase. When we use Firestore SDK, it triggers any listeners on updates as soon as data is written into local Firestore cache. Since I have included server-time in the field, a second trigger will occur when the data has actually reached server, since server-time gets updated there. Is there a possible way for me to distinguish between these two triggers, so that, I can update the user that the message reached the server (like WhatsApp).
If this is absolutely not possible, I have two options:
Use custom cloud functions to send the message, so that listener triggers only after data has reached the server. Until then, I can show pending status for user.
Use custom cloud functions that triggers on Firestore writes at that location, and sets a flag, which indicates the data has reached the server.
If I have choose, which is better?
You can determine whether a snapshot is guaranteed to be up-to-date with the server by checking the isFromCache() and hasPendingWrites() methods in its metadata.
My problem is with how Firestore offline mechanism works (or i don't understand it). Specific issue is that data is firstly synced from cache instead from online if network is available. It's quite easy to reproduce this:
In your firestore database, change data type of one of your variables to the wrong type. For example if we have document "Person" which contains variable "name" which is type String, change type to number and put 1 as value.
Your application will now crash, because Firestore SDK tries to parse data from that specific "Person" variable "name" int as String.
Now fix this issue on Firestore database and set "name" back as String and set value "test name".
Now open application (with network available).
The problem that i'm seeing in my own application is that the data is firstly taken directly from cache, and in cache, that "Person" variable "name" is still written as int (number on firestore) and the application crashes before it can take updated value from online, even though I have active network connection and I fixed data type directly on Firestore database.
The only solution to prevent crash in this situation is for user to clear app storage/cache and go into application again. Is this reproducable issue for other users as well? Is this intended behaviour?
If there is more explanation needed please write in comments.
This is the expected behavior. You should use some combination of strategies to prevent problems due to bad data:
Obviously, verify that all writes contain the types you expect. Use security rules to validate those types on the server.
Always assume that Firestore can give you unexpected data when you read a document. Before assuming that a type is what you think, actually check the type of that to make sure your assumption is correct.
Clearing cache should not be your production-worthy solution. Take steps to make sure that only good data goes in, and that only good data gets processed on the way out.
I'm writing an Android application that uses the Firebase RealTime Database and Firebase Cloud functions. Several of my cloud functions are triggered by writes to the RealTime database and are used further process data saved by the Android client application.
I'd like my app to handle network connectivity changes gracefully. I understand that Firebase handles loss of connection by saving changes to the RealTime database locally and then syncing the changes to the server when connectivity is restored. This is well documented in the Firebase Documentation.
In my case though, since I need the Cloud Function post-processing of data saved to some paths to have occurred before it is useful, there's no point in having this data saved if it isn't going to make it to the server (and therefore trigger a Cloud Function), in a timely manner.
I'm using the updateChildren function from DatabaseReference to save the data and have a CompletionListener attached to monitor the outcome. I thought I may be able to use the DISCONNECTED and/or NETWORK_ERROR DatabaseErrors to identify cases where my data won't be reaching the server. However, if I interrupt the network connection before updateChildren is called, there aren't any errors generated. In this case, Firebase has likely saved the data locally with plans to sync it later, so updateChildren is considered to have been completed successfully.
My questions then are:
When are the DatabaseErrors DISCONNECTED and NETWORK_ERROR actually used by Firebase? Can I use them in some way to help manage connectivity issues?
What are the best practices for handling cases where Firebase data must make it to the server in order to be useful? Should I really just be POSTing my data to the Cloud Function directly?
Does Firebase have any notion of a timeout period that can be watched and data invalidated if it isn't synced within a specific period of time?
Yes, I recognize that I can use a listener attached to /.info/connected to detect changes in the connection state, but I'd rather be able to react and gracefully handle my case as it happens. I feel that my usage of the Realtime database together with Cloud Functions is common enough that there must be generally accepted way to implement it.
Any thoughts appreciated. Many thanks.
Did you try OnDisconnect method of firebase realtime database ?
I'm trying to find out how far the offline capabilities of Firebase on Android actually go.
As far as I understand, it should be possible to make the Database "persistent" with FirebaseDatabase.getInstance().setPersistenceEnabled(true);
The documentation reads:
The Firebase Realtime Database stores data returned from a query for use when offline. For queries constructed while offline, the Firebase Realtime Database continues to work for previously loaded data. If the requested data hasn't loaded, the Firebase Realtime Database loads data from the local cache. When we come back online our data will load and reflect the query.
Is this also true when the offline state is beeing forced by goOffline?
In this question the user got an answer from firebase support:
While you can use goOffline() to force the client offline for a long time, performance will deteriorate over time. The Firebase clients will queue intermediate state changes, instead of updating the stored state as the server does.
Does this mean the "local database" isn't actually updated like it would be when offline due to connection loss?
Because most of the time any query or value event listener doesn't come back, onDataChange is never called as is onCanceled (I checked!)
If only the connection is lost, it actually works as advertised, although sometimes with up to a minute delay, which seems to be a problem on its own.
What is then the intention of even offering the goOffline() method if this just stops the interaction with the database completely?
In my implementation the app starts offline, with an anonymous authentication. So in the beginning of course the "local database" will be completely empty. But shouldn't the value events at least fire onDataChanged with an empty datasnapshot?
I tried staying online until I received the anonymous UID and added an empty entry into firebase database, which then is queried/cached. After that if I call goOffline, no more entries can be added and no more queries will be answered.
Similar to the above mentioned question, my plan is to offer the user the option to stay offline, with of course the downside of the build up of stored write events in the local cache (but that shouldn't be that big of a problem as there isn't that much data)
So how can I make this work if even possible at all?
The only thing I can see is to have some different database solution in the beginning for actual offline capabilities which has to be translated & transferred to firebase when the user chooses to go online.
I implemented an Android application that requires a set of data, taken by a SQL Server database. The application obtains the data calling a WS. After a first call to WS when the application start the first time, I need to maintain the data updated, according to the modify that may happens server-side (SQL server database).
For obtaining this result I perform, with a with a predefined frequency, a WS call, for knowing if data on database are changed. If new data are available, other web service is called for obtaining them.
This solution works fine for my ( I don't require real-time update). But, I think that this solution is too expensive in term of energy consumption, cpu consumption and network traffic.
Since, I immagine this is a very common problem I would know if exists a generic way to deal with it.
I suggest you to use extra fields. Add four colums to your local tables in Android :
TRANSACTING_FLAG : Set it to true when you are posting or updating this resource on the server
REQUEST_STATE : Set this flag to POSTING / UPDATING / DELETING etc.
RESULT_CODE : Set this field to the last result code received by the server for this particular resource.
TIMESTAMP : Period after wich data has to be updated
Workflow is simple :
When you retrieve data for your server just check if the last updated timestamp of your resource is superior to the cache timestamp you have defined before. If the timestamp is superior perform a request to update data. The transacting boolean let you know that a particular resource is actually synchronizing with the server. The result code lets you know if the request has failed or not and enventually retry it when the network is available. Doing this way you will maintain the persitence between your local and remote database because at any moment you can check the "synchronized state" of any local resource thanks to extra fields seen before.
I made a library to handle this. Take a look to RESTDroid. Even if the cache functionnality is not handles, you will be able to simply add it.
What you do is ok for most cases. You can take advantage of Google Cloud Messaging, but it needs time and effort to get implemented. I would stay with your solution.
You could look into Query Notifications, using something like SqlDependency - http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.aspx. You can use this to track whether the results of a query change and to inform an application when this happens.
There are restrictions on the query you can use, and cost on the server is similar to an indexed view. You need .NET for this, by the way. If implemented in your Web Service, you would have to implement some kind of subscribe feature for your android, so that notifications could be pushed to it.
Another option to reduce the cost of checking for changes could be SQL Server Change Tracking - http://msdn.microsoft.com/en-us/library/bb933875.aspx