【问题标题】:How to make callable google cloud function idempotent如何使可调用的谷歌云函数幂等
【发布时间】:2022-12-20 03:13:01
【问题描述】:

我有一个谷歌云功能,可以将通知发送到 firebase 主题。 该功能工作正常,直到突然,它开始同时发送多个通知 2 或 3。联系 Firebase 支持团队后,他们告诉我应该使函数幂等,但我不知道怎么做,因为它是一个可调用函数。 更多详情,this is a reference question containing more detail about the case。 下面是函数的代码。

  • 更新 2

这是 admin sdk 中的一个错误,他们在上一个版本中解决了它。

更新

该函数已经是幂等的因为它是一个事件驱动函数 上面的链接包含函数日志,因为它只运行一次。 经过 2 个月的往返,它出现了firebase admin sdk 的问题函数代码getMessaging().sendToTopic() 已重试 4 次,并且原始请求因此在抛出错误并终止函数之前默认重试 5 次。所以重复通知的原因是 admin sdk 由于某种原因有时无法到达 FCM 服务器。它尝试向所有 subs 发送通知,但在中途或在发送所有通知之前它收到错误,因此它从开始,所以有些用户会收到一个通知,有些用户会收到 2、3、4。 现在的问题是如何防止这些默认重试或如何使重试从出现错误的地方继续。可能我会问一个单独的问题。 现在我通过防止来自接收者(移动客户端)的重复通知做了一个天真的解决方案。如果它在一分钟内收到多个通知具有相同的内容,则只显示一个。

const functions = require("firebase-functions");

// The Firebase Admin SDK to access Firestore.
const admin = require("firebase-admin");
const {getMessaging} = require("firebase-admin/messaging");
const serviceAccount = require("./serviceAccountKey.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://mylinktodatabase.firebaseio.com",
});

exports.callNotification = functions.https.onCall( (data) => {
  // Grab the text parameter.
  const indicator = data.indicator;
  const mTitle = data.title;
  const mBody = data.body;
  // topic to send to
  const topic = "mytopic";
  const options = {
    "priority": "high",
    "timeToLive": 3600,
  };
  let message;
  if (indicator != null ) {
    message = {
      data: {
        ind: indicator,
      },
    };
  } else {
    message = {
      data: {
        title: mTitle,
        body: mBody,
      },
    };
  }

  // Send a message to devices subscribed to the provided topic.
  return getMessaging().sendToTopic(topic, message, options)
      .then(() => {
        if (indicator != null ) {
          console.log("Successfully sent message");
          return {
            result: "Successfully sent message", status: 200};
        } else {
          console.log("Successfully sent custom");
          return {
            result: "Successfully sent custom", status: 200};
        }
      })
      .catch((error) => {
        if (indicator != null ) {
          console.log("Error sending message:", error);
          return {result: `Error sending message: ${error}`, status: 500};
        } else {
          console.log("Error sending custom:", error);
          return {result: `Error sending custom: ${error}`, status: 500};
        }
      });
});

【问题讨论】:

  • 你有没有检查我的answer
  • 抱歉来晚了,我正在调查其他应用程序/错误。

标签: javascript google-cloud-functions firebase-admin idempotent


【解决方案1】:

在这个博客中Cloud Functions pro tips: Building idempotent functions, 展示了如何使用两种方法实现函数的幂等性:

使用您的活动 ID

解决此问题的一种方法是使用事件 ID,这是一个唯一标识触发后台函数的事件的数字,并且——这一点很重要——在同一事件的函数重试中保持不变。

要使用事件 ID 解决重复问题,首先要从通过function parameters 访问的事件上下文中提取它。然后,我们将事件 ID 用作文档 ID,并将文档内容写入 Cloud Firestore。这样,重试的函数执行不会创建新文档,只会覆盖具有相同内容的现有文档。同样,一些外部 API(例如 Stripe)接受幂等密钥以防止数据或工作重复。如果您依赖此类 API,只需提供事件 ID 作为幂等键即可。

重试的新租约

虽然这种方法消除了绝大多数对函数重试的重复调用,但并行运行的两个重试执行可能会多次执行关键部分的可能性很小。为了几乎消除这个问题,您可以使用租用机制,它允许您在特定时间内独占地执行函数的非幂等部分。在此示例中,第一次执行尝试获得租约,但第二次尝试被拒绝,因为租约仍由第一次尝试持有。最后,在第一次失败后的第三次尝试重新取得租约并成功处理事件。

要将此方法应用于您的代码,只需在发送电子邮件之前运行一个 Cloud Firestore 事务,检查事件是否已被处理,同时存储当前执行尝试拥有发送电子邮件独占权的时间。其他并发执行尝试将被拒绝,直到租约到期,从而消除所有意图和目的的所有重复。

此外,如其他question所述:

问:是否需要使这些 onCall 函数幂等,或者它们永远不会执行重试?

A: 调用onCall函数不会自动重试。就重试策略达成一致取决于您的应用程序的客户端和服务器端代码。

也可以看看:

【讨论】:

  • 这是一个方便的答案,但有很多问题不适合我的情况。 - 该函数不会从服务器端或客户端重试。 (已检查)- 我的一个朋友有一个 PHP 服务器有同样的问题(冗余通知)。 - Firebase 的问题,现在正在与支持团队一起调查。 * 使事件驱动函数(可调用)幂等的一种方法是向函数发送计数器或 ID,并使用此锚点(计数器或 ID)签入函数代码//我还没有测试它,但我我会在测试时更新我的​​评论。
  • @MostafaOnaizan 您是否有机会使用计数器/ID 进行一些测试?
  • 是的,我做到了,稍后我会分享代码,但问题在 admin sdk 中。 Firebase 让我为他们模拟整个情况,以便他们可以处理。但这对我来说太过分了
  • 我更新了问题,你可以看看
猜你喜欢
  • 2021-06-08
  • 2020-12-12
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 2019-02-15
  • 2018-12-07
  • 2018-10-02
  • 2021-01-22
相关资源
最近更新 更多