Reputation: 92
I am recently experiencing regular errors for my firebase cloud functions. Expecially it is a function which is triggered when a user makes a post, and copies the post into the feed collections of all followers of the user.
My Code looks like this:
exports.onCreatePost = functions.firestore
.document("/Posts/{userId}/UserPosts/{postId}")
.onCreate(async (snapshot, context) => {
const postCreated = snapshot.data();
const userId = context.params.userId;
const postId = context.params.postId;
// 1) Get all the followers of the user who made the post
const followerRef = admin
.firestore()
.collection("Followers")
.doc(userId)
.collection("FollowerIds");
return followerRef.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
const followerId = doc.id;
admin
.firestore()
.collection("Feed")
.doc(followerId)
.collection("FeedPosts")
.doc(postId)
.set(postCreated);
});
return null;
});
});
The functions has worked well until recently, but now some people have 100+ followers and the functions fails regularily with 2 errors:
Error: Process exited with code 16
Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
I know the first Error means something like Headers already sent but I can not figure out what the problem with my function is. I also know that there are some write limits for firebase but the function is basically just adding one doc into each followers Feed-collections so in my understanding the write limit should not be affected.
To summarize my database structure:
Feed (collection) userId (document) FeedPosts (subcollection) postId (document)
Followers (collection) userId (document) FollowerIds (subcollection) followerId (document)
Posts (collection) userId (document) UserPosts (subcollection) postId (document)
Upvotes: 0
Views: 1386
Reputation: 83058
You incorrectly manage the Cloud Function life cycle. Returning a Promise in background Cloud Functions calling asynchronous methods tells the Cloud Functions platform to wait until the promise is fulfilled or rejected before cleaning up the function. See https://firebase.google.com/docs/functions/terminate-functions for more detail.
Since, in the forEach()
loop, you are executing a variable number of calls to the asynchronous set()
method, you need to use Promise.all()
in order to get a unique Promise (which resolves when all of the promises returned by the calls to the set()
method have resolved) that you can then return, as follows:
exports.onCreatePost = functions.firestore.document("/Posts/{userId}/UserPosts/{postId}").onCreate(async (snapshot, context) => {
const postCreated = snapshot.data();
const userId = context.params.userId;
const postId = context.params.postId;
const followersQuerySnapshot = await admin.firestore().collection("Followers").doc(userId).collection("FollowerIds").get();
const promises = followersQuerySnapshot.docs.map( // followersQuerySnapshot.docs is an Array of QueryDocumentSnapshots
doc => admin
.firestore()
.collection("Feed")
.doc(doc.id)
.collection("FeedPosts")
.doc(postId)
.set(postCreated)
);
return Promise.all(promises); // <= Important, here return the promise returned by Promise.all()
});
});
Upvotes: 2
Reputation: 6919
exports.onCreatePost = functions.firestore.document("/Posts/{userId}/UserPosts/{postId}").onCreate(async (snapshot, context) => {
const postCreated = snapshot.data();
const userId = context.params.userId;
const postId = context.params.postId;
// 1) Get all the followers of the user who made the post
const followerRef = await admin.firestore().collection("Followers").doc(userId).collection("FollowerIds").get();
const promises = [];
followerRef.forEach((doc) => {
const followerId = doc.id;
promises.push(admin.firestore().collection("Feed").doc(followerId).collection("FeedPosts").doc(postId).set(postCreated)}));
});
await Promise.all(promises);
});
});
cloud function retracts resources once the request is served. So you have to use await to retrieve data first. please read more about async/await
Upvotes: 1