admin管理员组

文章数量:1431406

My application has a simple cloud function to keep track of created/updated timestamps. It looks like this:

export const objectChanges = functions
  .firestore
  .document('objects/{id}')
  .onWrite(event => {
    const patch: {created_at?: string, updated_at: string} = {
      updated_at: <string> event.timestamp
    };

    if (!event.data.previous) {
      patch.created_at = event.timestamp;
    }

    return event.data.ref.set(patch, {merge: true});
  });

As soon as I upload this function and create/modify an object in the list, it starts constantly ticking the updated_at. I'm guessing it's detecting the change it itself is making to the updated_at field. This behavior confuses me, considering the documentation shows an example of this kind of update.

Is there a nuance I'm missing, or is this a Firestore bug?

My application has a simple cloud function to keep track of created/updated timestamps. It looks like this:

export const objectChanges = functions
  .firestore
  .document('objects/{id}')
  .onWrite(event => {
    const patch: {created_at?: string, updated_at: string} = {
      updated_at: <string> event.timestamp
    };

    if (!event.data.previous) {
      patch.created_at = event.timestamp;
    }

    return event.data.ref.set(patch, {merge: true});
  });

As soon as I upload this function and create/modify an object in the list, it starts constantly ticking the updated_at. I'm guessing it's detecting the change it itself is making to the updated_at field. This behavior confuses me, considering the documentation shows an example of this kind of update.

https://firebase.google./docs/functions/firestore-events#writing_data

Is there a nuance I'm missing, or is this a Firestore bug?

Share Improve this question asked Jan 2, 2018 at 16:47 JonahJonah 10.1k5 gold badges49 silver badges79 bronze badges 1
  • 2 Looks like you're probably missing this part: "Note: Any time you write to the same document that triggered a function, you are at risk of creating an infinite loop. Use caution and ensure that you safely exit the function when no change is needed." It doesn't appear as though any of the examples demonstrate this. – Kevin B Commented Jan 2, 2018 at 16:56
Add a ment  | 

4 Answers 4

Reset to default 4

If you check the example given in the docs there is one condition that is checked to see if name is changed. If yes then only execute the following code. If its unchanged, it simply returns, like this:

// We'll only update if the name has changed.
// This is crucial to prevent infinite loops.
if (data.name == previousData.name) return;

So in your case also, you will need to check if the actual data of your object (all the fields except updated_at) is changed or not. If nothing else (apart from updated_at) is changed, you should simply exit the function.

Pay extra attention to the part of the documentation sample code that you referenced:

// We'll only update if the name has changed.
// This is crucial to prevent infinite loops.
if (data.name == previousData.name) return;

You'll need to figure out a way to detect when the update performed by your function triggers a subsequent update at the same location.

In the onUpdate trigger, add or update a timestamp field to the document with the latest time (admin.firestore.fieldValue.serverTimestamp()). For this example, I will call the timestamp variable time.

The onUpdate function gives you a before and after document. If the before.time == after.time, then you know the user made a change. If the before.time !== after.time, you know the cloud function made a change.

if(snap.before.data().time !== snap.after.data().time){
   // The cloud function altered the document.
   // Return to prevent recursion!
   return;
}

When you update the document from the client, also update the time variable with the latest time. This way, the cloud function doesn't terminate before executing the desired code.

when you're writing a function triggered by itself, but changing just that updated_at field, you can check if the previous updated_at field is within a certain range of the new updated_at field.

The following code sits within your cloud function

.onUpdate( async (change) => {

})


//exit if the last write happened within 5 seconds
if(change.after.data().updated_at - change.before.data().updated_at <= 5) {
    return;
}

        const date: Date = new Date();

        try {
            await db
                .collection("collection_name")
                .doc(change.after.id)
                .set({ updated_at: date }, { merge: true });
        } catch (e) {
            functions.logger.log("failed updating doc in collection_name: ", e);
            return false;
        }

return true;

I'm using 5 seconds because -I don't care if my updated timestamp is off by 5 seconds -I'm not sure in production how long latency could be (this condition works also with 1s, and the cloud function in the emulator ran about every ~0.015 s)

本文标签: javascriptFirebase cloud function object update creating recursionStack Overflow