临时存储(Ephemeral Store)
在实时协作场景中,除了通过 CRDT 保持文档一致性,用户状态(Presence)同样重要,例如协作者姓名、指针位置或当前选中的对象。我们需要一种不会写入 CRDT 文档、但能瞬时共享的机制,让协作者彼此感知,从而协调操作、避免冲突。这正是 Ephemeral Store 的用途。

由于临时信息主要用于实时同步,我们采用了简单且高效的方案:Ephemeral Store 是基于时间戳的最终写入生效(Last-Write-Wins)键值存储。每个条目维护自身的更新时间戳,因此只需发送更新的条目,而无需广播整个状态。
示例
import { , EphemeralStoreEvent } from "loro-crdt";
import { } from "expect";
const = new ();
// 写入临时数据
.("loro-prosemirror", {
: { : 0 },
: { : 5 },
: "Alice",
});
.("online-users", ["Alice", "Bob"]);
(.("online-users")).(["Alice", "Bob"]);
// 只编码 `loro-prosemirror` 对应的数据
const = .("loro-prosemirror");
.((: EphemeralStoreEvent) => {
// 监听来自 local、import 或 timeout 的事件
});API
-
constructor(timeout): 创建 EphemeralStore 实例,可选timeout(默认 30000 毫秒)。超时后条目会自动移除。 -
set(key, value): 为指定键写入值,若键已存在则覆盖。 -
get(key): 获取指定键的当前值,不存在则返回undefined。 -
delete(key): 删除指定键及其值。 -
getAllStates(): 返回当前所有键值对。 -
keys(): 返回当前所有键的数组。 -
encode(key): 将指定键的值编码成可传输的二进制数据。 -
encodeAll(): 将全部键值对编码成二进制数据。 -
apply(bytes): 将其他节点发送的编码数据应用到本地临时存储。 -
subscribe((event: EphemeralStoreEvent) => void): 注册监听器,监听本地更新、远端更新或超时事件。interface EphemeralStoreEvent { // 事件来源:本地、远端导入或超时删除 by: "local" | "import" | "timeout"; // 新增的键 added: string[]; // 更新的键 updated: string[]; // 被移除的键 removed: string[]; } -
subscribeLocalUpdates((bytes: Uint8Array) => void): 仅监听本地更新,便于直接将增量转发给其他节点。// 需要保留订阅引用,避免被垃圾回收 const _sub1 = ephemeral1.subscribeLocalUpdates((update) => { ephemeral2.apply(update); }); const _sub2 = ephemeral2.subscribeLocalUpdates((update) => { ephemeral1.apply(update); });