提高 Hasura 订阅性能

Flo*_*rca 4 postgresql graphql graphql-subscriptions hasura

我们开发了一个依赖于用户之间实时交互的网络应用程序。我们使用 Angular 作为前端,使用 Hasura 和 Postgres 上的 GraphQL 作为后端。我们注意到,当超过 300 个用户同时活跃时,我们会遇到严重的性能损失。
因此,我们希望改进我们的订阅设置。
我们认为可能的问题可能是:

  1. 订阅过多
  2. 订阅太大且复杂,订阅中有太多分叉

关于 1. 每个用户在使用 Web 应用程序时大约有 5-10 个活跃订阅。关于 2. 我们的订阅很复杂,因为我们将最多 6 个表连接在一起。

我们想到的解决方案:

  1. 使用更多查询并限制在完全需要实时的字段上使用订阅。
  2. 将复杂的查询/订阅拆分为多个较小的查询/订阅。

    我们是否遗漏了另一个可能的原因?我们还可以使用什么来提高整体性能?

    谢谢您的意见!

Ale*_* Yu 12

前言

OP问题非常广泛,不可能在一般情况下得到回答。

因此,我在这里描述的内容反映了我在优化订阅方面的经验 - 由 OP 决定是否反映他们的情况。

系统简短描述

系统用户:上传文档、提取信息、准备新文档、在处理过程中进行对话(类似 IM 的功能),有人工智能机器人试图减轻重复任务的负担,以及与外部系统交换数据的服务。

有很多实体,人类和机器人参与者之间有很多交互。再加上相当复杂的授权规则:数据的可见性取决于组织、部门和文档内容。

开始时发生了什么

起初是:

  • 程序员为应用程序所需的全部数据编写了一个 graphql 查询
  • 变成querysubscription
  • 结束

前2-3个月还可以,然后:

  • 查询变得更加复杂,然后更加复杂
  • 订阅量增长
  • UI 变得滞后
  • 数据库实例始终接近 100% 负载。即使在晚上和周末。因为有人没有关闭应用程序

首先我们对查询本身进行了优化,但这还不够:

  • 有些东西成本高昂:JOIN、存在谓词、数据本身显着增长
  • 网络部分:您可以优化数据库,但仅仅传输所有需要的数据就有成本

订阅优化

步骤一、拆分订阅:订阅变更日期,查询变更

而不是将整个数据分割成多个部分的复杂订阅:

A. 订阅单个字段,表明实体已更改

例如

代替:

subscription{
  document{
    id
    title
    # other fields
    pages{  # array relation
    ...
    } 
    tasks{ # array relation
    ...
    } 
    # multiple other array/object relations
    # pagination and ordering
  }
Run Code Online (Sandbox Code Playgroud)

返回数千行。

创建一个函数:

  • 接受 hasura_session - 因此每个用户的结果都是单独的
  • 仅返回一个字段:max_change_date

于是就变成了:

subscription{
  doc_change_date{
    max_change_date
  }
}
Run Code Online (Sandbox Code Playgroud)

始终为一行且始终为一个字段

B. 应用逻辑的改变

  • 查询全部数据
  • 订阅doc_change_date
  • 记住值max_change_date
  • 如果 max_change_date 更改 - 重新查询数据

笔记

如果订阅功能有时返回误报,那绝对没问题。

无需将所有谓词从源查询复制到订阅函数。

例如

在我们的例子中:数据的可见性取决于组织和部门(甚至更多)。

因此,如果一个部门的用户创建/修改文档 - 此更改对其他部门的用户不可见。

但这些变化就像每个组织在一分钟内发生/两次。

因此,对于订阅功能,我们可以忽略这些粒度并max_change_date针对整个组织进行计算。

拥有更快、更粗略的订阅功能的好处是:它会更频繁地触发数据刷新,但整体成本会更低。

第二步。多重订阅

第一步是至关重要的一步。

Hasura 具有多路复用订阅:https://hasura.io/docs/latest/graphql/core/databases/postgres/subscriptions/execution-and-performance.html#subscription- Multiplexing

所以理论上哈苏拉足够聪明并能解决你的问题。

但如果您认为“显式比隐式更好”,您还可以采取另一步。

在我们的例子中:

  • 用户上传文件
  • 将它们合并到档案中
  • 创建新的文档类型
  • 与他人交谈

所以订阅就变成了:doc_change_date、dossier_change_date、msg_change_date 等等。

但实际上,只订阅一项可能会有好处:“嘿!有适合您的更改!”

因此,应用程序只创建一个订阅,而不是多个订阅。

笔记

我们考虑了两种多路订阅格式:

  • A. 订阅仅返回一个字段,{max_change_date}该字段对所有实体都是累加的
  • B. 订阅返回更精细的结果:{doc_change_date, dossier_change_date, msg_change_date}

现在“A”为我们工作。但也许我们将来会改成“B”。

第三步。我们对 Hasura 2.0 有何不同之处

那是我们还没有尝试过的。

Hasura 2.0 允许注册 VOLATILE 函数进行查询。

这允许在数据库中创建具有记忆功能的函数:

  • 您大概在表中为函数调用定义了一个缓存
  • 然后在函数调用时,您首先查看缓存
  • 如果不存在:将值添加到缓存
  • 从缓存中返回结果

这可以进一步优化订阅功能和查询功能。

笔记

实际上,无需等待 hasura 2.0 就可以做到这一点,但它需要 postgresql 方面的技巧:

  • 你创建了真正有效的 VOLATILE 函数
  • 另一个定义为 STABLE 的函数调用 VOLATILE 函数。该功能可以在hasura中注册

它有效,但很难推荐这个技巧。

谁知道呢,也许未来的 postgresql 版本或更新会让这一切变得不可能。

概括

这就是我现在能就这个话题说的一切。

事实上,我很高兴一年前读到类似的东西。

如果有人看到一些陷阱 - 请发表评论,我很高兴听到意见,也许还有其他方法。

我希望这个解释能够帮助某人或至少激发人们思考如何以其他方式处理订阅。