Cloud Functions updates too late firestore - node.js

im new at cloud functions
i just want to disable my targets after countdown end. this function works correctly but updates after 3 min after function finished
what am i missing?
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.timecontroller = functions.firestore.document("DigitalTargets/{digitalTargetID}").onCreate((snap, context) => {
const id = snap.id
const date = new Date(snap.data().endDate.toDate())
var countDownDate = date.getTime();
var myfunc = setInterval(function () {
var now = new Date().getTime();
var timeleft = countDownDate - now;
if (timeleft < 0) {
db.collection("DigitalTargets").doc(id).update({ isActive: false })
clearInterval(myfunc);
}
}, 1000);
})

Since you pay for the time that your Cloud Functions code executes, the container tries to execute your code and terminate as quickly as possible. Unless you tell it otherwise, that means that it terminates the code as soon as the final statement before the closing } executes.
But since you are executing an asynchronous operation with your setInterval call, the code actually needs to continue to run after the closing }.
To allow that you'll need to return a promise that resolves when the code is complete. Something like:
exports.timecontroller = functions.firestore.document("DigitalTargets/{digitalTargetID}").onCreate((snap, context) => {
const id = snap.id
const date = new Date(snap.data().endDate.toDate())
var countDownDate = date.getTime();
return new Promise((resolve, reject) => { // 👈 This tells Cloud Functions to wait
var myfunc = setInterval(function () {
var now = new Date().getTime();
var timeleft = countDownDate - now;
if (timeleft < 0) {
db.collection("DigitalTargets").doc(id).update({ isActive: false })
clearInterval(myfunc);
resolve(); // 👈 This tells Cloud Functions that you're done
}
}, 1000);
})
})
I recommend reading (and watching the videos in) the Firebase documentation on asynchronous behavior in Cloud Functions.

Related

how to change Firestore document data using Google Cloud Functions in node.js

I am trying to write a google cloud function that will check all documents under a collection in every minute and if a boolean parameter after 15 minutes later still false, it will change another document parameter.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.scheduledFunctionAppointment =
functions.pubsub.schedule('every 1 minutes').onRun((context) => {
const timestamp = FieldValue.serverTimestamp()
return getReceivers(mUserGroup).then((snapshot) => {
snapshot.forEach(doc => {
const mAppointmentObj = doc.data();
const mAppointmentCreationDate = mAppointmentObj.appointmentCreationDate;
const mAppointmentDate = mAppointmentObj.appointmentDate;
const mChaplainId = mAppointmentObj.chaplainId;
const mAppointmentId = mAppointmentObj.appointmentId;
const mIsAppointmentPaymentDone = mAppointmentObj.isAppointmentPaymentDone;
if (timestamp-mAppointmentCreationDate >= 900000){
if(mIsAppointmentPaymentDone === false){
admin.firestore().collection('chaplains').doc(mChaplainId).collection('chaplainTimes').doc('availableTimes').update({
mAppointmentId: {
isBooked: false
}
});
}
}
});
});
})
async function getTaskAppointments() {
const snapshot = await admin.firestore().collection('appointment_temporary').get();
return snapshot;
}
the code should check the appointment_temporary collection in every 1 minute. In every check, it will take the server time and appointment creation date. If after 15 minutes later, isAppointmentPaymentDone still false, it will make isBooked false that under availableTimes map data.
I wrote a function but it gives error. I am a mobile developer. I am not familiar with the node.js. Thank you very much for the help in advance.

Firebase function taking incorrect execution path, log messages stop

I have a function, that among other things calls a function returning a boolean value to determine if the passed in date is greater than the current Date:
function isExpired(expirationDate) {
const date = +new Date(expirationDate);
const now = +new Date();
const expired = (date < now);
functions.logger.log("date expired: ", expired);
return expired;
}
You'll notice in the helper function, I'm logging whether the date is expired or not. For some reason, that log message never appears either in the Firebase console or the CLI. I'm calling it like this:
const functions = require("firebase-functions");
var admin = require('firebase-admin');
const db = admin.database();
exports.generateCode = functions.https.onCall(async (data, context) => {
var expirationDate;
await joinCodeRef.once("value", snapshot => {
expirationDate = snapshot.val().expiresOn;
})
if (isExpired(expirationDate)) {
// do some stuff
} else {
// return some stuff
}
}
isExpired always evaluates to false even when the passed-in date is less than the current date. No log messages past if(isExpired) will appear in any logs either, BUT the proper values are returned in the else block meaning execution is being allowed to continue. I must be doing something wrong, but I'm not seeing it...
Not sure if it's the cause of your problem, but you should not combine using await with using callbacks.
exports.generateCode = functions.https.onCall(async (data, context) => {
const snapshot = await joinCodeRef.once("value");
const expirationDate = snapshot.val().expiresOn;
if (isExpired(expirationDate)) {
// do some stuff
} else {
// return some stuff
}
}

Firebase Cloud Functions Cron Async Func Multiple Calls

I have a cloud function. It is triggered by an App Engine Cron job. It triggers my Firebase Cloud Function every hour with a Google Cloud Pub/Sub. I fetch my Firebase Realtime Database once, and loop for every value. The problem is my main.async function called multiple times. I use an i variable for loop and my console logs more i count than my database length. I mean that if my database length is 4, but for loop runs 8 or 15 or 23 times. This values change randomly. I want to loop for my each database value,fetch some data on internet,and when it is done, go for next value. Here is the code:
use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var request = require('request-promise').defaults({ encoding: null });
var fs = require('fs');
// Get a reference to the Cloud Vision API component
const Vision = require('#google-cloud/vision');
const vision = new Vision.ImageAnnotatorClient();
var os = require("os");
var databaseArray = [];
var uniqueFilename = require('unique-filename')
exports.hourly_job = functions.pubsub
.topic('hourly-job')
.onPublish((event) => {
console.log("Hourly Job");
var db = admin.database();
var ref = db.ref("myData")
ref.once("value").then(function(allData) {
allData.forEach(function(deviceToken) {
deviceToken.forEach(function(firebaseIDs) {
var deviceTokenVar = deviceToken.key;
var firebaseIDVar = firebaseIDs.key;
var firstvalue = firebaseIDs.child("firstvalue").val();
var secondvalue = firebaseIDs.child("secondvalue").val();
var items = [deviceTokenVar, firebaseIDVar, firstvalue, secondvalue];
databaseArray.push(items);
});
});
return databaseArray;
}).then(function (databasem) {
main().catch(console.error);
});
return true;
});
const main = async () => {
var i;
for (i = 0; i < databaseArray.length; i++) {
console.log("Database Arrays " + i + ". elements: ");
if (databaseArrayfirst != "") {
var apiUrl = "http://api.blabla;
try {
const apiBody = await request.get(apiUrl);
///////////////////////////vison start//////////////////////
const visionResponseBody = await vision.documentTextDetection(apiBody)
var visionResponse = visionResponseBody[0].textAnnotations[0].description;
...some logic here about response...
/////////////////////////////////////////////////
var getdatabasevar = await admin.database().ref("myData/" + databaseArrayDeviceToken + "/" + databaseArrayFirebaseID);
await getdatabasevar.update({
"firstvalue": visionResponse
});
/////////////////////////////////////////////////
} catch (error) {
console.error(error);
}
///////////////////////////vison end//////////////////////
}
};
return true;
};
Thank you.

How to check the value a property of items from the Firebase database before deleting items

I have a Firebase Data Model:
Database:
tweets
favorite:Bool
tweetText: String
userID: String
timestamp: Double
I want to run a method that says "delete tweets, that aren't favorited, within the last 2 hours"
Here's what I have in the console (//'s are where I need help):
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp();
exports.deleteOldItems = functions.database.ref(‘/tweets/{tweet}’)
.onWrite((change, context) => {
var ref = change.after.ref.parent;
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value', function(snapshot) {
var updates = {};
snapshot.forEach(function(child) {
// Delete Tweet BUT ONLY IF FAVORITE == True????
//Help here
updates[child.key] = null
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
You're looking for something like this:
exports.deleteOldItems = functions.database.ref(‘/tweets/{tweet}’)
.onWrite((change, context) => {
var ref = change.after.ref.parent;
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value', function(snapshot) {
var updates = {};
snapshot.forEach(function(child) {
if (!child.val().favorite) {
updates[child.key] = null
}
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
})
If you are new to (using Firebase from) JavaScript, Cloud Functions for Firebase is not the easiest way to learn it. I recommend first reading the Firebase documentation for Web developers and/or taking the Firebase codelab for Web developer. They cover many basic JavaScript, Web and Firebase interactions. You could also use the Admin SDK in a local Node.js process, which can be debugged with a local debugger. After those you'll be much better equipped to write code for Cloud Functions too.

Firebase database querying taking to much time in Cloud Function

I use nodejs as a backend hosted on the firebase functions to query my firebase database, I have this code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({origin: true});
admin.initializeApp(functions.config().firebase);
const express = require('express')
exports.getAjax = functions.https.onRequest((request, response) => {
cors(request, response, () => {
console.log("request.body :", JSON.stringify(request.body));
console.log("request.query :", JSON.stringify(request.query));
var date = {
startDate: request.body.startDate,
endDate: request.body.endDate
}
var db = admin.database();
var logsDbPath = 'logs';
var usersDbPath = 'users';
var ref = db.ref(logsDbPath);
var tags;
db.ref(usersDbPath).once('value').then(function(tagsSnapshot) {
tagsSnapshot.forEach(function(tagSnapshot) {
var tagId = tagSnapshot.key;
tagSnapshot.forEach(function(sessSnapshot) {
var userSessId = sessSnapshot.key;
var userInfo = sessSnapshot.val();
});
});
tags = JSON.parse(JSON.stringify(tagsSnapshot.val()));
console.log(tags);
});
});
});
My main problem is that console.log(tags); and actually finishing the query and get the value for tags took ~38 sec in this case (sometimes takes ~ 1 min), I will put picture bellow:
And that object it's not that big at all, like 100 rows with 4 properties each, I don't think it should take that much, probably I did something wrong, but where?, I also have to write a promise or a callback in order to send that tags var response on the front end, cause the function finish before I get any value on tags(see picture), I come from a PHP background and this asynchronous javascript concept it's quite new for me.
You're not sending any response back to the client, which means that the function will keep running until it times out.
To make sure the function runs only for as long as needed, send a response to the client when you've loaded the data:
exports.getAjax = functions.https.onRequest((request, response) => {
cors(request, response, () => {
console.log("request.body :", JSON.stringify(request.body));
console.log("request.query :", JSON.stringify(request.query));
var date = {
startDate: request.body.startDate,
endDate: request.body.endDate
}
var db = admin.database();
var logsDbPath = 'logs';
var usersDbPath = 'users';
var ref = db.ref(logsDbPath);
var tags;
db.ref(usersDbPath).once('value').then(function(tagsSnapshot) {
tagsSnapshot.forEach(function(tagSnapshot) {
var tagId = tagSnapshot.key;
tagSnapshot.forEach(function(sessSnapshot) {
var userSessId = sessSnapshot.key;
var userInfo = sessSnapshot.val();
});
});
tags = JSON.parse(JSON.stringify(tagsSnapshot.val()));
console.log(tags);
// Send the response to the client, which also ends the function
response.status(200).send(tags);
});
});
});
Aside from that it's hard to say much. Keep in mind that Cloud Functions is in beta and is constantly changing, we have no way of knowing whether this is a cold start or warm start, and we can't see your project to inspect what's going on.
If you'd like to have more help here, I recommend that you try to reproduce the problem in a more troubleshoot-friendly environment. For example, can you reproduce the perfor,ance problem with a local node.js process?

Resources