跳到主要内容

同步引擎

同步引擎

Bolt 内置一个 原生同步引擎 ——不是插件、轮询循环,也不是第三方复制层。本页介绍它在底层如何工作:实时查询 Machine、guest 的 connect 与 extendPrefix、仅由宿主调用的 advance,以及承载它们的共享宿主核心。应用所编写的读写 API 参见 实时数据。

心智模型:你的查询是活的

把每一条实时查询想成服务器替你 持续作答 的一个悬而未决的问题——浏览器里没有第二份数据库。你的应用读取自己声明过的答案,通过普通集合命令写入,而任何人的提交一旦触及某个答案,引擎立刻将其纠正。

        SERVER DATABASE (Postgres)                     THIS BROWSER
        ┌────────────────────────────────┐            ┌────────────────────────────┐
        │ collections                    │            │ the sync Machine —         │
        │ policies · write contracts     │            │ versioned prefixes this    │
        │ approvals · audit              │            │ tab registered, plus an    │
        │ SyncChange capture             │            │ in-memory write overlay    │
        └────────────────────────────────┘            └────────────────────────────┘
              ▲                                            ▲
              │  client mutation pipeline                  │
              └────────────────────────────────────────────┘

        sync.connect registers prefixes; sync.extendPrefix grows them;
        the host calls sync.advance after a commit and pumps apply
        frames over the standing stream

两条流让答案保持真实。 下行: 每一次提交都以一帧 apply 推送——该提交产生的前缀增量、重置与写入结果一并送达。 上行: 你的写入以普通集合命令传输,由服务器提交或拒绝——服务器始终是权威。

在 Colony 上,这一机制的服务器端运行在你的租户运行时中,经由代理的 HTTP 传输;引擎本身是 Bolt 的关注点,在任何宿主上行为一致。

答案如何保持最新

服务器不会被定时轮询。每条已提交的变更会捕获 SyncChange 事实(链接与路由值,而不是仅按 id 唤醒)以及同一事务中的写入结果。没有 changelog 游标、outbox,也没有已持有 id 的摘要。一次提交会唤醒索引在变更集合下的计划,推送随即开始:

  someone commits a mutation
        │  SyncChange facts + write outcome in the SAME transaction
        ▼
  COMMIT  ──►  the host awaits the lane; the guest sync.advance-s
               affected prefixes against the commit's change list
                                    │
                                    ▼
        keyed delta  ──►  removeIds + put at a final index
                           — or a reset that re-registers
                                    │
                                    ▼
        event: apply   { updates, resets, outcomes }   one reducer event:
        the Machine applies prefix deltas, settles the writes, the
        UI updates — nothing re-runs by hand
  • 每一条增量都带着 版本栅栏 :仅当客户端保留的版本等于更新的 fromVersion,且 toVersion 是下一个整数时才会应用。否则连接会重启。
  • 一次 重置 始终合法:浏览器丢弃该前缀并重新注册。计划键或权限不匹配、前缀损坏,或触及字节/行上限,都会重置——唤醒时不会改发一份完整答案补丁。
  • 每份答案在发送前都会在 SQL 中按读取者的 策略范围 过滤,主体不可读的列则按直接读取所走的同一条规则做掩码——客户端永远收不到策略拒绝的行,而已持有的行在授权被撤销后,只能存活到下一次与策略相关的提交。
  • 空闲的流不产生成本,因为没有任何东西依赖定时器:在有提交推过它之前,连接始终是安静的。

连接、重连与多标签页

  FIRST CONNECT                    RECONNECT / RESET
  ─────────────                    ─────────────────
  sync.connect registers every     the same command re-registers
  live prefix and returns rows     prefixes the browser dropped
  plus the plan the host files
        │                          │
  sync.extendPrefix grows the      a missed wake closes the
  viewer's loaded prefix           stream; there is no changelog
  without bumping version          to replay
        │                          │
  the stream carries apply         a frame that does not continue
  frames: updates, resets,         every fromVersion restarts
  outcomes                         the link
  • sync.connect 携带每条查询的 key、input 与 requestedPrefix,以及已分离的键和仍在途的写入 id。它解析每个请求的前缀,返回行以及宿主将登记的计划。重连与重置使用同一条命令。
  • sync.extendPrefix 在不提升版本的情况下增长查看者已加载的前缀。错过一次唤醒会关闭流;没有 changelog 可供重放。
  • 每个浏览器配置文件共享一条 EventSource,跨标签页与工作区复用。宿主按 planKey 登记一份计划;多条连接可以作为查看者挂上,各自有不同的 loadedPrefix 长度。
  • 重连会重新解析。每个作用域的注册表状态是 O(活跃计划);浏览器不持久保存任何内容。
不同于 bolt sync 或 Studio 预览同步
Bolt 同步引擎 ——运行中的租户应用里的实时数据查询与集合写入。
bolt sync CLI ——在构建前生成 .norbital/ 类型与注册表的文件系统编译器同步。
工作区工作室预览同步 ——应用 DDL 并记录不可变发布产物的发布产物构建。参见 工作区工作室。

线上发生了什么

Colony 对外提供专用的同步路由。浏览器 POST /__bolt/sync/connect 与 /__bolt/sync/extend ;宿主把流保持打开在 /__bolt/sync/stream.

命令用途
sync.connect浏览器(以及重连 / 重置)。解析每个请求的前缀,返回行以及宿主将登记的计划。
sync.extendPrefix浏览器,单调增长。在不提升版本的情况下,把行追加到查看者已加载的前缀之后。
sync.advance宿主在提交之后调用。按该提交的 SyncChange 列表重新求值已登记的前缀,返回更新或重置。浏览器从不调用它。
stream (SSE)apply 帧:updates、resets 与写入 outcomes 在一个载荷里——一次提交,一次 reducer 事件。

未能接续每个已保留 fromVersion 的帧是协议错误,连接会重启。重置始终合法。完整协议记录在 公开的 Bolt 同步引擎文档 中。

共享宿主核心:注册表与泵

订阅状态活在宿主注册表里。SyncRegistry 与 SyncConnectionLane 是 bolt-protocol 中唯一可执行的共享核心,由云上的 Colony 与自托管的 bolt-server 直接使用。宿主只提供连接、作用域、guest 调用与断开映射适配器,不会各自重写登记、失效、排序或发送:

  • 注册表 按 planKey 登记不透明的 guest 事实——版本、前缀键、保留字节、权限指纹、依赖与路由约束。多条连接可以作为查看者挂上,各自有不同的 loadedPrefix 长度。宿主从不解析主体、求值谓词或构造增量。
  • 泵 在每个 (tenant, environment, releaseId) 的一条通道上串行化 connect、extendPrefix 与 committed。宿主等待通道接受;无法写入的接收端或 guest 失败会关闭不确定的连接。没有 changelog 可用来重放错过的唤醒。
  • 实时数据 ——针对此引擎编写的实时查询与集合写入
  • Bolt 框架 ——同步引擎在平台中的位置
  • 策略 ——界定每份答案可以包含什么