kisenon

分支

Kisenon 中的写时复制 Postgres 分支 —— 在 LSN 处分叉、生命周期与级联。

一个分支是指向项目存储中某个日志序列号(LSN)的指针。创建一个 分支是一次 O(1) 的行插入;它不会复制数据。分支随着您向其写入而发散, 并且只存储增量。

模型

每个项目从创建起就有一个 main 分支。从 main(或任何现有分支) 您可以分叉出一个新分支:

  • 在 HEAD 处 —— 新分支从父分支的当前 LSN 开始。
  • 在特定 LSN 处 —— 新分支从父分支 WAL 保留窗口内的某个历史 LSN 开始。这是"时间旅行"的场景:只要 WAL 仍被保留,您就可以从 过去的某个点分叉。

分支上的读取会回落到父分支的页面,直到分支发散。写入只记录增量。结果:

  • 一个带有十个小分支的 100 GB 项目按总共约 100 GB 计费。
  • 派生一个分支是亚秒级的。
  • 分支是可丢弃的。运行一次破坏性迁移,丢掉该分支,父分支不受影响。

命名

分支名是用户可见的标签,作用域限定到父项目。约束与项目名一致:

  • 1–63 个字符。
  • ^[a-zA-Z][a-zA-Z0-9_-]*$
  • 在项目内唯一。

内部分支 id(br_<24 hex>)是稳定的引用。在针对 API 编写脚本时, 请使用 id 而非名称;名称可以被重命名。

状态机

一个分支会经历如下状态转换:

creating → ready → deleting → deleted
  • creating —— 控制平面正在向存储层注册该分支。通常是亚秒级; 如果存储处于负载下则更久。
  • ready —— 分支可以挂接到端点并接受写入。
  • deleting —— 级联进行中:该分支上的每个端点都在被停止和移除。
  • deleted —— 终态。该行会为了可审计性保留一小段时间, 然后被垃圾回收。

CLI 会显示每个分支的当前状态:

keon branches list --project <project-id>

创建

keon branches create --project <project-id> --name my-feature --parent main

要在某个历史 LSN 处分支,传入 --parent-lsn <lsn>

keon branches create \
  --project <project-id> \
  --name pre-migration \
  --parent main \
  --parent-lsn 0/1A2B3C4D

该 LSN 必须在项目的 WAL 保留窗口内。如果它已被压实越过, 分支创建会失败。

删除

删除一个分支会级联到挂接在其上的每个端点:

  1. 该分支上的所有端点都被停止和移除。
  2. 分支本身被拆除。
  3. 存储由 pageserver 压实异步回收。
keon branches delete <branch-id>

您无法删除一个项目的 main 分支 —— 请改为删除项目。您可以删除任何 其他分支,即使它有子分支;在那种情况下,子分支会在删除 LSN 处 被重新挂接到被删除分支的父分支上。

重置与恢复

在控制台的分支详情页中, 操作菜单在任何非根分支上暴露两个 重新分叉操作:

  • 重置到父分支 —— 在父分支的当前 HEAD 处重新分叉该分支。 自原始分叉以来在该分支上所做的每一次写入都会被丢弃。该分支上的 端点在切换期间短暂挂起。
  • 恢复到时间点 —— 在选定的 LSN 或一个 UTC 时间戳(RFC 3339)处 重新分叉该分支。该点之后的写入会被丢弃。

两者都是异步运行的:控制台发出请求,重新分叉落地后分支返回到 ready。根(main)分支无法被重置 —— 没有可重置到的父分支。

快照

一个快照会固定一个时间点 LSN,使其在 WAL 保留视界之外仍可恢复。 没有快照时,一个恢复目标只在其 WAL 仍被保留期间可达;快照让那个点 无限期地保持可寻址。

分支详情页上的 Snapshots 标签页列出该分支的快照,并允许您捕获 一个新的。每一行提供:

  • 恢复 —— 在快照固定的 LSN 处重新分叉该分支。
  • 删除 —— 丢弃该快照(底层 LSN 不再被固定,可能被压实回收)。

相关

  • 项目 —— 拥有该分支的东西。
  • 端点 —— 挂接到分支以接受客户端流量的东西。
  • 概念 —— 让分支免费的存储/计算拆分。