Search code examples
javascriptfirebasegoogle-cloud-platformgoogle-cloud-functionsgoogle-cloud-pubsub

Firebase cloud function doesn't log correctly


I'm running a pubsub scheduled function on firebase functions which run every two mintues. I try to log the events collected by the snapshot loop but something doesn't go properly.

exports.check_event_ended_notification = functions.pubsub.schedule('every 2 minutes').onRun((context) => {
        let default_lineup = "default";
        let events = [];

        return admin.database().ref("Events").once('value')
            .then(snapshot => {
                console.log(("EVENTS number : " + snapshot.numChildren()));
                snapshot.forEach(event_snapshot => {
                    let event_key = event_snapshot.key;
                    let event = event_snapshot.val();
                    events.push(event_key);
                    console.log("EVENT_UID : " + event_key);
                    console.log(("EVENTS : " + events));
                    // iterate through all events and check if they're marked as ended
                    // if not, check if ended
                    let event_status = event[['ended_status']];
                    if (event_status === '0') {
                        // get event date
                        let event_day = event[['date']]['0'];
                        let event_month = event[['date']]['1'];
                        let event_year = event[['date']]['2'];
                        let event_full_date = event_year + "-" + event_month + "-" + event_day;
                        // get today date
                        let today = new Date();
                        let today_day = today.getDate();
                        let today_month = (today.getMonth() + 1);
                        let today_year = today.getFullYear();
                        let today_full_date = today_year + "-" + today_month + "-" + today_day;
                        //create date objects to compare
                        let event_date = new Date(event_full_date);
                        let today_date = new Date(today_full_date);

                        // if event date is in the past
                        // check to see if its marked as ended.
                        if (event_date.getTime() < today_date.getTime()) {
                            console.log("EVENT : past event :  " + event_key);
                            if (event[['ended_status']] === "0") {
                                console.log("EVENT : ended 0  :  " + event_key);
                                //mark as ended
                                // admin.database().ref("Events/"+event_key+"/ended_status").set("1");
                                //create notifications for participation artists
                                for (let artist_key in event[['lineup']]) {
                                    console.log("ARTIST before: " + artist_key);

                                    if (artist_key !== default_lineup) {
                                        console.log("ARTIST after: " + artist_key);

                                        let approved_invitation = event[['lineup']][artist_key]['approved_invitation'];
                                        let handshake_status = event[['lineup']][artist_key]['handshake_status'];
                                        let quit = event[['lineup']][artist_key]['quit'];
                                        let removed = event[['lineup']][artist_key]['removed'];
                                        let event_publish_status = event[['publish_status']];
                                        let owner_uid = event[['owner_uid']];

                                        if (approved_invitation === '1' && handshake_status === '1' && quit === '0' && removed === '0' && event_publish_status === '1') {
                                            return admin.database().ref("Notifications/" + artist_key + "/" + owner_uid + "/" + event['uid']).push({
                                                notification_type: "ended",
                                                seen_status: "0",
                                                timestamp: new Date().getTime()
                                            })
                                        }
                                    }
                                }
                            }
                        }
                    }

                    return null;
                })
            })
    }

while the numChildren() log at the beginning show there are 4 children under the snapshot (which is correct), the foreach() method seems to run only twice, collecting the first two children and adding them to the "events" list.

and the logs stop after this:

2020-07-29T14:22:00.593Z ? check_event_ended_notification: EVENTS number : 4
2020-07-29T14:22:00.593Z ? check_event_ended_notification: EVENT_UID : 3853c2db-f31a-4f46-8c1b-740ca4e3407b
2020-07-29T14:22:00.593Z ? check_event_ended_notification: EVENTS : 3853c2db-f31a-4f46-8c1b-740ca4e3407b
2020-07-29T14:22:00.594Z ? check_event_ended_notification: EVENT_UID : 4253c2db-f31a-4f46-8c1b-740ca4e3407s
2020-07-29T14:22:00.594Z ? check_event_ended_notification: EVENTS : 3853c2db-f31a-4f46-8c1b-740ca4e3407b,4253c2db-f31a-4f46-8c1b-740ca4e3407s

Solution

  • You are not correctly managing the life cycle of your Cloud Function. As explained in the doc, you need to "resolve functions that perform asynchronous processing (also known as "background functions") by returning a JavaScript promise".

    Also, in a Cloud Function, it is not recommended to use the on() method, which sets a listener, but the once() one, which "listens for exactly one event of the specified event type, and then stops listening.".

    So the following should do the trick:

    exports.check_event_ended_notification = functions.pubsub.schedule('every 2 minutes').onRun((context) => {
        let default_lineup = "default";
        let events = [];
    
        return admin.database().ref("Events").once('value')  // Note the return here
            .then(snapshot => {
    
                snapshot.forEach(event_snapshot => {
                    let event_key = event_snapshot.key;
                    let event = event_snapshot.val();
                    events.push(event_key)
                    console.log("EVENT_UID : " + event_key);
                    console.log(("EVENTS : " + events));
                });
                
                // Do something with events... We don't have enough details
                
                return null;  // Or return a promise
    
            });
    
    });
    

    Update following your comment

    If you want, in the forEach(), to write to the database (which is an asynchronous process), you should use Promise.all(), in order to return a promise when all the parallel calls to the asynchronous push() method are done.

    Here is a skeleton of the code, that you should adapt with all the details:

    exports.check_event_ended_notification = functions.pubsub.schedule('every 2 minutes').onRun((context) => {
        let default_lineup = "default";
        let events = [];
    
        return admin.database().ref("Events/").once('value')
            .then(snapshot => {
    
                const promises = [];
    
                snapShot.forEach(event_snapshot => {
                    let event_key = event_snapshot.key;
                    let event = event_snapshot.val();
                    events.push(event_key)
                    console.log("EVENT_UID : " + event_key);
                    console.log(("EVENTS : " + events));
    
                    promises.push(
    
                        admin.database().ref("Notifications/" + artist_key + "/" + owner_uid + "/" + event['uid']).push({
                            notification_type: "ended",
                            seen_status: "0",
                            timestamp: new Date().getTime()
                        })
    
                    );
    
                });
        
                return Promise.all(promises);   // This returns a promise!!
    
            });
    
    });