跳到主要内容

实时数据

实时数据

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

读取是实时查询

每次读取都是一个 实时查询 ,作为本地 SQL 针对数据的一份按策略过滤的副本执行。使用 client.db.<collection>.findManyfindFirstcount ——只由过滤、排序与限制构成的读取会经服务端所用的同一个编译器编译成本地 SQL,因此它即时完成且永不离开设备。关联展开、全文搜索、聚合与历史则交由服务器回答:一个仅仅接近的本地答案,比一个正确的远端答案更糟。当一条变更落地(无论是你的还是别人的),引擎会重新评估每个依赖该变更集合的实时查询,并把结果 diff 进响应式值。

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

const orders = client.db.orders.findMany({
  where: { status: { eq: 'open' } },
  with: { customer: true },
  orderBy: { created_at: 'desc' }
});
// Live — another tab or automation closes an order and this result updates.

在这一切之前,副本只构建一次。它先依据租户自己的迁移完成置备,然后对该用户可读的每个集合做一次分页 快照 ,再从该快照交回的游标开始订阅流。仅靠变更日志无法做到这一点:只有经过集合运行时的写入才会进入 outbox,因此种子数据与导入的行完全不在其中。

写入是乐观更新

浏览器写入使用 client.db.<collection>.mutate(values) 。它返回 Promise<void>;成功完成会使受影响的实时查询失效。 client.db.<collection>.pending 是正在进行的数值计数,因此并发写入可以独立结束。

  client.db.cost_estimates.mutate(values)
      values = precisely typed root + any explicitly included relationship state
              │  pending += 1
              ▼
  server: policy → approvals → before hooks → reconcile the
          root + every included relationship → after hooks → audit
              │                         (one transaction)
   ┌──────────┴───────────┐
   ▼                      ▼
  committed               refused
  affected queries        reason surfaces;
  invalidate              nothing was written
   │                      │
   └──────────┬──────────┘
              ▼  Promise<void> resolves; pending -= 1
  affected live queries re-run and the outbox carries the
  committed change to every other replica

已包含的关联表示其完整期望状态。其中的行会被插入或更新;以前已存储但未出现在其中的行会被删除。显式包含的关联会递归同步,而未包含的关联保持不变。根记录与所有已包含关联会原子协调。

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

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

await client.db.cost_estimates.mutate(values);
// The included relationship is its complete desired state. Present rows are
// inserted or updated; stored rows omitted from it are deleted. Relationships
// omitted from the mutation are untouched. Explicit nesting reconciles
// recursively, and the whole submitted graph commits atomically.

client.db.cost_estimates.pending; // numeric in-flight count

mutate 从不返回记录:只写与行过滤策略可能允许写入,却不允许相应读取。实时查询是调用方可见数据的唯一来源。这个浏览器变更界面不定义顶层记录删除编码。

不变量

读取路径永远不会等待此设备已经见过的数据。 每个服务器应答都会折入本地副本——对任何内容的第二次访问都是即时的,应用代码中没有任何 invalidaterefetchrevalidate

  • 集合 ——定义实时查询读取的模型
  • 应用 ——在实时读取之上组合运营 UI
  • 同步引擎 ——副本、变更流与传输如何工作
  • 策略 ——界定什么能到达本地副本