Skip to content

Fix PostgreSQL identity rotation race with commits - #5943

Merged
huangruiteng merged 4 commits into
loopx-project:mainfrom
Duang777:codex/fix-postgresql-authority-identity-rotation-race-20261008
Oct 8, 2026
Merged

huangruiteng merged 4 commits into
loopx-project:mainfrom
Duang777:codex/fix-postgresql-authority-identity-rotation-race-20261008

Conversation

@Duang777

@Duang777 Duang777 commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • fence every PostgreSQL authority commit with a transaction-scoped shared identity lock
  • make identity rotation take the matching exclusive lock before reading or changing metadata
  • preserve concurrency across independent Goals and preserve the runtime role's read-only metadata privilege
  • add a real PostgreSQL regression that controls the commit/rotation interleaving and verifies readback identity

Problem

A commit could read store identity A, pause before locking its Goal head, and then finish after rotation had committed identity B. The commit returned an A-scoped revision even though subsequent reads and receipts were B-scoped, causing the next CAS to conflict immediately.

The lock order is now identity lock, then per-Goal head lock. Shared commit locks remain compatible with one another; rotation uses exclusive mode.

Closes #5941

Validation

  • red-before-fix real PostgreSQL characterization: rotation completed while the commit was paused
  • npm run typecheck:control-plane
  • npm run test:postgresql-authority-service with both live PostgreSQL URLs: 11 passed, 0 skipped
  • npm run test:postgresql-authority-store with live PostgreSQL: 339 passed
  • python -m pytest tests/test_postgresql_integration_workflow.py -q: 6 passed
  • loopx check on the four changed paths: 0 errors, 0 warnings
  • loopx canary premerge --from-git-diff --git-diff-base upstream/main: 13 selected checks passed
  • git diff --check

Baseline

Audited and implemented from upstream/main@82d1b837479dd5eb64b581bb0ca36d8c1ff1f0cc.

Signed-off-by: Duang777 <duangjl007@gmail.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=xhigh

动机

使用 PostgreSQL 保存长期任务状态、并由管理员轮换数据库身份的调用者。 原来:一次写入读取旧身份后暂停,管理员完成轮换,写入随后提交成功却返回旧 revision;调用者用这个成功回执继续工作时立即遇到 CAS 冲突。现在:写入与轮换有数据库保证的先后次序,不能先轮换完成再提交旧身份的写入;普通不同 Goal 的提交仍可并行。

相同真实数据库反例在基线复现旧身份回执与新身份读回不一致,在本 head 通过;反向交错、旧 revision 拒绝、新 revision 继续写入也通过。 本 PR 不启用 PostgreSQL、不晋升 provider、不改变 File/SQLite/NoKV,也不授予 agent 数据库管理或恢复权限。

改动思路

身份一致性是既有 PostgreSQL 提交不变量,应由当前 TypeScript provider 在事务内保证;共享/独占 advisory lock 复用同一 helper,不新增并行策略 owner。 本 PR 只交付数据库提交与身份轮换的序列化修复及回归测试,服务部署、认证 transport、完整备份恢复和跨版本升级编排保持既有边界。

不改会保留可复现竞态;只锁 Goal head 无法保护全库身份;让每次写入独占身份锁会丢失独立 Goal 并行;对 metadata 使用行锁可能需要扩大 runtime 的 metadata 权限。因此采用事务级共享/独占 advisory lock,仍由原 provider 和管理员轮换 owner 执行。此处 advisory 是数据库锁 API 名称,保证由数据库强制执行。

独立验收是 Issue #5941,spec_revision 82d1b837479dd5eb64b581bb0ca36d8c1ff1f0cc 的审计基线;Expected result:写入/轮换有共同先后次序且独立 Goal 并行;Proposed slice:身份锁先于 Goal head,其他 provider/schema/revision 保持;Validation:真实临时 PostgreSQL、类型检查及独立并行反例。另读了该基线的 docs/reference/postgresql-authority-service-v0.md,保留 admin 轮换与旧 revision 拒绝、历史 receipt 可读的既有契约。

具体改动

完整 head 178048b94b8cf0bcc9183331f99cb87a6c299ba1;实际共同基线 82d1b837479dd5eb64b581bb0ca36d8c1ff1f0cc,当前 PR base 为较新的 5cb9e4b8222a94a24a38ad2d7163e32322c11f73。全4文件 +149/-1,其中生产代码仅18行。

postgresql_authority_store.ts:360 的私有 lockStoreIdentityTransaction 用局部 shared | exclusive 类型选择两个事务级锁函数,并把同一 bigint key 参数化传入。commitAuthority(:655)在事务内先取共享锁,再读身份、锁 Goal head、检查 CAS 并提交原 events/receipts;rotatePostgreSqlAuthorityStoreIdentity(:465)先取对应独占锁,再读/更新 singleton metadata。释放仍随 COMMIT/ROLLBACK,原 failed/conflict/ambiguous 处理及连接清理不变。

postgresql_authority_service.integration.test.ts 新测试在真实 metadata 查询之后暂停写入:另一 Goal 必须能完成,而带 lock timeout 的轮换不能完成;随后提交回执与独立 load 一致。另两个测试文件更新 mock 对锁 query 的响应并检查锁先于 metadata 读取,没有新增公开 schema、CLI 或并行决策源。

对主干的风险

本轮使用独立的相同数据库 harness 验证完整顺序。基线实际返回 A:1,但 load 与 receipt 都为 B:1,并触发“轮换必须等待”的失败;本 head 相同合成 fixture 通过,另一 Goal 在 held writer 未释放时确实完成。又在真实轮换 UPDATE 后暂停,新的 Goal 提交必须等待,轮换结束后产生 B:1;旧 A:1 CAS 被拒绝,新 B:1 CAS 实际写到 B:2。这覆盖超时拒绝后的恢复到进度,不能只靠两个 mock 的成功结果。

真实隔离 PostgreSQL17:服务11项、store339项通过,均零 skipped;store 包含 restricted runtime role、tenant RLS、metadata UPDATE 拒绝、atomic rollback、丢失 COMMIT 回应与历史 receipt 恢复。轮换单测6项、Python 工作流6项、TypeScript typecheck 通过。原生 premerge 的3项直接、13项选定检查(5项 catalog +8项 risk) 检查全部通过,无 manual hold。未查询、轮询或等待 CI。

主要剩余风险是参与者版本:旧写入/旧轮换实现不认识这个 advisory key,因此混合版本部署不能声称同样受保护;部署必须让这两个当前 owner 一起升级。锁是全数据库 identity 的必要序列化点,管理员轮换可能暂时等待普通写入;长时运行、真实恢复/failover、部署 transport 和安装版产品采用未在本轮证明。

我的整体评价

APPROVE,无阻塞发现。身份一致性是既有 PostgreSQL 提交不变量,应由当前 TypeScript provider 在事务内保证;共享/独占 advisory lock 复用同一 helper,不新增并行策略 owner。 本 PR 只交付数据库提交与身份轮换的序列化修复及回归测试,服务部署、认证 transport、完整备份恢复和跨版本升级编排保持既有边界。 有限未来重构已应用为一个共享 helper 与明确锁顺序,没有新增 provider/框架,也没有第二份 Python 状态规则。新模式是局部类型词汇;既有身份、revision、授权与其他 provider 保持原 owner。

该结论覆盖当前源代码的实际数据库路径;不把它当作 PostgreSQL 晋升、完整恢复或部署资格。维护者负责 core 合并。

English verdict: APPROVE - 178048b; transaction-scoped shared/exclusive identity fencing fixes the reproduced commit/rotation race while preserving independent Goal concurrency and runtime metadata privileges. Same-harness baseline/head and reverse-order/stale/new-CAS checks passed; 11 service,339 store,6 unit,6 Python tests and typecheck/premerge passed with real PostgreSQL17 and no skipped integration. Mixed-version/deployed recovery remains unqualified.

Duang777 and others added 3 commits October 8, 2026 14:29
@huangruiteng
huangruiteng merged commit 05af21d into loopx-project:main Oct 8, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PostgreSQL authority identity rotation can commit under a retired lineage

3 participants