Using Firestore from a JobIntentService: Failed to gain exclusive lock to the Firestore client's offline persistence - firebase

Whenever I exit the app while I have an alarm set and the alarm goes off while the app is "DEAD" I get an Exception while trying to update a field in Firestore.
The code works when the app is running in the foreground so I really have no clue of what is going on. Either way, here is the code for 2 functions which get called from the JobIntentService which is in turn created from a BroadcastReceiver:
private val firestoreInstance: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
fun updateTaskCompletedSessions(taskDocRefPath: String, completedSessions: Int){
val taskDocRef = firestoreInstance.document(taskDocRefPath)
taskDocRef.get().addOnSuccessListener { documentSnapshot ->
documentSnapshot.reference
.update(mapOf("completedSessions" to completedSessions))
}
}
fun updateTaskSessionProgress(taskDocRefPath: String, sessionProgress: String){
val taskDocRef = firestoreInstance.document(taskDocRefPath)
taskDocRef.get().addOnSuccessListener { documentSnapshot ->
documentSnapshot.reference
.update(mapOf("sessionProgress" to sessionProgress))
}
}
The full error goes as follows:
Failed to gain exclusive lock to the Firestore client's offline persistence.
This generally means you are using Firestore from multiple processes in your app. Keep in mind that multi-process Android apps execute the code in your Application class in all processes, so you may need to avoid initializing Firestore in your Application class. If you are intentionally using Firestore from multiple processes, you can only enable offline persistence (i.e. call setPersistenceEnabled(true)) in one of them.
I will appreciate any help. Thank you!

I'm happy to announce that I found a solution! I was using two consequently firing JobIntentServices - one for completedSessions, the other for sessionProgress. (Bad design, I know...)
When I played around with it and made just ONE JobIntentService to call both of these functions, the exception is gone which makes perfect sense.

Related

Unity Firebase RTDB doesn't work after building

Unity version 2020.3.22f1,
Firebase SDK 9.0.0 dotnet4
I've imported both the analytics and real-time database SDK.
The Analytics works perfectly fine.
Regarding the database, building an Android app bundle and uploading to internal test or building an APK and uploading directly to my phone or building for IOS and uploading to test-flight all 3 results with an error.
This is how I initialize Firebase-
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
//init analytics
FirebaseAnalytics.SetAnalyticsCollectionEnabled(true);
DatabaseReference = FirebaseDatabase.DefaultInstance.RootReference;
Debug.Log(JsonConvert.SerializeObject(DatabaseReference.Database.App.Options));
});
This is how I use the database (increment the win field of a specific level) -
DatabaseReference.Child("Levels").Child($"Level{levelNum}").Child("Wins").RunTransaction((mutableData) =>
{
mutableData.Value = Int32.Parse(mutableData.Value.ToString()) + 1;
return TransactionResult.Success(mutableData);
});
Referring to the log above in the DatabaseRefrence initialization, in the editor, I can see all the configuration properties - databaseUrl,apikey,AppId, etc...
Debugging the APK on my phone the Options property only includes the databaseUrl.
And when trying to perform a transaction to the database an error is being thrown -
W/Unity: Exception in transaction delegate, aborting transaction
System.NullReferenceException: Object reference not set to an instance of an object.
at ....(Firebase.Database.MutableData mutableData)
Things I've tried so far-
I've added the SHA1/SHA256 of both my debug Keystore and googles console App integrity
I've checked and the XML files are being generated with all the details successfully at the streamingAssets folder and at Assets\Plugins\Android\FirebaseApp.androidlib\res\values\google-services.xml
I've tried Initializing the Firebase app manually as mentioned here - https://stackoverflow.com/a/66874818/7210967, doing that indeed results with the debug.log above to include all the Options parameters but the same error occurs as if it doesn't actually use it. (I've tried doing that both with the configuration files in place and removed them completely).
I've tried overriding the default app instance Options.
I've read some posts saying that Proguard obfuscation might cause errors with firebase? couldn't find anything related to Unity.
If anyone has any ideas, please share! ty
Transactions in Firebase Realtime Database work a bit differently than you might expect, as they immediately invoke you handler with the client's guess about the current value of the node, which in general is going to be null.
So when you call mutableData.Value in your code, you get back null and you then call ToString() on it, which leads to the error you get. To solve this, first check whether the mutableData.Value is null before invoking methods on it.
int current = mutableData.Value is null ? 0 : Int32.Parse(mutableData.Value.ToString());
mutableData.Value = current + 1;
return TransactionResult.Success(mutableData);
Syntax errors are possible in the above, as it's been a while since I wrote C#.
A transaction send both the SDKs guess and your new value based on that guess to the server, which then does a compare-and-set operation. If the guess doesn't match the actual value in the database, the server rejects the write with the current value, which the client then uses to call your transaction handler again with an updated current guess.

In instrumented tests, how do you make Cloud Firestore write commands succeed when disabling the network?

So I am attempting to use the Cloud Firestore offline cache ONLY as an API for my instrumentation tests, to avoid having to read and write from the server database during my integration tests.
First, in my test setup method, I call this method
protected fun setFirestoreToOfflineMode() {
Tasks.await(FirebaseFirestore.getInstance().disableNetwork())
}
Then, at the beginning of each relevant test, I use
fun givenHasTrips(vararg trips: Trip) {
GlobalScope.launch(Dispatchers.Default) {
trips.forEach {
firestoreTripApi.put(it)
}
}
}
In that put method, I have the following code:
try {
Tasks.await(tripCollection().document(tripData.id).set(tripData)),
firestoreApiTimeoutSeconds, TimeUnit.SECONDS)
Either.Right(Unit)
} catch (e: Throwable) {
Either.Left(Failure.ServerError)
}
I am calling the set() method and am waiting for a successful result in order to be able to return that the operation was a success, to update my UI afterward.
What happens is the cache DB is written correctly BUT the "set()" function times out because the database is in offline mode. I have read that Firestore only confirms a success if the Server DB was correctly written. If that is the case, I do not know if it is possible to have this call not time-out when operating strictly in offline-cache mode.
Is there a solution to have Firestore act as if the local cache database was the source of truth and return successes if placed in offline mode, just for tests?
The Task returned by the methods that modify the database (set, update, delete) only issues a callback when the data is fully committed to the cloud. There is no way to change this behavior.
What you can do instead is set up a listener to the document(s) that are expected to change, and wait for the listener to trigger. The listener will trigger even while offline.

Firebase multi path query takes long to response

I am using firebase realtime database and I have a multi path firebase update query that I was using for a while, however for last couple of days the callback never fires and other firebase database requests are blocked as well until this one fires. Callback takes incredibly longer to fire than it should or it used to. And most interestingly this issue does happen on Windows environment only.
let updateObj = {}
updateObj[`transcripts/${uid}/${itemId}/currentState`] = currentState
updateObj[`lists/${uid}/${itemId}/edited`] = firebase.database.ServerValue.TIMESTAMP
updateObj[`lists/${uid}/${itemId}/filename`] = title
db.ref().update(updateObj, function(error){
//handle error
}
I thought it could possibly be because another reference is initialized at the same path later on but callback sometimes hangs even before that.
updateObj[`transcripts/${uid}/${itemId}/currentState`] = currentState
variable currentstate above has a somewhat of a big array node inside so I also think the array might be the issue considering they don't really work that efficiently with firebase realtime database.
Commenting this out solves all the problem but I am still clueless how this suddenly started to break firebase realtime database for me.

Using Offline Persistence in Firestore in a Flutter App

I'm developing a app that uses Firebase's Firestore to send data to the web. One of the functions of the app is being able to save data in the device while being offline and send it to Firestore when internet connection is restored.
I activated offline persistence but it dosen't work.
DEBUG CONSOLE:
W/OkHttpClientTransport(28536): Failed closing connection
W/OkHttpClientTransport(28536): javax.net.ssl.SSLException: Write error: ssl=0x7f7acfc408: I/O error during system call, Broken pipe
W/OkHttpClientTransport(28536): at com.google.android.gms.org.conscrypt.NativeCrypto.SSL_write(Native Method)
W/OkHttpClientTransport(28536): at com.google.android.gms.org.conscrypt.NativeSsl.write(:com.google.android.gms#14798020#14.7.98 (040406-222931072):4)
How can I activate offline persistence and sync with Firestore when internet is restored?
MY CODE:
Future<Null> sendFirebaseData(var selectedModel) async {
Firestore.instance.enablePersistence(true);
var certID = await getIDCertificado();
var dateTime = new DateTime.now();
var nowHour = new DateFormat('kk:mm:ss').format(dateTime);
Map<String, dynamic> dataHeader = {
'ID': certID,
};
Map<String, dynamic> finalDataMap = {}..addAll(dataGeneral)
..addAll(dataInstrumento)..addAll(dataPadrao)
..addAll(dataAdicional)..addAll(dataHeader);
await Firestore.instance.collection('certificados').document((certID.toString()))
.setData(finalDataMap);}
when you use offline persistence in Firestore, don't use Transactions or await for response.
so, change this :
await Firestore.instance.collection('certificados').document((certID.toString()))
.setData(finalDataMap);
to this:
Firestore.instance.collection('certificados').document((certID.toString()))
.setData(finalDataMap);
When you restore your internet connection your data will be sync automatically, even if you are in background.
Doesn't work when your app is closed.
Context Of Promises & Callbacks when Offline
Why the above code change to remove "await" works.
Reference: Firebase Video - How Do I Enable Offline Support 11:13
Your callback won't be called and your promise won't complete until the document write has been successful on the server. This is why if your UI waits until the write completes to do something, it appears to freeze in "offline mode" even if the write was actually made to the local cache.
It is OK to not use async / await, .then() or callbacks. Firestore will always "act" as if the data change was applied immediately, so you don't need to wait to be working with fresh data.
You only need to use callbacks and promises when you need to be sure that a server write has happened, and you want to block other things from happening until you get that confirmation.
I think the currrent answer is outdated. According to the firebase documentation, offline persistentence is enabled by default for Android and iOS. For the web, it is not.
In flutter, the firestore implementation is based on the underlying OS. So you're safe on mobile apps, but not with flutter for web.
It is enabled by default but still only when you are not using await or transactions, further you can use timeout to stop listening to network connection by firestore after a specific time.
ref.setData(newNote.toMap()).timeout(Duration(seconds: 2),onTimeout:() {
//cancel this call here
print("do something now");
});

Firestore database keeps crashing

I have successfully migrated my app from RealtimeDB to Firestore but after migrating the app crashes too often with the following error, how to fix this?. I have never run in to this error while using RealtimeDB
Fatal Exception: java.lang.RuntimeException: Internal error in Firestore (0.6.6-dev).
at com.google.android.gms.internal.zzejs.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6184)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:788)`
Caused by java.lang.RuntimeException: Failed to gain exclusive lock to the Firestore client's offline persistence.
This generally means you are using Firestore from multiple processes in your app.
Keep in mind that multi-process Android apps execute the code in your Application class in all processes,
so you may need to avoid initializing Firestore in your Application class.
If you are intentionally using Firestore from multiple processes,
you can only enable offline persistence (i.e. call setPersistenceEnabled(true)) in one of them.
`at com.google.android.gms.internal.zzefi.start(Unknown Source)
at com.google.android.gms.internal.zzeca.zza(Unknown Source)
at com.google.android.gms.internal.zzecc.run(Unknown Source)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at com.google.android.gms.internal.zzejp$zza.run(Unknown Source)
at java.lang.Thread.run(Thread.java:761)`
If you try to access the db from multiple threads, this issue will occur. So if you are using RX, you need to create a single background thread for all insertions. This is because Firestore locks the DB while writing.
val FIRESTORE_OPERATION_THREAD = Schedulers.single()
yourSingle.subscribeOn(FIRESTORE_OPERATION_THREAD)
. ...
What #Sandip Soni would work whenever various calls don't interleave each other(like repeated inserts) and by the async nature of Firebase they're likely to do it. What did work for me was a little hack, synchronizing the access to the Firestore instance and firing one write operation outside any foreign thread in order to let Firestore get the local db instance in it own thread. the write operation is an arbitrary one, it was just for setup purpose:
class Firestore {
companion object {
val instance: FirebaseFirestore by lazy {
return#lazy synchronized(Firestore::class){
FirebaseFirestore.getInstance().apply { lock() }
}
}
private fun FirebaseFirestore.lock() {
collection("config").document("db").update("locked", true)
}
}
}
You must be using Firestore from multiple processes in your app. Multi-process Android apps execute the code in your Application class in all processes.
To solve the error you may need to:
Avoid initializing Firestore in your Application class or in a module for Rxjava. Instead, initialize a new Firestore instance before every call to the firestore database like this:
val fireStore = FirebaseFirestore.getInstance()
val settings = FirebaseFirestoreSettings.Builder()
//offline persistence is enabled automatically in Android
.setTimestampsInSnapshotsEnabled(true)
.build()
fireStore.firestoreSettings = settings
//go ahead and call database using the Firestore instance
This means that you will not use the same Firestore instance for multiple calls.
Then clear your application cache.
Just clear the Application's cache in the settings. It's work for me!

Resources