跳到主要内容

实时数据

实时数据

这就是租户应用接触数据的方式。读取是 实时查询 ,写入是 乐观更新 ,两者都通过 $bolt/client ——你永远不需要打开连接、管理缓存或使任何东西失效。Bolt 的 同步引擎 让每个客户端保持最新;本页是该约定面向编写者的一半。

读取是实时查询

每次读取都是一个 实时查询 ,在服务器注册,并按操作主体的策略由服务器求值。使用 client.db.<collection>.findMany 、 findFirst 或 count 。带连续 limit 的 findMany 与 findFirst 是在 sync.connect 上登记的实时前缀。count、findGrouped、带 after 游标的分页以及语义搜索都是一次性读取,不会登记为实时。浏览器只在内存中保留当前答案。提交改变某个实时前缀的依赖项时,宿主推进该前缀,流上发送带版本栅栏的按键增量——或一次迫使浏览器重新注册的重置。

import { client } from '$bolt/client';

const orders = client.db.orders.findMany({
  where: { status: { eq: 'open' } },
  with: { customer: true },
  orderBy: { created_at: 'desc' }
});
// Mounted once, registered with the server, then updated by SSE apply frames.

首次挂载时,客户端发送一次 sync.connect 握手 ,登记每个实时前缀,并返回当前行以及宿主将要归档的计划。重连会重新登记这些前缀;错过一次唤醒会关闭流。没有 changelog 可供重放,也没有已持有 id 的摘要。

写入是乐观更新

浏览器写入使用 client.collection.<collection>.create(input) 。它返回持久性为 memory 的乐观结果与结算句柄。Machine 通过 HTTP 推出幂等图,权威端则经同步通道报告结果。 client.collection.<collection>.pending 计数入队操作仍在进行的写入;权威状态属于每个返回的结算句柄。

  client.collection.cost_estimates.update(id, input)
      input = the declared columns + explicit relation actions
              │  enqueue in this tab's memory
              ▼
  optimistic row projects over held in-memory query answers
              │  collections.write push with idempotency key
              ▼
  server: refuse anything outside the selection → policy → transform →
          approval route → commit the root + every nested action
              │             → history + SyncChange capture (one transaction)
              ▼
  host advances registered live prefixes for changed collections
              │  one ordered SSE apply frame
              ▼
  keyed deltas or resets + authoritative write settlement
  (accepted | rebased | rejected | quarantined)
	>

输入就是集合声明的选择:它的列,以及每个关联之下它接受的显式操作——`create`、`update`、`upsert`、`link`、`unlink`、`delete`。省略的关联或空数组什么都不做;任何东西都不会因省略而被删除。create 不携带 id——由服务器分配。根记录与每个嵌套操作原子提交。

生成的类型会精确描述该嵌套图,无需类型断言或兼容包装。查询拥有 current、loading 和 error;变更拥有 pending。组件不复制查询数据、刷新、加载、错误或变更状态。

import { client } from '$bolt/client';

const write = await client.collection.cost_estimates.update(id, {
  status: 'submitted',
  line_items: {
    create: [{ description: 'Scaffolding', amount: 1200 }],
    update: [{ id: lineId, set: { amount: 950 } }],
    delete: [{ id: staleId }]
  }
});
// Relation actions are explicit. An omitted relation or an empty array does
// nothing; nothing is ever deleted by omission. The root and every nested
// action commit atomically.

write.row; // optimistic projection; durability is this tab's memory
await write.settlement.settled; // authoritative outcome arrives through sync

写入返回乐观行,而非持久的服务器记录。只写与行过滤策略可能允许写入,却不允许相应读取,因此已注册实时查询仍是已授权当前数据的来源。结算句柄会报告 accepted、rebased、rejected 或 quarantined;被路由到审批的写入会在挂起标记下临时提交,并以待审批结算。

不变量

以服务器为权威的答案是唯一已提交事实。 浏览器只在内存中保留已挂载答案,并且只应用带版本栅栏的按键增量;应用代码中没有任何 invalidate 、 refetch 或 revalidate 。

  • 集合 ——定义实时查询读取的模型
  • 应用 ——在实时读取之上组合运营 UI
  • 同步引擎 ——已注册查询、SSE apply 帧与变更推送如何工作
  • 策略 ——界定每份服务器权威答案的范围