从零搭建无服务器 Beads 数据联邦:基于 GCS / S3 桶的跨机器同步实战
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
导读
本文基于 Beads 官方文档 bucket-federation.md,讲解如何用对象存储桶(GCS / S3)在两台机器之间搭建一个无服务器、无托管账号、无需开放端口的 Beads 数据库联邦:把桶当作 Dolt 的 remote,让两台机器的本地副本通过bd sync双向同步。读完本文,你将掌握从建桶、注册 remote、种子推送、克隆出生第二副本到设定同步节奏与处理五种典型故障的完整实操路径,并理解bd sync的退出码契约、冲突语义与源码级实现原理。
联邦是什么:桶即 remote
Beads 的联邦能力建立在 Dolt 之上——Dolt 原生支持 GCS 与 S3,因此一个对象存储桶可以直接充当 remote 的角色,与 git remote 之于仓库完全同构。两台机器指向同一个桶,就构成一个联邦:
machine one gs://my-bucket/beads/myproject machine two ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ .beads/dolt │ ──── bd sync ────► │ bucket │ ◄──── bd sync ────── │ .beads/dolt │ │ (replica) │ ◄──────────────────│ (no server) │ ─────────────────────►│ (replica) │ └─────────────┘ └──────────────┘ └─────────────┘这种模式是"自带云(BYO-cloud)"路线:当数据必须留在你自己的云账号里、或者你手上已经有一个桶时,选择它;DoltHub 仍是零配置的默认选项。
每台机器都持有完整的本地副本,可以离线工作;定时器在两端各自运行bd sync。两者之间除了桶之外没有任何仲裁者——冲突的协调完全依赖 Dolt 的合并语义。
前置条件
开始前请确认以下三点齐备:
- Dolt 后端——这是唯一受支持的存储后端,联邦能力依赖 Dolt 的 remote / push / pull 机制。
- 一个可写的桶,且两台机器都有凭据:
- GCS:Application Default Credentials,即运行
gcloud auth application-default login,或通过GOOGLE_APPLICATION_CREDENTIALS指向服务账号密钥文件; - S3:标准 AWS 凭据链——
AWS_PROFILE、AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY,或实例角色(instance role)。
- GCS:Application Default Credentials,即运行
- 每台机器上都有 Dolt 提交身份(
user.name/user.email)——见下文 故障模式,新机器通常没有,第一次 merge 时才会暴露出来。
第一步:创建桶路径
每个 Beads 数据库对应一个桶路径。两个数据库共享同一路径,会在同一个 Dolt 历史上互相打架,造成无法合并的分叉。
# GCS gcloud storage buckets create gs://my-bucket --location=us-central1 # S3 aws s3 mb s3://my-bucket桶内不需要预先准备任何目录布局——第一次 push 会创建一切。
第二步:机器一——注册 remote 并种子推送
在已经持有待共享数据库的工作区里执行:
bd dolt remote add origin gs://my-bucket/beads/myproject bd dolt push支持的 scheme 包括gs://、s3://(或 Dolt 的aws://)、az://、dolthub://、https://、file://以及 git SSH。
两个值得注意的细节:
- 必须用
bd dolt remote add,而不是裸的dolt remote add。bd 通过 store API 注册 remote,因此运行中的 Dolt SQL server 能立即看到它。用doltCLI 添加的 remote 只落在文件系统配置里,push/pull 会以remote not found失败,直到 server 重启。源码中对此有明确注释:dolt.go 说明"remote 通过dolt remote add(文件系统配置)添加、但未通过bd dolt remote add(还会在 SQL server 的dolt_remotes表中注册)时"会触发该错误。 - 命名为
origin还会把sync.remote持久化进.beads/config.yaml——这正是bd sync无需任何标志就能找到 remote 的原因。其他名字也可以,但每次同步都必须写bd sync --remote <name>。实现上,bd dolt remote add会调用config.SetYamlConfigInDir(beadsDir, "sync.remote", remoteURL)并把.beads/config.yaml的改动以你的 git 身份提交(dolt.go、sync_remote.go)。
验证:
bd dolt remote list第三步:机器二——从桶"出生"副本
bd init --remote gs://my-bucket/beads/myprojectbd init --remote从桶克隆 Dolt 数据库,并持久化sync.remote,因此机器二立即可同步。这是克隆而非导入:没有 JSONL 往返、没有重新生成主键,完整提交历史一并带来。
出生后先验证再信任:
bd dolt remote list # 指向桶 bd list --status all --json | jq length # 与机器一的 issue 数量对齐如果机器一在最后一次 push之后关闭了一个 issue,在这里它看起来仍是打开的——这是联邦滞后(federation lag),不是坏克隆,会在下一次同步时到达。
注意:如果机器二上已有一个想用桶副本替换的
.beads/目录,不要直接克隆覆盖。先把它挪到一边,再运行bd init --remote;参见 Init Safety 的防护机制。
第四步:选择同步节奏
bd sync就是整个循环——pull、正向检查冲突、修复is_blocked、带限次重试的 push:
bd sync # 默认 remote bd sync --remote mini # 指定命名的 remote bd sync --json # 机器可解析的输出在两端各用定时器运行。按下面测量到的规模,60 秒节奏很从容;无变化时整个循环是空操作。
# cron,每分钟 * * * * * cd /path/to/workspace && /usr/local/bin/bd sync --json >> /tmp/bd-sync.log 2>&1定时器应基于退出码分支,而不是解析输出:
| 退出码 | 含义 | 定时器应做的 |
|---|---|---|
| 0 | 已同步,或无事可做 | 无 |
| 1 | 错误(传输、认证、存储) | 若重复出现则告警 |
| 2 | 合并冲突——已停止,未推送 | 告警人类;绝不自动解决 |
| 3 | 重试耗尽(push 竞争,或其他写者的脏工作集) | 无;下一个 tick 会重试 |
| 4 | 脏工作集是卡死而非繁忙 | 告警人类;后续 tick 都不会发布 |
这些退出码在源码中是显式契约:sync.go 定义了ExitSyncConflict = 2、ExitSyncRetriesExhausted = 3、ExitSyncDirtyStuck = 4,注释明确说明这套码是"为了让同步定时器无需解析输出即可分支"而设计的。从源码还可以看到更多实现细节:
- 默认重试次数为 3(sync.go 的
defaultSyncAttempts = 3),对应生产部署的默认值:push 竞争实际上在第一次重试就解决,而无界循环在繁忙集群下永不收敛。 --json输出的status字段取值包括ok/conflict/retries-exhausted/dirty-stuck/disabled/no-remote(sync.go),并附带attempts、conflicts、rows_corrected、pushed、transients等字段,供监控系统消费。- 同步循环在每次 pull 之后都做正向冲突检查(查
dolt_conflicts),而不是信任 pull 的退出码——见 sync.go 的runSyncLoop;这正是文档"不要相信 pull 退出码"警告的实现来源。
选择间隔的三条规则
- 滞后量就等于间隔。每个副本对其它副本的视图,构造上就比实际最多老一个完整间隔。
- 租约 TTL 与回收宽限期(reclaim grace)都必须大于间隔。租约只在授予它的那个副本上有意义,另一台机器上的 reaper 若依据比租约还旧的数据判断活性,就会误判。参见 Federation Setup——租约是逐副本的,并用
bd config set node_id <name>命名每个副本,使跨副本回收防护生效。源码中node_id被明确标记为"租约防护的副本身份(从 yaml/env 读取,绝不来自数据库)",见 config.go。 - 更长的间隔意味着更多冲突,而不只是更旧的数据。
updated_at会被每一次bd 变更触碰,因此两台副本在两次同步之间编辑同一个 issue 时,即使改的字段互不相交也会冲突。对不同issue 的不相交编辑,在任何节奏下都能干净合并。
实测成本
以下数据来自一个生产双机部署(笔记本 + Mac Mini,约 1.3k issues、约 115k chunks、Dolt 2.1.10、GCS remote):
| 操作 | 耗时 |
|---|---|
| 全量冷推送(种子填充空桶) | 28s |
| 全量克隆(出生副本) | 5s |
| 增量推送 | ~4s |
| Pull + 合并 | ~1s |
因此 60 秒节奏下每个 tick 只花几秒钟,稳态由空操作主导。注意:这些是文档记录的特定部署实测值,不同网络、桶规格与数据规模下的数字会变化,不应外推为普遍性能承诺。
故障模式
bd dolt push报 remote 不存在
remote 是用裸dolt remote add添加的,因此它存在于文件系统配置中,却不在 SQL server 的dolt_remotes表里。重新注册:
bd dolt remote add origin gs://my-bucket/beads/myproject新机器上 pull 失败且无冲突报告
没有 Dolt 提交身份的机器无法创作 pull 所需的合并提交,因此即使是空操作的 pull 也会失败。这是新装机上最常见的首次同步失败:
dolt config --global --add user.name "Your Name" dolt config --global --add user.email "you@example.com"Shell 里认证正常,但 server 模式下同步失败
使用外部 Dolt SQL server 时,CALL DOLT_PUSH/PULL在server 进程内部执行,而它只有启动时继承的环境。之后导出的凭据永远到不了它那里。bd 会检测与 remote scheme 匹配的云凭据(gs://对应GOOGLE_*/GCS_*,s3:///aws://对应AWS_*,az://对应AZURE_STORAGE_*),并把 push/pull 路由到继承当前环境的doltCLI 子进程。若同步仍无法认证,请带着凭据重启 server。
remote 名字不是origin
用bd init --remote(或dolt clone)出生的副本把 remote 命名为origin;手工添加 remote 的机器可能命名为任何东西。定时器必须在每台机器上传正确的--remote——或者重命名 remote,使两端一致。
sync 以 2 退出(冲突)
bd sync在重算或推送之前停止,绝不自动解决它无法安全定局的东西。重复运行会以同样的方式持续停止,直到操作者解决分歧。在.beads/dolt/<db>内:
dolt sql -q 'select * from dolt_conflicts' # 正向检查——不要相信 pull 退出码 dolt conflicts cat issues # base / ours / theirs 行 dolt conflicts resolve --theirs issues # 或 --ours dolt add -A && dolt commit -m 'resolve conflict' && dolt push origin main然后让定时器恢复。从源码看,退出 2 的语义是"合并冲突;同步已停止,什么都没推送,不自动解决"(sync.go),且 pre-flight 阶段就会检查上次停止的同步遗留的活冲突行,避免其被误报为晦涩的 pull 失败(sync.go)。
一个桶路径,两个数据库
一个桶路径就是一个 Dolt 历史。把第二个、不相关的 Beads 数据库指向同一路径,会产生任何合并都无法调和的分叉。给每个数据库独立的路径。
与bd federation的关系
两条路径都写 Dolt remote,区别在于用途:
bd sync(本文) | bd federation sync | |
|---|---|---|
| 目标 | 工作区配置的 remote | 命名对等城镇(peer towns) |
| 冲突 | 停止(退出 2);无覆盖开关 | 提供--strategy ours\|theirs |
| 用途 | 一个数据库跨机器的副本 | 跨独立团队/组织的共享 |
把桶注册为命名对等方只需一条命令,且对等方表面额外提供主权层级与拓扑:
bd federation add-peer backup gs://my-bucket/beads-backup关于对等方、主权层级与拓扑,参见 Federation Setup。
延伸阅读
- Federation Setup — 对等方、主权、拓扑
- Dolt Architecture — remote、push/pull、存储布局
bd dolt·bd init·bd sync --help(完整同步面)- Sync Failures — 恢复卡死的同步
- 源码线索:sync.go(退出码与状态契约)、dolt.go(
sync.remote持久化)、sync_remote.go(remote 解析顺序:sync.remote→ 废弃的sync.git-remote)、config.go(node_id副本身份)
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考