ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

gbrain 中 `gbrain serve` 与 `gbrain sync` 的并发协作:PGLite 单写者模型下的同步委派机制

gbrain 中 `gbrain serve` 与 `gbrain sync` 的并发协作:PGLite 单写者模型下的同步委派机制 gbrain 中gbrain serve与gbrain sync的并发协作PGLite 单写者模型下的同步委派机制【免费下载链接】gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain导读在 gbrain 的 PGLite 数据脑embedded Postgres/WASM 单写者架构上gbrain serve常驻持有数据库连接此时再运行gbrain sync会因单写者锁而无法打开数据库。本文基于 docs/architecture/serve-sync-concurrency.md完整解析 gbrain 如何通过把同步委派给 serve 进程来化解这一冲突包括委派决策梯、经过鉴权的持久化 IPC 协议、分片式游标推进、失败恢复与挂起诊断。读完你将掌握 serve 在线期间正确执行同步的全部规则、限制与排障手段。核心结论serve 在线时sync 委派给 serve 执行一句话版本在 PGLite 数据脑上即使gbrain serve正在运行gbrain sync也可以照常执行——同步工作被委派给 serve 进程因为 serve 本来就持有唯一的写连接。PGLite 是单写者的嵌入式 Postgres以 WASM 运行。常驻持有者resident owner在连接关闭前一直持有数据存储外部稳定的原生锁。存活的持有者永远不会被驱逐live holder is never displaced失败的 IPC 也永远不会授权第二次打开。这意味着任何试图绕开持有者直接打开 PGLite 数据存储的做法在 gbrain 中都是不被允许的。从源码看这一约束在 CLI 连接层就得到了贯彻gbrain sync在连接引擎之前会先探测存活持有者若发现持有者是存活的gbrain serve则走委派路径而不是直接报错参见 src/commands/sync-delegate.ts 中maybeDelegateSyncToServe的注释LiveServeLockError 自 #2348 起确立存活持有者不可被取代。该模块明确自述PGLite brain 上存活的gbrain serve在其生命周期内持有单写者锁因此gbrain sync无法打开数据脑——与其失败不如把同步在 serve 内部通过 resolve-IPC 套接字执行CLI 侧只负责轮询进度并打印结果。委派如何工作四步机制CLI 在打开数据存储之前先解析所选数据脑并通过经过鉴权的持久化 IPCauthenticated persistence IPC将工作委派给被观察到的常驻持有者。整体分四步CLI 使用其持久化的本地 CLI 注册凭据发起委派。HTTP 与 stdio 常驻进程都暴露该监听器但 hook secret 或 stdio 注册不能授予 CLI 权限。选中的 PGLite 挂载mount使用各自的数据存储与注册。也就是说委派权限只属于可信的本地管理通道CLI 通道stdio 被视为不可信的内存调用方无法借道获取 CLI 权威见 docs/guides/concurrent-writes.md 的Local registrations and canonical ownership一节。在受管激活managed activation之前持有者在其既有连接上运行仅导入的performSync激活之后它通过持久化日志推进有界的受管同步分片bounded managed-sync slices。客户端在结果为writer_yield或writer_pending时反复重试这些分片。受管同步保留其不可变immutable的发现清单与游标。若客户端退出或丢失确认已接受的页面请求仍可完成用相同选项重跑即可从剩余游标处继续。嵌入embeddings被延后执行因为委派绕过了直接 CLI 的内联成本门槛inline cost gate。持有者使用其配置的 provider 与密钥来排空嵌入--no-embed会抑制该调度。从源码看委派侧的选项传递经过了严格的默认拒绝default-deny白名单sync-delegate.ts定义了可过线转发的布尔旗标表WIRE_BOOL_FLAGS--full、--dry-run、--no-pull、--no-embed、--no-extract、--no-schema-pack、--skip-failed、--retry-failed、--include-gitignored、CLI 自行消费的取值旗标VALUE_FLAGS--source、--timeout、--hard-deadline以及仅在委派外有意义的旗标IGNORED_FLAGS--yes、--no-delegate、--no-hard-deadline。任何不在表内的 argv 令牌都会触发明确点名该旗标的拒绝——正如源码注释所说一个被静默丢弃的--exclude会执行错误的同步拒绝是唯一安全默认。决策梯decision laddermaybeDelegateSyncToServe的决策过程在源码中清晰可读共六级0. 显式退出出现--no-delegate或环境变量GBRAIN_SYNC_NO_DELEGATE1→ 不委派走常规连接路径。1. 非宿主数据脑挂载不委派套接字、secret 与锁都属于宿主数据目录挂载脑的同步必须走常规路径。2. 无存活持有者或持有者是非 serve 进程→ 不委派维持旧行为死 PID 回收 / 有界等待。3. 存活 serve 白名单之外的 argv 令牌→ 默认拒绝并点名该旗标退出判卷exit verdict为 1。4. 存活 serve 套接字应答→ 委派打印横幅、以 1 秒周期轮询sync_status、首次 Ctrl-C 触发sync_abort第二次 Ctrl-C 直接硬退出、打印同步结果。5. 存活 serve 但无套接字 / 陈旧 serve / 未授权→ 给出带修复建议的礼貌拒绝退出判卷 1绝不抛原始堆栈。每种拒绝都给出三条出路去掉触发拒绝的旗标如果是旗标导致的、停止 serve、或传--no-delegate。轮询侧也有容错设计MAX_POLL_FAILURES 60次连续失败才判定套接字死亡且每 5 次失败会用只读 PID 探测区分serve 忙长同步 WASM 语句会阻塞事件循环瞬时超时是正常的与serve 已消失。委派超时与客户端死亡防护客户端总是发送其解析出的硬性截止时间interactive 默认 3600s非 TTY 默认同样映射为 3600sserve 侧每个调用都有界。--no-hard-deadline请求无同步截止时间。关键在于即使客户端进程死亡任务也始终有界——这是唯一一种无界编码0 秒之外的保障见deriveDelegatedTimeoutSeconds的实现。serve 侧还对超时做了硬上限钳制DELEGATED_SYNC_TIMEOUT_MAX_SECONDS 86_40024 小时与同步硬截止时间的量级一致src/core/context/sync-ipc.ts。IPC 线协议窄类型、secret 门控、fail-closed委派 IPC 是三种窄请求类型wire shapes 定义于 src/core/context/sync-ipc.tssync_start { options, clientToken }→{ ok, jobId }或{ ok:false, error }sync_status { jobId }→ 任务状态 进度 最终结果sync_abort { jobId }→ 开始协作式中止typed partial该模块刻意保持叶节点设计纯类型 纯函数不导入 resolve-ipc 或任何引擎模块避免循环依赖。信任姿态与 turn_context 一致——窄类型请求、secret 门控、原始 SQL 绝不越过线缆。服务端验证器validateDelegatedSyncOptions对未知键一律拒绝这是防止服务端专用 SyncOpts 字段从套接字触达的关键、类型不符拒绝、sourceId必须符合规范形状、timeoutSeconds必须是非负整数。noEmbed有一个微妙语义它不会到达performSync——委派的同步任务永远以 noEmbed 运行因为 #2139 成本门槛位于runSync委派路径从不经过它该旗标只是记录用户拒绝了嵌入从而抑制 serve 的延后嵌入排空缺省时由 serve 的后续 sweep 排空。clientToken是客户端生成的一次性幂等令牌丢失确认后的重试若命中busy且令牌匹配则是在附着attach到自己的任务若令牌匹配到保留的终态任务则返回{ok, jobId, completed:true}而不是重复执行。委派与 MCP 流量的共存共享数据存储与公平调度MCP 流量与委派同步共享持有者的数据存储。受管同步在有界批次之间让步yield但导入工作仍可能影响读延迟。这背后的调度策略在 src/core/persistence/sync-run.ts 的performManagedSync中有具体实现公平性双向保证前台请求优先获得服务sync 在每 25 个前台提交或连续 1 秒前台服务后即使新交互请求持续到达也能赚到一个有界批次creditedPages 25信用在 250ms 后清零。一次只准入一页one page admitted ahead of the scan扫描在每最多 25 页或 250ms 之间让出。批次让步每个 slice 完成后返回partialwriter_yield客户端据此重复下一 slice若某个写入请求在 5 秒等待后仍未达终态则返回partialwriter_pending。这两者正是委派客户端持续循环的条件。游标持久化游标头header与不可变清单manifest分离存储——推进一页时只重写游标头绝不重写整个发现文件列表。清单存于私有managed-sync操作的op_checkpointssingleton JSON-array envelope清单本身以managed-sync-manifest独立存储。saveCursor通过乐观并发completed_keys比较在事务内更新防止多个持有者循环互相覆盖。受管同步的调度规则、支持的导入类型与检查点规则详见 canonical writer enforcement注册、回执与恢复详见 concurrent writes。值得强调的是数据存储所有权与gbrain-sync:*源租约是两回事——原生锁阻止第二个 PGLite 持有者出现源租约则协调同步工作本身租约过期或 PID 元数据都不授权文件系统所有权的接管。限制一览表场景行为不支持的旗标--all、--watch、--workers、--break-lock以及任何未分类项点名该旗标直接拒绝。受支持的选项包括--repo、--source、--exclude、--src-subpath、--include-hidden、--json接受某个旗标并不等于绕过受管模式限制。serve --http或 stdio MCP两者都暴露经过鉴权的持久化 IPC供本地 CLI 注册使用。陈旧或不可用的常驻 IPC拒绝连接而不是另开一个引擎升级/重启常驻进程或恢复后用相同选项重试。挂载数据脑mounted brains选中的 PGLite 挂载委派给它们自己的持有者。Postgres 不需要 PGLite IPC但规范 worktree 所有权仍然适用。客户端退出选择--no-delegate或GBRAIN_SYNC_NO_DELEGATE1禁用委派但它并不允许打开已被持有的 PGLite 数据存储。截止时间客户端发送其解析出的硬截止时间interactive 默认 3600sserve 每个调用都有界。--no-hard-deadline请求无同步截止时间。受管导入使用--no-pull。Git pull/rebase、代码/图片导入器与忽略文件遍历仍被拒绝参见 canonical writer 指南。serve 在同步中途关闭持有者中止当前分片并等待其工作完成后才断开。已接受的页面请求与受管游标保持其持久状态。需要特别注意的是表格最后一行与源码的一致性serve 的关闭不是丢弃同步——中止的分片是协作式的游标落盘下次用相同选项重跑即从断点继续。旧版兼容协议较旧的共享 secret 协议sync_start/sync_status/sync_abort仍作为未激活数据脑的兼容路径保留。它的旗标集更窄且拒绝受管数据脑。GBRAIN_SERVE_SYNC_IPC0可禁用该旧协议但它不能替代撤销持久化 CLI 注册。换句话说禁用旧协议只是关掉一扇门撤销注册才是撤销权威本身。sync-delegate.ts的messages表对unsupported_kind的解释也印证了这一点serve 已禁用同步委派GBRAIN_SERVE_SYNC_IPC0或启动失败。如果 serve 在同步中途死亡当进程死亡时内核会释放其原生锁。后继者必须先获取该锁并协调持久化请求与恢复状态然后才能发布publish。遗留的.gbrain-lock元数据仅为兼容性与诊断保留删除它不能授权接管——这与sync-delegate.ts的 crash 提示一致serve 死亡后其同步锁行可能需要最多 60 秒才能变为可回收若重跑报告死 PID 锁可用gbrain sync --break-lock清除。但请记住--break-lock在委派模式下是被拒绝的旗标只有 serve 死亡、锁真正归属死 PID 时才适用。恢复的实操路径重复相同的gbrain sync选项以恢复受管游标。不可变清单 持久游标保证断点续传。意外的文件字节会使受影响的根root保持阻塞以待修复而无关的根可以继续推进。在尝试任何管理性修复之前先检查持有者与恢复状态gbrain sources writer status --probe --json诊断同步挂起手动排障按文件追踪若同步卡死无进展、高 CPU带上 per-file begin 追踪重跑卡住的文件名就会被点名GBRAIN_SYNC_TRACE1 gbrain sync --no-pull --no-embed --yes最后一条[sync] begin import: path且没有对应完成记录的就是挂起发生时正在处理的文件。在--workers 1/--all下卡住的文件位于有 begin 行但没有匹配完成行的集合中。源码层面GBRAIN_SYNC_TRACE在 src/commands/sync.ts 中于每文件导入前输出[sync] begin import: ${path}注释明确说明这是为挂起排障设计的大脑库巨大时可用环境变量按需开启避免每文件一行刷屏。schema-pack 正则导致的卡死如果怀疑是 schema-pack 正则导致的某个包带有灾难性回溯的inference.regex禁用该包完成同步之后再重跑抽取gbrain sync --no-schema-pack --no-pull --no-embed --yesgbrain schema lint会将经典嵌套量词 ReDoS 形态(a)、(a*)*、……标记为警告。--no-schema-pack在sync.ts中从 v0.41.37.0 (#1569) 起作为逃生舱口存在跳过包加载页面退回到遗留前缀分型legacy prefix typing并会向 stderr 打印明确提示。自动化排障进度感知的停顿看门狗手动诊断有一个自动化表亲进度感知的停顿看门狗progress-aware stall watchdog。如果导入排空在GBRAIN_SYNC_STALL_ABORT_SECONDS默认 900 秒以文件导入进度为键而非锁心跳内没有前进运行会以reason: stall_timeout中止并释放 per-source 锁使下一次gbrain sync从检查点恢复。sync-lock.ts的类型定义确认了stall_timeout是官方partial结果原因之一。两条重要边界看门狗在文件之间触发——卡在某个文件导入内部的挂起会一直跑到墙钟硬截止时间。0可禁用看门狗。完整的同步可恢复性旋钮表位于 CLAUDE.md 的 Sync resumability lock tuning 一节。与周边机制的边界理解本机制还需要区分三组容易混淆的概念详见 docs/architecture/canonical-writers.md 与 docs/guides/concurrent-writes.md受管激活managed activation是显式边界激活后页面正文、frontmatter、可见性、标签、别名、事实、takes、时间线行、源身份/路径与同步检查点都要求协调者权威。SQL 触发器覆盖 pages、tags、slug 别名、自由文本别名、facts、takes、时间线条目与 sources物理嵌入/索引遥测只是投影。锁 ≠ 所有权PGLite 只有一个进程持有者Postgres 允许多个经过鉴权的入口进程但每个规范文件系统根只有一个指定宿主持有者。陈旧心跳是诊断信息绝不授权接管所有权。注册是持久的、独立的委托主体CLI 通道是可信本地管理stdio 是不可信内存调用方。撤销在重启后依然有效丢失凭据文件或收到拒绝响应都不会静默创建一个替代委托主体。stdin 凭据与旧共享 secret 同步都无法获取 CLI 权威。总结gbrain serve与gbrain sync的并发不是靠抢占锁解决的而是靠把同步搬进锁持有者内部解决的CLI 通过 secret 门控的窄类型 IPC把有界分片交给持有单写者连接的 serve 执行不可变清单 持久游标 幂等令牌保证任意时刻中断都能断点续传默认拒绝的旗标白名单保证委派永远不做错误的同步。理解这套委派模型与它的限制表、恢复流程和看门狗是在 PGLite 数据脑上安全运行常驻 serve 与定期同步的前提。【免费下载链接】gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表