简体   繁体   English

我怎样才能正确地消除 firebase 云 function 的执行

[英]How can i debounce the execution of a firebase cloud function correctly

I have a Firebase Cloud Function that monitors changes to my Realtime Database based on a sample provided in Firebase's documentation .我有一个 Firebase Cloud Function,它根据 Firebase 文档中提供的示例监视对我的实时数据库的更改。

My function is working correctly and executes for every change as it was written to do.我的 function 工作正常,每一次更改都按其编写的方式执行。

With that said, and, as per Firebase's recommendation:话虽如此,并且根据 Firebase 的建议:

• Debouncing - when listening to realtime changes in Cloud Firestore, this solution is likely to trigger multiple changes. • 去抖动——当监听Cloud Firestore 中的实时变化时,该解决方案很可能会触发多个变化。 If these changes trigger more events than you want, manually debounce the Cloud Firestore events.如果这些更改触发的事件比您想要的多,请手动去抖动 Cloud Firestore 事件。

I would like to do just that.我想这样做。

Can anyone offer a good approach?任何人都可以提供一个好的方法吗?

If we look at this function based on Firebase's example:如果我们根据 Firebase 的示例查看此 function:

exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
            async (change, context) => {

  // Get the data written to Realtime Database
  const eventStatus = change.after.val();

  // Create a reference to the corresponding Firestore document
  const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);

  // re-read the current data and compare the timestamps.

  const statusSnapshot = await change.after.ref.once('value');
  const status = statusSnapshot.val();

  // If the current timestamp for this data is newer than
  // the data that triggered this event, we exit this function.

  if (status.last_changed > eventStatus.last_changed) {
    return null;
  }

  // Otherwise, we convert the last_changed field to a Date

  eventStatus.last_changed = new Date(eventStatus.last_changed);

  // write it to Firestore

  userStatusFirestoreRef.get().then((user: any) => {
    user.forEach((result: any) => {       
      result.ref.set(eventStatus, { merge: true })
    });
  });
  return;
});

How should i attempt to debounce its execution?我应该如何尝试去抖动它的执行?

Can i attempt to debounce the .onUpdate() event?我可以尝试去抖动.onUpdate()事件吗?

I originally thought the following would suffice:我最初认为以下内容就足够了:

functions.database.ref('/status/{uid}').onUpdate(
  debounce(async(change:any, context:any) => {
    ...
  }, 10000, {
    leading: true,
    trailing: false
  })
);

But, thanks to @doug-stevenson for pointing out that attempting to debounce the onUpdate event in this way will not work for the following reason:但是,感谢@doug-stevenson 指出尝试以这种方式去抖动 onUpdate 事件将不起作用,原因如下:

"That's not going to work because each invocation of the function might happen in a completely different server instance with no shared context." “那是行不通的,因为 function 的每次调用都可能发生在完全不同的没有共享上下文的服务器实例中。”

One way is to use a task scheduler (eg, Google Cloud Tasks).一种方法是使用任务调度程序(例如,Google Cloud Tasks)。 Rather than handling the event directly in the cloud function itself, you'll use the task scheduler to control when the event is handled.与其直接在云 function 本身中处理事件,不如使用任务调度程序来控制何时处理事件。

I've included two approaches: one for a debounce and one for a delayed throttle.我提供了两种方法:一种用于去抖,一种用于延迟油门。

Debounce去抖动

The idea is to enqueue a task for each cloud function invocation.这个想法是为每个云 function 调用排队一个任务。 If there is already a task scheduled for that entity, the existing task should be canceled.如果已经为该实体安排了任务,则应取消现有任务。

For example, if your debounce interval is 5 minutes, schedule each task 5 minutes into the future.例如,如果您的去抖间隔为 5 分钟,则将每个任务安排在未来 5 分钟后。 When each task is scheduled, cancel the previous task for that entity, if there is one.安排好每个任务后,取消该实体的前一个任务(如果有的话)。 When the task finally runs, it means there were no other cloud invocations within 5 minutes (ie, a successful debounce).当任务最终运行时,这意味着在 5 分钟内没有其他云调用(即成功的去抖动)。

Delayed Throttle延迟油门

A delayed throttle means that your event will be handled at most once per interval, at the end of the interval.延迟节流意味着您的事件在每个时间间隔最多处理一次,在时间间隔结束时。

The idea is: each time the cloud function runs, enqueue a task only if it's not a duplicate.这个想法是:每次云 function 运行时,仅当任务不是重复时才将其入队。 You'll want to come up with a task naming convention that lets you de-dupe redundant tasks.你会想要提出一个任务命名约定,让你删除冗余任务。

For example, you could append the entity id with the scheduled execution time.例如,您可以 append 具有计划执行时间的实体 ID。 When your cloud function runs, if there is already a scheduled task for that id and time, you can safely ignore that event.当你的云 function 运行时,如果已经有那个 id 和时间的计划任务,你可以放心地忽略那个事件。

Here's a code sample that handles events at most once per minute.这是一个每分钟最多处理一次事件的代码示例。

// round up to the nearest minute
const scheduleTimeUnixMinutes = Math.ceil(new Date().getTime() / 1000 / 60);
const taskName = id + scheduleTimeUnixMinutes.toString();
const taskPath = client.taskPath(project, location, queue, taskName);

// if there's already a task scheduled for the next minute, we have nothing
// to do.  Google's client library throws an error if the task does not exist.
try {
  await client.getTask({ name: taskPath });
  return;
} catch (e) {
  // NOT_FOUND === 5.  If the error code is anything else, bail.
  if (e.code !== 5) {
    throw e;
  }
}

// TODO: create task here

Since each event might be delivered more than once, you must track the provided event ID provided in context.eventId .由于每个事件可能会多次传递,因此您必须跟踪context.eventId中提供的事件 ID。 If you see a duplicate event, you know that it's being repeated.如果您看到重复的事件,您就知道它正在重复。

There are dozens of strategies to do this, and there is not just one right way to do it.有许多策略可以做到这一点,而且没有一种正确的方法可以做到这一点。 You could store the processed ID in a database or some other persistent storage, but you can't just store it in memory, since each function invocation can happen in complete isolation from each other.您可以将处理后的 ID 存储在数据库或其他一些持久性存储中,但不能只将其存储在 memory 中,因为每个 function 调用都可以在彼此完全隔离的情况下发生。

Also read about "idempotence", as this is the property of functions the behave the same way with each invocation.另请阅读“幂等性”,因为这是函数的属性,每次调用的行为方式都相同。

https://firebase.google.com/docs/functions/tips#write_idempotent_functions https://firebase.google.com/docs/functions/tips#write_idempotent_functions

https://cloud.google.com/blog/products/serverless/cloud-functions-pro-tips-building-idempotent-functions https://cloud.google.com/blog/products/serverless/cloud-functions-pro-tips-building-idempotent-functions

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何测量云 Function 中 Firebase 中特定代码块的执行时间? - How to measure execution time of a particular block of code in Cloud Function for Firebase? 我怎么知道我的 firebase 云 function 是否正常工作? - How can i know that my firebase cloud function is working or not? 如何从云端访问 firebase 自定义声明 function - How can I access firebase custom claims from a cloud function Firebase Cloud Function - 我该如何解决这个错误:ECONNRESET - Firebase Cloud Function - How can I solve this error: ECONNRESET Firebase:云 function 已正确部署但未被触发 - Firebase: Cloud function deployed correctly but not being triggered 如何从 TypeScript 中的可调用 Firebase Cloud Function 向我的 Unity 应用程序发送自定义 object? - How can I send custom object from my callable Firebase Cloud Function in TypeScript to my Unity app? Firebase Cloud Function,onCreate事件触发后如何访问父数据? - Firebase Cloud Function, how can I access parent data after onCreate event triggered? 如何在 typescript 编写的谷歌云 Function 中检索和使用 firebase 数据? - How can I retrieve and use firebase data in a Google Cloud Function written in typescript? 为什么我的 Firebase Cloud Function 不能使用“allAuthenticatedUsers”? - Why can't I use `allAuthenticatedUsers` for my Firebase Cloud Function? 如何从 Cloud Functions 访问和操作 Firebase 数据库? - How can I reach and manipulate Firebase Database from Cloud Functions?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM