提高 Hasura 订阅性能

Improve Hasura Subscription Performance

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

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

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

我们想到的解决方案:

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

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

    感谢您的输入!

前言

OP 问题很宽泛,一般情况下无法回答。

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

系统的简短描述

系统用户:上传文件、提取信息、准备新文件、在过程中交谈(类似 IM 的功能)、有试图减轻重复性任务负担的 AI 机器人、与外部交换数据的服务系统。

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

开始是什么

起初是:

  • 程序员为应用程序所需的全部数据编写了一个 graphql 查询
  • 已将 query 更改为 subscription
  • 完成

前 2-3 个月没问题然后:

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

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

  • 有些事情的成本是理所当然的:JOIN、存在谓词、数据本身显着增长
  • 网络部分:您可以优化数据库,但仅传输所有需要的数据是有成本的

订阅优化

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

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

一个。订阅指示实体已更改的单个字段

例如

而不是:

subscription{
  document{
    id
    title
    # other fields
    pages{  # array relation
    ...
    } 
    tasks{ # array relation
    ...
    } 
    # multiple other array/object relations
    # pagination and ordering
  }

那 return 数千行。

创建一个函数:

  • 接受 hasura_session - 因此每个用户的结果都是单独的
  • return只有一个字段:max_change_date

所以变成了:

subscription{
  doc_change_date{
    max_change_date
  }
}

总是一行,总是一个字段

乙。更改应用程序逻辑

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

备注

如果订阅功能有时return误报那是完全可以的。

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

例如

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

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

但这些更改就像每个组织在一分钟内 ones/twice。

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

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

第二步。多重订阅

第一步很关键

而且 hasura 有多个订阅:https://hasura.io/docs/latest/graphql/core/databases/postgres/subscriptions/execution-and-performance.html#subscription-multiplexing

所以理论上 hasura 可以足够聪明并解决你的问题。

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

在我们的案例中:

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

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

但实际上只订阅一次可能会有所帮助:“嘿!有适合你的变化!”

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

备注

我们考虑了 2 种多路复用订阅格式:

  • 一个。订阅 return 只是一个字段 {max_change_date},对所有实体
  • 是累积的
  • 乙。订阅 return 更精细的结果:{doc_change_date, dossier_change_date, msg_change_date}

现在“A”适合我们。但也许我们以后会改成“B”。

第三步。我们对 hasura 2.0

的不同之处

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

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

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

  • 您大概在 table
  • 中为函数调用定义了缓存
  • 然后在函数调用时首先查看缓存
  • 如果不存在:将值添加到缓存
  • return 来自缓存的结果

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

备注

实际上可以在不等待 hasura 2.0 的情况下做到这一点,但它需要 postgresql 方面的技巧:

  • 您创建了真正起作用的 VOLATILE 函数
  • 和另一个定义为 STABLE 的函数调用 VOLATILE 函数。这个函数可以注册到hasura

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

谁知道呢,也许未来的 postgresql 版本或更新会使它变得不可能。

总结

关于这个话题我现在能说的就这些了。

其实一年前我会很高兴读到类似的东西。

如果有人看到一些陷阱 - 请发表评论,我很乐意听到意见和可能的替代方法。

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