I have the following code that gets a random Firestore document given some parameters
const { onSchedule } = require("firebase-functions/v2/scheduler");
const { logger } = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
var db = admin.firestore();
function getRandomFromCollection(collection, level, enabled) {
return new Promise(function (resolve, reject) {
logger.info("Starting random search")
let key = collection.doc().id;
logger.info("random key done");
collection.where("enabled", "==", enabled).where("level", "==", level).where("__name__", '>=', key).limit(1).get()
.then(snapshot => {
if (snapshot.size > 0) {
snapshot.forEach(doc => {
logger.info("done");
resolve(doc);
});
}
else {
collection.where("enabled", "==", enabled).where("level", "==", level).where("__name__", '<', key).limit(1).get()
.then(snapshot => {
snapshot.forEach(doc => {
logger.info("done");
resolve(doc);
});
})
.catch(err => {
logger.info("error");
reject(err);
});
}
})
.catch(err => {
logger.info("error");
reject(err);
});
});
}
exports.newQuestions = onSchedule({
schedule: "every day 9:55",
maxInstances: 1,
timeZone: "UTC"
},
async (event) => {
let collection = db.collection("questions");
getRandomFromCollection(collection, 1, false).then(doc => {
logger.info("Got question");
}).catch(err => {
logger.error(`ERROR! Something when wrong when generating question! Error: ${err}`)
})
});
I have ran this code many times in the functions emulator with no issue but when I deploy it to firebase to be ran once a day it seems to fail around every 1 in 5 calls.
When the error occurs the function logs shows the "Starting random search" and "Random key done" but then returns nothing else and the promise isn't resolved or rejected.
You don't correctly manage the life cycle of your Cloud Function, see the documentation or other SO answers.
Since you use the async
keyword I've re-written your CF as below, using async
/await
. (Do not hesitate to add comments to this answer if necessary, in order to possibly fine tune together this proposed solution).
async function getRandomFromCollection(collection, level, enabled) {
logger.info("Starting random search");
let key = collection.doc().id;
logger.info("random key done");
const snapshot = await collection
.where("enabled", "==", enabled)
.where("level", "==", level)
.where("__name__", ">=", key)
.limit(1)
.get();
if (snapshot.size > 0) {
// No need to use snapshot.forEach since
// there is only 1 doc in the QuerySnapshot
// if snapshot.size > 0
// snapshot.docs[0] is the unique DocumentSnapshot
return snapshot.docs[0];
} else {
const snapshot = await collection
.where("enabled", "==", enabled)
.where("level", "==", level)
.where("__name__", "<", key)
.limit(1)
.get();
if (snapshot.size > 0) {
return snapshot.docs[0];
}
}
}
exports.newQuestions = onSchedule(
{
schedule: "every day 9:55",
maxInstances: 1,
timeZone: "UTC",
},
async (event) => {
try {
let collection = db.collection("questions");
await getRandomFromCollection(collection, 1, false);
logger.info("Got question");
return true;
} catch (error) {
logger.error(
`ERROR! Something when wrong when generating question! Error: ${err}`
);
return true;
}
}
);
PS: You may be interested by this SO answer on "How to get random documents in a collection".