文档教程临时存储

临时存储(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);
    });