I came across unique use case where following feature will come in very useful.
In essence I have components that are listening for specific changes in a document, my security rules are set up in a way where reads are allowed, but all writes are disabled as database updates are only possible through cloud function, hence I was researching the docs to find if it is possible to do something like this.
/**
* This update should only happen locally / offline in order to update state of components
* that are listening to changes in this document
*/
myDocumentRef.update({ name: 'newname' });
/**
* Then cloud function is called that performs validation and if correct
* updates document with new data (same as one we set offline above).
* Once that is done listening components will receive new data. If unsuccessful
* we would set document to old data (again offline) and show error.
*/
await myCloudFunction();
Hence my question: is it possible to perform that update (and only update) locally / offline?
This is basically "optimistic update" flow utalising firebase as global store of sorts.
Firestore does not provide a concept of "local only" updates. All writes will be synchronized with the server at the earliest convenience. If a write eventually fails, the promise returned by the API call (if still in memory) will be rejected. If the sync happens after the app restarts, the promise is lost, and you have no way of knowing if the write succeeded. Local writes that eventually fail will be reverted in the local cache.
What you will have to do instead is write your data in such a way that the local listener can tell the stage of the write, possibly by writing metadata into the document to indicate that stage, cooperating with the Cloud Function on keeping that up to date.
Related
Does anyone know if there is an easy way to trigger a function everytime i re-deploy some funciont to firebase?
I have an specific firabase functions which i define inside GCP (this way when i do "firebase deploy" it doesnt re-deploy, unnisntal or touch in any form my current function)
but sometimes i might update this function manually on GCP and i would like to trigger a inner function of its code everytime it happens... is it possible?
ex:
exports.decrementAction = (req, res) => {/*do stuff*/res.status(200).send("ok")};
function auxiliary(){
//to be called on re-deploy
}
Unfortunately, there isn't an easy way for you to trigger a function within a code that is being redeployed. Since this code is only being deployed at the moment, this wouldn't be possible to be done automatically.
The alternative would be to have this function separately from the "root" function in the moment of deploying and use triggers to run this other Cloud Function, when the first is redeployed. This way, it would be possible to run it based in the deployment of the other.
You can get more information on the triggers available for Cloud Functions here: Calling Cloud Functions. With them, you should be able to configure the timing for the execution.
Besides that, it might be worth it to raise a Feature Request for Google's to verify the possibility of adding this in future releases.
Let me know if the information clarified!
I think there exists a manner.
With Pub/Sub you can catch logs from Stackdriver (docs). Those services allow you to store only the logs related to the deployment of a Cloud Function.
The store could be, for instance, Cloud Firestore. As you should know, there is available a trigger for Cloud Firestore events.
Finally, every time an event log related to a function's deployment is generated, it will be stored and triggers a function attached to that event. In the function, you can parse or filter the logs.
I am using many cloud function trigger and admin sdk to do multi-path update.I don't want to do too much multi-path update in client cause it will make firestore rules very complex and firestore rules also have document access call limits.So I decide to using cloud function to do most denomorlization stuff.
There is how one of my function work.
cloud function triggered at profiles/{userId}
and i using .get to load multi-path update needed paths at
profilesPaths/{userId}
set writebatch.update on those paths
writebatch.commit()
And I think there have a problem.cloud function is asynchronous right?.So when function are running to step 3 And at the same moment a client delete one of update path from cf already loaded document at profilesPaths/{userId} (already loaded at step 2).and now cloud function loaded document is not the latest version.Will this happend? or i should using transactions to lock those documents?
Yes, Cloud Functions run asynchronous, and possibly in parallel. You should be using transactions to make sure that updates are consistent among all the clients that are trying to modify them concurrently.
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.
On the Firebase docs they mention 4 types of triggers:
onCreate
onDelete
onUpdate
onWrite
Is there a way to listen to added row in the Cloud Functions and modify fields of an added row before the "child_added" listeners are triggered? Is there a way to implement BeforeCreate?
Desired BeforeCreate cycle (in Cloud Functions):
Request to add a new message
Change the message fields
Add a new message with modified fields
Clients receive a "child_added" event
All events for the Realtime Database in Cloud Functions trigger asynchronously after the write has been committed. For this reason, other users may already have seen the data before your function can change it.
To solve this problem you'll want to ensure the data only gets written to the location everyone sees after it's been validated/modified.
To validate/modify the new data before listeners to that data can see it, you have two options:
Use a HTTP triggered function for writing the data. The application code calls the HTTP function, which does the data manipulation you want, and then writes the result to the database.
Have the applications write to a "moderation queue", which is just a separate location in the database. The Cloud Function triggers fro this queue, validates/modifies the data, writes it to the actual location, and then deletes it from the queue.
With both of these approaches you lose parts of the transparent offline behavior of the Firebase Realtime Database though, so you'll have to choose.
You need to use onWrite for this to work, as you are saving to the database more than once when you are using child_added.
onWrite(handler) returns functions.CloudFunction containing non-null functions.database.DeltaSnapshot
Event handler that fires every time a Firebase Realtime Database write of any kind (creation, update, or delete) occurs.
more info here: https://firebase.google.com/docs/reference/functions/functions.database.RefBuilder#onWrite
Op wants to do the following:
Request to add a new message
If he wants to request a new message from the end-user then it is better done on the client side.
Change the message fields
Here he wants to change what was written inside the field, which is also usually done on the client side not in cloud functions.
Add a new message with modified fields
Here he wants to add the new message to the database (according to my analysis). Then this can be done in the cloud functions and the message can be added using set()
Clients receive a "child_added" event
then here after adding the new message to the database, he wants the client to receive the database trigger, that will be triggered with the new message. Here he can use cloud functions like onWrite() or onCreate()
When I call setValue while my user is offline in my Android app, Firebase provides a snapshot of the change to my listener from the local store,even though the change hasn't been committed to the server. I understand that Firebase will make an effort to sync with the server when connectivity is restored, but if the app restarts that change is lost forever.
I've already got a custom local store for all my data, and I was hoping I could keep track of uncommitted changes myself, clearing a dirty bit when I hear back from the server of a successful setValue. But that doesn't seem possible if the local store is pretending the date is committed.
I don't think I want to use disk persistence (since I already have my own). Is there a way to tell when the update is from the local store vs a real server commit? Or maybe I should use a completion listener to clear the dirty bit?
I understand that Firebase will make an effort to sync with the server when connectivity is restored, but if the app restarts that change is lost forever.
The default behavior is indeed that the pending writes are kept in memory. You can however easily change that by enabling disk persistence.
Firebase.getDefaultConfig().setPersistenceEnabled(true);
But since you indicate you don't want to use that, the alternative is to use a CompletionListener to detect when the changes have been committed to the database on Firebase's servers:
ref.setValue("I'm writing data", new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Data could not be saved. " + firebaseError.getMessage());
} else {
System.out.println("Data saved successfully.");
}
}
});
Note that completion listeners are not persisted to disk. So even if you enable dis persistence, the completion callbacks will not fire when pending writes are fulfilled after an app restart. So don't try to mix disk persistence or completion listeners.