Files
qinglong/docs/adr/ADR-0125-postgresql-failover-admission-and-ambiguous-commit-boundary.md

26 KiB
Raw Permalink Blame History

ADR-0125PostgreSQL Failover Admission 与不确定提交边界

  • 状态:ProposedPostgreSQL 18 六角色、可写主库 activation readiness、物理流复制、remote_apply 同步确认、专用复制链分区、fence-before-promote、timeline promotion、旧主 pg_rewind/新 slot/只读同步重入、稳定测试端点切换、双 control 进程摘流与双 fresh activation、Package manager/executor 晋升前后 readiness、scheduler claim-held promotion/expiry takeover、scheduler decision、Worker credential delivery v1/v2/v3/v4、Remote Worker completion、用户 cancellation intent 与 cancellation convergence 的 COMMIT-response-loss,以及通用写后 COMMIT 前/post-COMMIT 连接丢失窗口已验证;生产 operator/proxy TLS、基础设施 STONITH、真实 Pod 网络分区与 raw-wire packet-loss 待实现)
  • 日期:2026-07-23
  • 关联 RFCQL-RFC-0001 D-06、D-34、D-41、D-45、D-53、D-57、D-104、D-106、D-123
  • 关联 ADRADR-0039、ADR-0045、ADR-0053、ADR-0054、ADR-0105、ADR-0107、ADR-0124

背景

QingLong 3.0 已在本机 arm64 PostgreSQL 18.4 上完成 22 条 migration、capability v21、35 张表、六角色最小权限和两个独立 runtime pool 的 scheduler 竞争验证。pg-0017 把 Database/schema/table GRANT 从 CI/fixture 手工 SQL 收回 reviewed stream,并增加 role attribute/CONNECT readinessADR-0137 的 pg-0018 又加入 admin-only Plugin Package installation 三表和受审 Project lock functionADR-0141 的 pg-0019 加入 admin-only ApprovalRequest/Approved Action dispatch 两表、digest 防漂移与受审 Policy fence functionpg-0020 再加入 admin-only Package admission receiptpg-0021 增加 admin-only immutable Package proposal 与 Approved Action execution/start-barrier 两表及精确 FK/CHECK/ACLADR-0144 的 pg-0022 再把 Package proposal/management 与 execution/recovery 拆成 manager/executor 两个最小权限角色,均不扩大 runtime/worker authority。后续 qinglong/postgresql-ha-contract@v1 已增加真实物理流复制、remote_apply 同步确认、专用复制网络隔离、旧 primary 停止 fencing、standby timeline 1→2 promotion、旧主 pg_rewind 后以新 replication slot 只读同步重入、测试专用稳定单写端点切换、两个独立 control 进程的摘流与 fresh activation、Package manager/executor 晋升前后 readiness、持 schedule claim 跨 promotion 后按数据库 expiry 接管并 exact admission,以及 scheduler decision、Worker credential delivery v1/v2/v3/v4、Remote Worker completion、用户 cancellation intent 和 cancellation convergence 在 COMMIT-response-loss 后按耐久事实收敛。它证明了数据库 primary failover 基础链路和这些已提交领域事务的幂等恢复窗口,但测试端点与 promotion guard 不是生产 operator/proxy 或基础设施 STONITH,故障位于 PostgresClient/受控 Docker 网络边界而非 raw-wire packet-loss。

ADR-0147 已将上述历史基线推进为 24 条 migration、capability v23、37 张表,并在同一 physical HA fixture 中增加 manager-only durable quota 和 identity keyset ledger quota 双实例并发 16 个请求精确 8 allow/8 rejectkeyset 双实例同代竞争、全新实例 旧代拒绝、同代 rewrite/隐式移除拒绝与 COMMIT response loss 均收敛。当前具体 HA gate 为 24 个。

当前 cluster-control 在启动时先验证 schema/readiness、完成 recovery,再安装 HTTP admission;这一顺序是安全的。但激活后的 /readyz 只投影 admission 是否仍安装。pg.Pool 的空闲连接错误交给调用方 callback,而借出的 PoolClient 会移除 Pool 自己的 idle error listener,必须由借用方在整个持有期接管连接级 error 事件。两条路径都必须定义该错误如何撤销 admission、处理在途请求,以及数据库恢复后何时允许重新开放。否则旧主停机时,Pod 既可能继续向负载均衡报告 ready,也可能因 checked-out client 的未处理 EventEmitter error 直接退出。

主备切换还会产生不确定提交窗口:客户端可能在 COMMIT 已成功后才丢失响应。对 scheduler、credential delivery、completion 或 cancellation 做通用自动重试,会把“连接恢复”错误提升为重复副作用。

决策

1. 数据库 HA 由稳定写端点提供

常驻 runtime 与 worker-ingress 只接受一个受信、TLS 校验的稳定 read-write endpoint,例如 Kubernetes Service、PostgreSQL operator primary Service 或受审 proxy。QingLong 不在进程内实现主从选举、DNS 轮询、多 host 猜测或自建连接代理。

endpoint 必须只把新连接导向当前可写 primary。runtime、worker-ingress、admin 和 migration 继续使用独立 credential/Pooladmin/migration 不变成常驻 failover sidecar。runtime、admin 与 worker-ingress 的 activation readiness 必须先确认 pg_is_in_recovery()=falsetransaction_read_only=off,否则以 server_not_writable_primary 失败关闭,再在目标 promotion generation 上验证现行 server version、migration history、capability、catalog 与精确权限。migration 的 DDL 仍由 PostgreSQL 在只读目标上原生拒绝;后续可增加同名显式 preflight,但不得因此创建第二套选主逻辑。

2. 活跃 admission 必须有单向 availability fence

cluster-control application 增加进程内、无 timer 的 availability fence

starting -> ready -> unavailable -> stopped

  • 只有 startup readiness、recovery 与 lifecycle 全部成功后才能进入 ready
  • active Pool idle error、连接类 SQLSTATE、read-only primary、schema/capability 漂移或显式 health probe 失败,必须幂等地从 ready 推进到 unavailable
  • 第一次推进必须立即让 /readyz 返回 503,在读取新 /api/v3 body 前拒绝,并 Abort/有界 drain 已安装 admission
  • /livez 可以继续报告进程存活,供编排器区分“需要摘流/重启”与进程死亡;
  • 多个 Pool/error callback 只能合并为一次撤销,不创建 per-error timer、队列或无界错误集合。

unavailable 不允许在原 activation 上只因下一次查询成功而自动回到 ready。重新开放必须新建 activation,重新执行 schema/role readiness、startup recovery、lifecycle start 与 admission install。首个实现可以选择让编排器重启 Pod;未来若支持进程内 reactivation,也必须经过完全相同的 gate。

worker-ingress 使用相同原则,但拥有独立 listener/admission fence。runtime 与 worker-ingress 任一关键数据库 authority 不可用时,部署层必须将对应 endpoint 摘流;不能让一个 Pool 的成功掩盖另一个 Pool 的失效。

3. Pool error callback 只报告,不得抛出或直接重试业务

pg.Pool 的 idle-client error callback 和 checked-out PoolClient 的连接级 error event 都是 availability signal,不是异常重抛点。每条新建物理连接必须安装一个覆盖其完整生命周期的 availability listenerclient binding 仍在 acquire 后安装自己的有界 listener,并在 release 前只移除该 checkout listenerrelease 后由 Pool 恢复 idle listener。物理连接 listener 不随 checkout 切换而移除,用于覆盖旧主停机时“首个 error 已触发摘流、Pool 正在 drain、同一连接又发出后续 error”的窗口。生产组合必须把这些路径交给上述 fence 与低敏 diagnostic sinklistener/callback 内抛异常会绕过有序 drain,也不能证明事务是否提交。

普通 repository 查询仍保留当前有界 timeout 和领域错误映射。availability 分类至少覆盖 PostgreSQL connection exception class 0857P0157P0257P03、read-only transaction 与 endpoint identity/readiness 漂移;constraint、fence conflict 和合法业务拒绝不得误触发全 Pod 摘流。

4. 禁止通用透明事务重试

连接恢复只允许 Pool 为后续新操作建立新连接,不允许基础层自动重放未知事务。每个 mutation 继续依赖现有领域幂等键、版本 fence、immutable receipt 或数据库事实完成裁决:

  • 明确在事务开始前失败,可以由上层按既有 bounded policy 重新发起;
  • 事务内失败且 PostgreSQL 已确认 rollback,可以返回可重试的 unavailable
  • COMMIT 响应丢失属于 outcome_unknown,必须在新 activation/recovery 中读取 durable evidence,禁止原样盲重放外部副作用;
  • scheduler 未提交的 claim 只由数据库时间 expiry takeover;已提交的 Run/idempotency key 决定 exact replay
  • credential delivery/discard、Worker completion 与 cancellation 使用各自 ledger/receipt/fence,不共享一个“重试所有 SQL”开关。

5. 发布证据必须拆开三类故障

不得把一种测试扩大为另一种结论:

  1. control replica failover:同一 primary 上两个独立 Pool/进程竞争与 replica 退出;
  2. connection fail-closedbackend terminate、endpoint 无路由、read-only 切换期间立即 not-ready、拒绝新 body、在途有界 drain;
  3. database primary failover:真实 PostgreSQL 18 primary/standby promotion、稳定 endpoint 切换和新 activation recovery。

正式 HA 门禁至少使用两个 cluster-control 进程或 Pod,并在下列窗口注入故障:连接前、BEGIN 后首个写前、写后 COMMIT 前、COMMIT 响应丢失、claim 持有期间、Worker credential delivery v1/v2/v3/v4、Run completion 与 cancellation。每个场景必须记录 promotion generation、endpoint、旧/新 backend、恢复时间、HTTP readiness 时间线、Run/Event/ledger 数量与重复副作用为零的证据。

网络分区测试必须包含旧 primary 隔离和 fencing;仅停止一个容器再原地启动不构成 split-brain 证据。远端 CI 的 PostgreSQL 16/18 × x64/arm64 单节点矩阵继续保留,但不能替代这一 HA job。

6. 同步确认、分区、Promotion 与旧主重入

Cluster mutation 对客户端返回成功前,至少一个合格 standby 必须已应用对应 WAL;首个 production contract 使用 synchronous_commit=remote_apply 和显式同步 standby。部署若选择异步复制,只能声明 非零 RPO,不能使用 QingLong 3.0 的零 acknowledged-write-loss HA 声明。单 standby 分区时,安全优先于 写可用性:mutation 会停在 SyncRep,调用方超时或连接终止后只能得到 outcome unknown。该事务可能 已存在于旧主本地 WAL,但在 promotion candidate 不存在;它既不是已确认成功,也不能被错误宣称 rollback,必须在新 generation 读取耐久事实后裁决。

promotion 必须严格晚于可验证的旧主 fencing。QingLong 内部不执行 STONITH;生产 operator/proxy 必须 证明旧主不再可写,再暴露新 primary endpoint。测试 guard 只能证明应用门禁顺序,不能替代节点电源、 存储或网络层 fencing。

每个 promoted generation 必须重新应用并复验同步策略,因为 ALTER SYSTEMpostgresql.auto.conf 不是 WAL 复制事实。旧主重入固定为:

  1. promoted primary 创建新的物理 replication slot
  2. 保留足够 WAL,或从受审 archive 恢复 rewind 所需 segment
  3. 对已停止旧主执行 pg_rewind --write-recovery-conf
  4. recovery connection 改回专用 replication role 和新 slot,不复用 rewind superuser
  5. 证明旧主 pg_is_in_recovery=true、receiver streaming,且新主将其标为 sync
  6. 证明分叉期仅存在旧主的未确认事务已消失,并完成一次 post-rejoin remote_apply marker
  7. 以上全部成立后,稳定 endpoint 与 fresh control activation 才可开放。

fixture 的 wal_keep_size=128MB 只是当前有界测试工作集,不是生产容量默认值。生产保留量必须按最大 分区时长、写入速率、checkpoint 和 archive RTO 计算;WAL 不足时只能执行新的 base backup,不能把 rewind 失败的旧主直接启动。

7. 资源与部署边界

本决策不影响 edge/standalone:它们继续使用单 SQLite authority。cluster-control 不新增 workspace package、常驻 sidecar、watcher 或 per-Run timer。稳定 endpoint/proxy/operator 属于集群部署依赖,必须单独记录资源、版本、TLS 和故障域,不能计入路由器 Profile。

拒绝的方案

  • pg.Pool 报错后仍保持 ready,等每个 handler 自行 503:拒绝,因为负载均衡会持续发送新流量并放大故障。
  • callback 内直接 throw:拒绝,因为绕过 admission drain,且无法裁决不确定提交。
  • 查询成功一次就原地恢复 ready:拒绝,因为没有重跑 schema/role readiness 和 startup recovery。
  • 所有连接错误统一重试事务:拒绝,因为 COMMIT response loss 会导致重复 mutation 或外部副作用。
  • 应用内自建多 host 选主:拒绝,因为复制数据库 operator/proxy 的职责,难以证明 split-brain fencing。
  • 用双 Pool row-lease 测试宣称 PostgreSQL HA:拒绝,因为没有 primary promotion 或 endpoint 切换。

当前证据

  • 本机 postgres:18 为 PostgreSQL 18.4、arm64,镜像摘要 sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a22 条 migration、capability v21、35 张表。pg-0017 先复验四个非特权 LOGIN role,再由 migration stream 安装精确 Database/schema/table GRANTpg-0018 只为 admin 安装 Plugin Package 三表及单用途 Project lock functionpg-0019 只为 admin 安装 ApprovalRequest/dispatch 两表及 Policy fence functionpg-0020 只为 admin 安装 Package admission receiptpg-0021 只为 admin 安装 immutable Package proposal 与 Approved Action execution/start-barrier 两表;pg-0022 再把 Package proposal/management 与 execution/recovery 分离为 manager/executor 两个非特权 LOGIN roleHA fixture 不复制手工 GRANT。
  • ADR-0147 的当前增量为 24 条 migration、capability v23、37 张表;pg-0023 给 manager 安装 durable management quotapg-0024 增加单行 keyset ledger, 其他五角色不扩权。
  • cluster-postgres 六角色真库 integration 31 pass、1 个同角色 backend termination 条件 skip、0 failcluster-control PostgreSQL integration 6/6、0 skip。
  • 两个独立 runtime pool 的 backend PID 不同;初始化与同一 occurrence 各只有一个赢家,持 claim 的 pool 关闭并强制数据库时间过期后由另一 owner 收敛。
  • 当前审计同时发现并修正 capability v15 后 CI admin/worker-ingress GRANT 漂移,以及 Worker listener 的旧 capability 魔法数字/缺失 versioned register schema。
  • cluster-control 已实现单 listener、无 timer/重试/历史队列的 one-way availability fence;生产入口从同一 enabled config 原子创建 runtime Pool 与 fence,禁止部署侧分别装配而发生错配。
  • fake HTTP/Pool 路径已证明 ready→unavailable、并发/早期 signal 合并、body 前 503、/readyz 503、/livez 200 和 stop 幂等;2026-07-24 cluster-control 全量测试 141 项中 139 pass、2 个外部服务条件 skip、0 fail。
  • 本机 PostgreSQL 18.4 已用 migration superuser 执行 pg_terminate_backend 终止 production runtime Pool 的唯一 idle backend;真实 pg.Pool error 触发摘流,cluster-control integration 6/6、0 skip。
  • PostgreSQL adapter 对 query、transaction client acquire/query、checked-out client connection event、物理连接生命周期和 idle Pool 五条路径统一报告 availability;查询路径只接受 SQLSTATE class 08、2500657P0157P04 与明确网络错误。唯一键、serialization/deadlock、lock timeout 和 statement cancellation 保留原异常但不触发整 Pod fence。2026-07-25 HA 重跑暴露出更窄的双 error 竞态:第一个 57P01 已触发 availability drain 后,同一物理连接在 checkout listener 移除与 Pool teardown 期间又发出 error,导致旧 control 进程退出。连接级 lifetime listener 修复后,cluster-postgres 全量测试为 132 pass、1 个真库条件 skip、0 failcluster-admin 为 74 pass、1 个 Kubernetes 条件 skip、0 fail,完整物理 HA 门重新通过。
  • runtime、admin 与 worker-ingress readiness 的首个 server observation 现在同时读取 pg_is_in_recovery()transaction_read_onlystandby 和 read-only transaction 均在 migration history、catalog、role capability 与 application assembly 前以 server_not_writable_primary 拒绝。package/legacy parity 的正向报告固定包含 writablePrimary:true,并有两个独立负向门禁。
  • 旧 activation 摘流后不会原地恢复;测试显式停止旧 application,再以同一稳定配置创建全新 production activation。新实例重新通过 schema/role readiness、startup recovery 与 lifecycle gate,以不同 backend PID 返回 ready。
  • pnpm test:postgres-ha:ql3 使用 postgres:18repo digest sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a)创建两个独立数据卷、primary/standby 管理网络和专用复制网络。初始 standby 必须同时满足 recovery/streaming/syncremote_apply marker 返回前必须已在 standby 可见。
  • PostgreSQL 18 镜像声明的 volume root 是 /var/lib/postgresql,而实际 PGDATA/var/lib/postgresql/18/dockerfixture 必须把 primary/standby 具名卷挂到前者,并 显式把后者作为 PGDATA,不能只挂子目录而让 Docker 为父级再创建匿名卷。所有临时 container 以 rm -v 回收,但具名卷仍由 exact name 单独删除。2026-08-04 no-start docker create smoke 的实际 Mounts 只有预期具名卷,专用 smoke container/volume 随后均已删除。释放 Docker overlay 并补齐空 standby PGDATA 创建后,完整 promotion 门在 18.4 arm64 上退出 0timeline 1→2、remote_apply、旧主 fence/rewind/只读同步 重入、双 fresh control 及总 gates.passed=truefail-close 325.582 ms、fresh activation 456.102 ms、rewind 1,831.809 ms。运行前后 Docker volume 总数保持 913 ql3-ha-* container/volume/network 零残留,受保护 control-plane 未被操作。
  • fixture 摘除旧 primary 的复制接口并终止唯一既有 walsender,保留客户端通道。Docker 拓扑确认复制网络只剩 standby,旧主 pg_stat_replication 为 0test-only promotion guard 在旧主仍 writable 时明确拒绝。分区后的 remote_apply COMMIT 被确认卡在 SyncRep,最近一次证据在 1,510.482 ms 后模拟客户端超时并终止专用 backend;该行只存在旧主本地,promotion candidate 为 0,调用方从未得到成功。
  • 门禁随后停止旧 primary 并确认容器为 exited,再等待两个 generation-1 control 子进程均 unavailable、/readyz 503 且 /livez 200standby timeline 1→2 promotion 后,分区期未确认 marker 仍为 0。新 primary generation 重新应用同步配置并创建新物理 slot;测试以 wal_keep_size=128MB 保留分叉 WALpg_rewind --write-recovery-conf 退出 0,旧主使用专用 ql3_replicator 和新 slot 以 inRecovery=true、streaming、sync 重入。一次 post-rejoin remote_apply marker 在旧主可见,分叉 marker 为 0;稳定端点此后才切换。
  • 两个 generation-2 control 子进程通过同一稳定地址重新完成包含可写主库与当前 role capability observation 的 readiness/recovery/lifecycle2026-07-29 最近一次本机 arm64 PostgreSQL 18.4repo digest sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a)证据为 fail-closed 263.784 ms、双 fresh activation 412.927 ms,旧主 pg_rewind 11,127.484 ms。完整报告的 35 个具体 gate 与总 passed 均为 true,其中包含晋升前后 Package manager/executor authority readiness、AI schema、Package quarantine/trust、Tool snapshot/artifact/result key 和全部既有 COMMIT-response-loss contract;切换前后 marker 各 1,领域 fixture 合计 5 Run、13 Eventcredential delivery 为 4 条连续状态,unexpected domain side effect 为 0。
  • scheduler claim-held 窗口已加入同一物理 promotion 门禁:primary 以数据库时间 1785289093833 取得 15 秒 claim,并在 standby 复验 owner/token/version/next-fire 的 WAL replay;旧主 fencing 前 claim 仍剩 14,589 ms 且 occurrence Run 数为 0。promotion 时间为 1785289096600fresh control 在 claim expiry 1785289108833 之后的 1785289109305 才完成 admissionclaim version 1→2 后清空,同一 scheduled time 最终严格为 1 queued Run、1 claimed remote-worker Attempt、2 Event、0 duplicate。
  • scheduler decision COMMIT-response-loss 已加入同一物理 promotion 门禁:真实 ClusterSchedulerCoordinator 经独立 runtime Pool 进入到期 decision transactiondriver 确认 COMMIT 后,fixture 终止该 backend 并让上层观察 ECONNRESET。standby 在 promotion 前已通过 WAL 看到完整 1 Run/1 Attempt/2 Event 与 claim 清空,promotion 后 fresh control 没有重放 occurrence,最终 0 duplicate。故障范围是“driver-confirmed COMMIT + backend self-termination + PostgresClient 边界失败”,不是 raw-wire packet-loss。
  • Worker credential delivery v1/v2/v3/v4 COMMIT-response-loss 已加入同一物理 promotion 门禁,并使用独立最小权限 ql3_adminql3_worker_ingress Pool。standby 在 promotion 前依次看到 [1][1,2][1,2,3][1,2,3,4]promotion 后仍只有 4 条连续 ledger、3 条 credential history、3 条 mutation/auditstage/publish/entropy 各 1 次,旧 credential 为 active → revoked,恢复候选为 0,领域行不含原始 ql3w token。四个 fixture 均在 driver 确认 COMMIT 后终止 transaction backend 并让调用方观察失败,故障范围仍是 PostgresClient 边界,不是 raw-wire packet-loss 或生产 Secret provider 故障。
  • Remote Worker completion COMMIT-response-loss 已加入同一物理 promotion 门禁:fixture 从 durable starting authority 在一个 runtime-role transaction 中完成 Lease、Attempt、Run 与双 Eventdriver 确认 COMMIT 后终止 transaction backend,首次调用得到 REMOTE_WORKER_COMPLETION_UNAVAILABLE。健康连接以同一 fence/receipt/Event identity 重放得到 already_completedstandby 在 promotion 前已看到 succeeded Run/Attempt、completed Lease version 5 和 2 条不同 dedupe key 的 Eventtimeline 1→2 promotion 后仍为 2 条,duplicate 为 0。
  • 用户 cancellation intent 与 cancellation convergence 各自拥有独立 COMMIT-response-loss fixture。意图事务提交 Project/RoleBinding-fenced run.cancel_requested 后调用方收到 unavailable,同一 mutation 重放为 already_requested;收敛事务再原子提交 claimed Attempt 与 queued Run 的 cancelled 终态和两条 reconciler Event,调用方收到 convergence unavailable。健康重放扫描 0 条;standby 在 promotion 前及 promoted primary 上均严格保持 run.cancel_requested → attempt.cancelled → run.cancelled、Run version 4/event sequence 3、3 个不同 dedupe keyduplicate 为 0。
  • 该场景同时暴露并修复 startup recovery 与 dispatcher authority 的冲突:正常 scheduler admission 会留下 queued Run + claimed remote_worker Attempt。recovery source 现在只排除仍为最新、未取消、未拥有 worker/lease/offer/callback/start/result/error 状态的精确 pristine dispatch candidate;任何漂移仍进入恢复候选并 fail closed。否则 fresh activation 会把正常待分发工作误判为人工恢复并拒绝启动。
  • 通用事务窗口已加入同一物理 promotion 门禁:客户端先完成 COMMIT,再通过 backend 自终止让整个外层操作观察失败,promotion 后 marker 恰好为 1,证明必须读取耐久事实且禁止透明重放;另一个事务在写入后、COMMIT 前停止旧 primary,客户端 COMMIT 失败且 promotion 后 marker 为 0。这两者只证明 PostgreSQL 事务基线,不替代 completion 或 cancellation 各自的领域幂等/receipt/fence 矩阵。
  • GitHub Actions 增加独立 PostgreSQL 18 x64/arm64 physical-promotion job;它不与单节点 PostgreSQL 16/18 四角色矩阵混算。
  • 尚未证明生产 operator/proxy 的 TLS/健康路由、节点/存储级 STONITH、真实 Pod 网络分区与 split-brain fencing、raw-wire packet-loss。受控 Docker 复制链分区、fence-before-promote、同步 RPO 裁决、旧主 rewind/rejoin,以及 PostgresClient 边界的 scheduler decision、credential delivery v1/v2/v3/v4、completion、cancellation intent/convergence COMMIT-response-loss 已完成;这些不等价于生产 operator 认证或所有 mutation 的逐 statement fault matrix。

实施顺序

  1. 已完成:在 cluster-control 定义一次性 availability fence 与 admission disposer ownershipPool callback 只提交 signal,生产配置绑定不暴露 callback 拼装缝隙;
  2. 已完成:用 fake Pool/HTTP 测试证明 ready→unavailable、body 前拒绝、早期/并发 signal 合并和 stop 幂等;
  3. 已完成PostgreSQL 18 idle/active pg_terminate_backend 已证明真实 Pool/query error 摘流;runtime/admin/worker-ingress activation readiness 已在 schema 检查前拒绝 standby/read-only 目标;旧 activation 停止后 fresh production activation 已重新跑完整 gate 并以新 backend ready;通用写后 COMMIT 前回滚与 post-COMMIT 连接丢失后的耐久检查、scheduler claim-held promotion,以及 PostgresClient 边界的 decision、credential v1/v2/v3/v4、completion、cancellation intent/convergence COMMIT-response-loss 已验证;
  4. 已完成(测试级)primary/standby fixture 已以两个真实 control 进程完成物理 streaming、remote_apply、专用复制链分区、未确认事务裁决、fence-before-promote、timeline promotion、新 generation 同步策略重放、新 slot、旧主 rewind/read-only sync rejoin、endpoint switch、双 fresh activation、scheduler expiry takeover,以及已提交 decision、credential ledger、completion 与 cancellation 的 exact convergence
  5. 未完成(生产级):使用真实 PostgreSQL operator/proxy、TLS endpoint、Pod/节点网络分区和基础设施 STONITH 重跑同一不变量,并增加 raw-wire PostgreSQL response-loss
  6. 记录恢复时间、重复率、未决事务裁决、连接池/RSS 和日志,达标后再把本 ADR 提升为 Accepted。