Does cloud function that writing to the same document trigger itself again - node.js

For example I have this cloud function
export const testFunction = functions.firestore
.document('posts/{postID}')
.onWrite((change) => {
if (!change.after.data()) return;
const { count = 0 } = change.after.data();
t.set(change.after.ref, { count: count + 1 }, { merge: true });
});
Will it be stuck in a loop since it's constantly updating the document field and triggering itself?

Your code does not show what t is, so we can't tell exactly what this function is doing.
But if you are updating a document within a trigger that fired in response to an update on that same document, the trigger will execute again. It's up to you to make the function return without updating the document in order to avoid an infinite loop. So, you will need to figure out how to determine if that's the case.

Related

get the number of documents before and after Firestore trigger - Cloud functions

I'm trying to get the number of documents in a collection before and after a document has been added using cloud functions, the code in nodeJs I wrote is this:
exports.onShowcaseCreated = functions.firestore
.document("Show/{document}")
.onCreate((snapshot, context) => {
const showcaseDict = snapshot.data();
const uid = showcaseDict.uid;
return db.collection("Showcases").where("uid", "==", uid).get()
.then((showsnap) => {
const numberOfShowcaseBefore = showsnap.size.before;
const numberOfShowcaseAfter = showsnap.size.after;
console.log( numberOfShowcaseBefore, numberOfShowcaseAfter);
if ( numberOfShowcaseBefore == 0 && numberOfShowcaseAfter == 1 ) {
return db.collection("Users").doc(uid).update({
timestamp: admin.firestore.Timestamp.now(),
});
});
});
});
but the console logs are undefined undefined it seems like this is not the right approach for taking the number of documents before and after a document has beed added
The before and after properties are only defined on the argument that is passed to onCreate. You call that snapshot in your code, but it's actually a Change object as defined here.
Reading data from the database gives you a QuerySnapshot object as defined here. As you can see, that size on that QuerySnapshot is just a number and does not have before or after properties.
So there's no way to determine the size before the event triggered with your approach. Any query you run in the code, runs after the event was triggered so will give you the size at that moment.
To implement this use-case I'd recommend storing a count of the number of relevant documents in the database itself, and then triggering a Cloud Function when that document changes. Inside the Cloud Function code you can then read the previous and new value of the size from the change document that is passed in.

Poor Performance Writing to Firebase Realtime Database from Google Cloud Function

I have been developing a game where on the user submitting data, the client writes some data to a Firebase Realtime Database. I then have a Google Cloud Function which is triggered onUpdate. That function checks the submissions from various players in a particular game and if certain criteria are met, the function writes an update to the DB which causes the clients to move to the next round of the game.
This is all working, however, I have found performance to be quite poor.
I've added logging to the function and can see that the function takes anywhere from 2-10ms to complete, which is acceptable. The issue is that the update is often written anywhere from 10-30 SECONDS after the function has returned the update.
To determine this, my function obtains the UTC epoch timestamp from just before writing the update, and stores this as a key with the value being the Firebase server timestamp.
I then have manually checked the two timestamps to arrive at the time between return and database write:
The strange thing is that I have another cloud function which is triggered by an HTTP request, and found that the update from that function is typically from 0.5-2 seconds after the function calls the DB update() API.
The difference between these two functions, aside from how they are triggered, is how the data is written back to the DB.
The onUpdate() function writes data by returning:
return after.ref.update(updateToWrite);
Whereas the HTTP request function writes data by calling the update API:
dbRef.update({
// object to write
});
I've provided a slightly stripped out version of my onUpdate function here (same structure but sanitised function names) :
exports.onGameUpdate = functions.database.ref("/games/{gameId}")
.onUpdate(async(snapshot, context) => {
console.time("onGameUpdate");
let id = context.params.gameId;
let after = snapshot.after;
let updatedSnapshot = snapshot.after.val();
if (updatedSnapshot.serverShouldProcess && !isFinished) {
processUpdate().then((res)=>{
// some logic to check res, and if criteria met, move on to promises to determine update data
determineDataToWrite().then((updateToWrite)=>{
// get current time
var d = new Date();
let triggerTime = d.getTime();
// I use triggerTime as the key, and firebase.database.ServerValue.TIMESTAMP as the value
if(updateToWrite["timestamps"] !== null && updateToWrite["timestamps"] !== undefined){
let timestampsCopy = updateToWrite["timestamps"];
timestampsCopy[triggerTime] = firebase.database.ServerValue.TIMESTAMP;
updateToWrite["timestamps"][triggerTime] = firebase.database.ServerValue.TIMESTAMP;
}else{
let timestampsObj = {};
timestampsObj[triggerTime] = firebase.database.ServerValue.TIMESTAMP;
updateToWrite["timestamps"] = timestampsObj;
}
// write the update to the database
return after.ref.update(updateToWrite);
}).catch((error)=>{
// error handling
})
}
// this is just here because ES Linter complains if there's no return
return null;
})
.catch((error) => {
// error handling
})
}
});
I'd appreciate any help! Thanks :)

setInterval() with fresh database data?

Hello and thank you in advance for taking your time to help me,
I've a setInterval() script who change some data in my MongoDB.
For example:
const isOn = mySavedDatabase.isOn;
var timer = setInterval(function() {
fetch('https://thewebsite.com', options)
.then(res => {
return res.json();
}).then(async data => {
if(data.data[0] && isOn == false) {
await xx.updateMe(xxx.id, { "isOn": true });
// some others actions here
} else if(isOn && !data.data[0]) {
await xx.updateMe(xxx.id, { "isOn": false });
} else if(isOn && data.data[0]) {
// nothing to do here.
}
})
}, 60000);
This script works fine, but it does not take the new changed data from the database.
If in the database, when the script starts, the value of "isOn" is true, it will remain true until the end of the script even if, in this one, I changed the data.
I checked, the value is changed by the setInterval() but, as long as it is in the loop, it does not take data from the database that it itself has changed.
Currently, for setInterval() work, I have to add each time :
clearInterval(timer);
But, it does not respect the time of one minute that I put and it sends way more requests than 1 per minute.
You will need to re-evaluate mySavedDatabase.isOn
I would just remove constant isOn, and use mySavedDatabase.isOn instaed.
Resolved.
Just pass the:
const isOn = mySavedDatabase.isOn;
In the setInterval() to get fresh data.

Trigger a function when a new document is created is not working

I have tried an increment counter example (https://github.com/firebase/functions-samples/tree/master/child-count) with Cloud Functions which is referencing Realtime Database, but not Firebase Firestore.
I am following firestore document and tried some changes. Still facing issue and not able to run this code for firestore new document. I am writing cloud function first time, and I'm not sure I've written this right.
exports.countlikechange = functions.database.ref('/posts/{postid}/likes/{likeid}').onWrite(
async (change) => {
const collectionRef = change.after.ref.parent;
const countRef = collectionRef.parent.child('likes_count');
let increment;
if (change.after.exists() && !change.before.exists()) {
increment = 1;
} else if (!change.after.exists() && change.before.exists()) {
increment = -1;
} else {
return null;
}
// Return the promise from countRef.transaction() so our function
// waits for this async event to complete before it exits.
await countRef.transaction((current) => {
return (current || 0) + increment;
});
console.log('Counter updated.');
return null;
});
I want to update count in parent document.
You should review the documentation for Firestore triggers. What you're writing is a Realtime Database trigger, because the function declaration is functions.database. You'll want to use functions.firestore instead. You're also using Realtime Database APIs instead of Firestore APIs. Your final solution that uses Firestore will look almost completely different than what you have now.

node.js setTimeout Loop Issue

I'm new to node.js.
I have tried to create a setTimeout that executes a database SELECT Query and repeats 3 seconds after processing the SELECT results has completed.
var newDBMessagesInterval = 3000; // 3 Seconds
(function newDBMessagesSchedule() {
setTimeout(function() {
dbNewMessagesQuery(function(dbResults,dbResultsLength) {
console.log(dbResults);
newDBMessagesSchedule();
});
}, newDBMessagesInterval)
})();
function dbNewMessagesQuery(callback) {
dbConnection.query("SELECT data1,data2,data3 FROM table WHERE condition=1;", function (dbError, dbResults, dbFields) {
if(dbResults.length > 0) {
callback(dbResults,dbResults.length);
}
});
callback();
}
It appears the setTimeout number of loops increases each time it runs (eg: first one console.log(dbResults), but then 2times and then 4 etc). Also I'm not sure if its waiting on the database SELECT to completed before trying to process the next time.
Looking for some advise on how to create this loop correctly with node.js
thx
Your dbNewMessagesQuery calls callback twice. Once synchronously, and once after the db query succeeds. You should just be calling it once after the query is done. With your current code, for every call to newDBMessagesSchedule, you queue up two more calls to run later.
function dbNewMessagesQuery(callback) {
dbConnection.query("SELECT data1,data2,data3 FROM table WHERE condition=1;", function (dbError, dbResults, dbFields) {
callback(dbResults, dbResults.length);
});
}
I'd also recommend not bothering to pass the length separately, and instead pass along the error if there is one. Currently you just assume there will never be an error.
function dbNewMessagesQuery(callback) {
dbConnection.query("SELECT data1,data2,data3 FROM table WHERE condition=1;", function (dbError, dbResults, dbFields) {
callback(dbError, dbResults);
});
}

Resources