Q: Firebase Extension 限制子节点

Q: Firebase Extension Limit Child Nodes

我是 运行 一个拥有 160k DAU 的 Firebase 应用程序,这是一款在线回合制游戏,使用 RTDB 在玩家之间发送更新。我的数据库负载在最繁忙的时候达到 60% 左右的峰值。

对我来说很长一段时间的问题是如何在比赛结束后“清理”比赛节点。当前结构:

root/games/$gameId

gameId由宿主在匹配过程中提供。之后双方都订阅了这个节点并发送更新。

我能做的是让两个客户端之一在游戏结束后删除节点,但这将是另一个(不可靠的)写入来增加数据库负载。我的主要目标是将“死游戏节点”保持在最低水平,从而降低存储成本(Blaze 计划)。我最近才知道扩展,但我很好奇我的“成本”是多少。

问题 1:此扩展的每次触发都算作对我的数据库的“写入”吗?鉴于一个实例每秒只允许写入 1000 次,这将进一步影响此计数(每秒会触发多次)。

Q2:在 youtube 信息视频中它说它“最适合”自动生成的 ID,但是需要吗? https://www.youtube.com/watch?v=i_u_6RknUro

我知道这个扩展实际上只是一个云函数,但我想知道它在幕后是如何工作的,然后才敢使用它:)

感谢您的帮助:)

所有 Firebase 扩展都是 open-source,因此如果您想了解 限制子节点 扩展的工作原理,您可以查看 source code link from its installation page

那里的代码:

export const rtdblimit = functions.handler.database.ref.onCreate(
  async (snapshot): Promise<void> => {
    logs.start();

    try {
      const parentRef = snapshot.ref.parent;
      const parentSnapshot = await parentRef.once("value");

      logs.childCount(parentRef.path, parentSnapshot.numChildren());

      if (parentSnapshot.numChildren() > config.maxCount) {
        let childCount = 0;
        const updates = {};
        parentSnapshot.forEach((child) => {
          if (++childCount <= parentSnapshot.numChildren() - config.maxCount) {
            updates[child.key] = null;
          }
        });

        logs.pathTruncating(parentRef.path, config.maxCount);
        await parentRef.update(updates);
        logs.pathTruncated(parentRef.path, config.maxCount);
      } else {
        logs.pathSkipped(parentRef.path);
      }

      logs.complete();
    } catch (err) {
      logs.error(err);
    }
  }
);

看起来像这个扩展名:

  • 在列表中创建新的子节点时触发。
  • 然后读取整个父节点以确定有多少个子节点。
  • 然后通过一次写入操作删除超过其最大值的任意数量的子项。

这与我对代码的回忆相符,因为我编写了它的第一个版本。 :)

针对您的问题:

Q1: Would every trigger of this extension count as a "write" to my database?

Cloud Functions 的触发发生在您将子节点写入数据库时​​。是写入算作写入,触发器本身发生 out-of-band。如果您阅读我链接的其余代码,您会发现它会删除所有超过最大值的子节点作为单次写入。

Q2: In the youtube information video it says it "works best" with auto-generated IDs, but is it needed?

在没有排序条件(const parentSnapshot = await parentRef.once("value"))的情况下读取节点,然后从该列表的开头删除节点。因此它假定要删除的节点位于列表的开头,这是按它们的键排序的。只要这也适用于您的数据,密钥来自何处并不重要。