Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cloud Functions for Firebase invoked many times

I am just trying out Cloud Functions for Firebase in an effort to move my firebase-queue workers over to the cloud functions. I added a simple function to add a last updated timestamp whenever I create a new node at a given ref. The function looks like this:

var functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

exports.setLastUpdatedTimestamp = functions.database.ref('/nodes/{nodeId}')
  .onWrite(event => {
    const original = event.data.val();
    console.log('Adding lastUpdatedTimestamp to node ', original.name);
    return event.data.ref.child('lastUpdatedAtFromFC').set(Date.now());
  });

I deployed this cloud function and added just one node from my app. I went to the Firebase Functions dashboard and see that the function has been invoked 169 times and I have no idea why. When I look at the logs, I see logs like attaching the function to all the past nodes as well.

Is it the case that the onWrite would behave kind of like child_added and run the function for all the existing entities as well?

Would this action be repeated every time I change and deploy the function again?

I was expecting it to run just once for the newly added node.

like image 264
Varun Gupta Avatar asked Mar 16 '17 13:03

Varun Gupta


People also ask

How many requests can a cloud function handle?

Under normal circumstances, the function scales up to 5 instances as it handles requests. When a new version of the function is deployed, the new version has its own max instances limit of 5.

How long can Firebase functions run for?

Time Limits60 minutes for HTTP functions. 10 minutes for event-driven functions.

What is the maximum execution time for a cloud function?

In Cloud Functions (1st gen), the maximum timeout duration is nine minutes (540 seconds). In Cloud Functions (2nd gen), the maximum timeout duration is 60 minutes (3600 seconds) for HTTP functions and 9 minutes (540 seconds) for event-driven functions.

How many requests can Firebase handle?

10 for single-document requests and query requests. 20 for multi-document reads, transactions, and batched writes. The previous limit of 10 also applies to each operation.

What is Cloud Functions for Firebase?

Please try again later. Cloud Functions for Firebase is a serverless framework that lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your JavaScript or TypeScript code is stored in Google's cloud and runs in a managed environment.

What is the difference between Firebase CLI and cloud build?

The Firebase CLI creates a .zip archive of the function code, which is then uploaded to a Cloud Storage bucket (prefixed with gcf-sources) in your Firebase project. Cloud Build retrieves the function code and builds the function source.

How can I view and search through my Firebase logs?

You can use the Firebase console to view and search through your logs. Get started setting up, creating, and deploying functions. Learn more about what you can do with functions. Try the Cloud Functions codelab.

How do I deploy a project to Firebase?

Write JavaScript code (or TypeScript code to transpile at deployment) to handle events from Firebase services, Google Cloud services, or other event providers. Use the local emulator to test your functions. Enable billing for your project and deploy your functions using the Firebase CLI.


2 Answers

This is a common mistake when writing functions that deal with database writes. When you handle the event for the initial write at a location, then do a second write back to that same location, that second write will trigger yet another event that will run the function again, and so on, which will be an infinite loop.

You need some logic in your function that determines if the second write event should not re-write to the database. That will stop the loop. In your case, you don't need a function to set the last update time. You can do that with a special value on the client to tell the server to insert the current time into a field.

https://firebase.google.com/docs/reference/js/firebase.database.ServerValue#.TIMESTAMP

like image 88
Doug Stevenson Avatar answered Sep 22 '22 18:09

Doug Stevenson


I had this exact issue. There are two questions here. How to stop an infinite loop once it's start. That's been answered. But the real question is how to add a lastUpdated date field to your objects using Firebase Cloud Function triggers.

Here's my attempt at handling the onWrite() loop issue.

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();


exports.onWriteFeatherUpdateSummary = functions.database.ref('/messages/{id}')
    .onWrite((change, context) => {
      // Grab the current value of what was written to the Realtime Database.
      let updated = change.after.val();
      // Grab the previous value of what was written to the Realtime Database.
      const previous = change.before.val();
      let isChanged = true;
      let isCreated = (previous === null); // object 'created'

      // Only when the object gets updated
      if (!isCreated) {
        // An object should never directly change the lastUpdated value, this is for trhe trigger only
        isChanged = (updated.lastUpdated === previous.lastUpdated);
      }

      console.log(`isChanged: ${isChanged} ; isCreated: ${isCreated}`);

      if(isChanged) {
        // Insert whatever extra data you wnat on the update trigger
        const summary = `This is an update!`;

        // Add a 'createdDate' field on the first trigger
        if (isCreated) {
          // Make sure your object has a createdDate (Date) before the lastUpdated (Date)!
          Object.assign(updated,
              {
                createdDate : admin.database.ServerValue.TIMESTAMP
              }
            );
        }

        // Add lastUpdated Date field on very update trigger (but not when you just changed it with the trigger!)
        Object.assign(updated,
            {
              summary : summary,
              lastUpdated : admin.database.ServerValue.TIMESTAMP
            }
          );
      }

      return change.after.ref.set(updated);
    });
like image 33
CPD Avatar answered Sep 20 '22 18:09

CPD