fix: retry precommit mysql lock conflicts

This commit is contained in:
2026-09-20 21:47:30 +08:00
parent 30a8081bd9
commit a1e6fcc07f
7 changed files with 170 additions and 8 deletions

View File

@@ -124,7 +124,7 @@ stateDiagram-v2
任务领取可评估短事务配合 `SELECT ... FOR UPDATE SKIP LOCKED`;其适用场景是队列类访问,不应被用于跳过业务一致性校验。[MySQL 锁定读取说明](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking-reads.html)
所有事务明确锁定顺序,处理死锁和提交结果不明。提交结果不明时先通过稳定操作身份核对,不能换身份重试造成额外任务。
所有事务明确锁定顺序,处理死锁和提交结果不明。平台事务封装只对提交前由 MySQL 返回的 1205(锁等待超时)或 1213(死锁)回调错误做最多两次、带上下文取消的短退避重试;Begin、Commit 或其他错误不会自动重放。提交结果不明时先通过稳定操作身份核对,不能换身份重试造成额外任务。该重试只适用于数据库回调,处理器和外部副作用始终在事务外运行。
## 6. 可靠衔接

View File

@@ -80,7 +80,7 @@ P4/P5定时等待与后续查询轮次的实现及回归记录见[时钟报告](
### P6 首批实施顺序
1. 已补[首批可复现故障工具](reports/phase-6-fault-boundaries.md):A04“派发已提交但消息尚未送达”、W01/W13事务提交结果不明,以及W16最终回执/旧PENDING结果重放/下一轮竞争。迁移器同时提供当前版本无外键新装基线;每个切点必须有实际数据库或进程证据,不能用响应错误推断事务是否已提交,也不能用普通SIGNAL竞争替代最终查询场景。
1. 已补[首批可复现故障工具](reports/phase-6-fault-boundaries.md):A04“派发已提交但消息尚未送达”、W01/W13事务提交结果不明,以及W16最终回执/旧PENDING结果重放/下一轮竞争。迁移器同时提供当前版本无外键新装基线;每个切点必须有实际数据库或进程证据,不能用响应错误推断事务是否已提交,也不能用普通SIGNAL竞争替代最终查询场景。数据库事务封装另对提交前 MySQL 1205/1213 做最多两次短退避重试,Commit 回执不明保持原身份核对语义,真实锁环回归见[P7当前回归状态](reports/phase-7-regression-status.md)。
2. 已实现[基础运行观测](api/observability.md)及[有界业务状态与事件采样](reports/phase-6-business-observability.md):HTTP/WS、数据库连接池、后台批次、固定任务/交付/流程/Inbox状态计数、最近事件类别、终态耗时样本、固定事件阶段耗时、最早年龄、截断/新鲜度指标和可离线验证的告警规则;独立采集监听默认关闭,固定指标目录拒绝动态标签;发布前只读结构核验由[`orbit-admin verify`](api/operations.md)提供,运维人员也可用 `orbit-admin observe snapshot` 获取同一有界单节点快照;后台循环间隔、批量上限、Worker/Event连接上限、Hub领取协程和有界内存排队均支持有界环境配置,并在启动日志和固定指标中记录。仍需端到端外部效果延迟、全局持久吞吐、Inbox从事件产生到推进完成的精确阶段延迟、全局发送队列、多节点聚合及生产采集/通知验证;SDK受控重连风暴和真实慢消费者背压已补[边界回归](reports/phase-6-reconnect-storm.md)与[慢消费者报告](reports/phase-6-slow-consumer.md),跨节点重连仍待目标拓扑。
3. 已补齐受保护管理环境中的固定凭据操作文件、轮换、撤销、提交不明故障切点、幂等回执和脱敏管理审计;轮换保留授权绑定并要求新令牌重新建立会话,旧 Worker 会话被排空且立即过期,撤销允许在停用范围内执行且不改写历史记录。已用一次性 MySQL 定向验证旧令牌失效、新令牌重绑和零外键;仍需多节点安全重绑演练、凭据最小权限配置核对和生产密钥托管/审计演练。
4. 已补[`orbit-admin retention inventory`](api/retention.md)只读盘点和[阶段报告](reports/phase-6-retention-inventory.md),能区分过期幂等、确定终态、未知效果、未终结流程、已确认/未确认交付、已消费/未消费Inbox、事件和审计规模;它不执行归档或删除。已补[容量模型](19-capacity-model.md),把企业数转换为任务、流程、事件、连接、写放大和保留期输入;仍需评审各类保留周期、归档格式、受保护记录核对,再执行备份恢复、兼容升级和六类容量负载。未确认交付、有效幂等身份、当前/历史执行证据和未终结流程不能被普通清理破坏。生产HA及RPO/RTO继续按实际拓扑验证,不缩减为本机多进程检查。

View File

@@ -45,7 +45,7 @@ curl -fsS http://127.0.0.1:8080/health/ready
`live` 只表示进程仍在运行;`ready` 同时核对 MySQL、迁移账本和零外键要求。收到 `503` 时停止向该节点发送新的受理、执行授权和流程推进请求,保留原幂等键并等待核对,不把网络错误当作业务失败。
后台调度默认每秒检查 Weave 和计划、每两秒执行一次执行恢复,每批最多处理100条持久化候选。容量验证后可以通过 `ORBIT_WORKFLOW_TICK_INTERVAL`、`ORBIT_SCHEDULE_TICK_INTERVAL`、`ORBIT_RECOVERY_TICK_INTERVAL` 和 `ORBIT_RUNTIME_BATCH_LIMIT` 调整;间隔只允许 `100ms` 到 `1m`,批量只允许 `1` 到 `100`。`ORBIT_SHUTDOWN_TIMEOUT` 控制 SIGTERM 后的排空和优雅关闭窗口,允许 `1s` 到 `5m`,默认 `10s`。Worker/Event WebSocket 连接上限分别由 `ORBIT_WORKER_CONNECTION_LIMIT`、`ORBIT_EVENT_CONNECTION_LIMIT` 控制,范围 `1` 到 `4096`;每个 Hub 的数据库领取协程和有界内存排队由 `ORBIT_DISPATCH_WORKERS`、`ORBIT_DISPATCH_CAPACITY` 控制,范围分别为 `1` 到 `256`、`1` 到 `65536`。这些参数只影响单节点内存和领取并发,真实授权、租约和配额仍由 MySQL 决定。调优必须和数据库锁等待、连接池、积压及恢复窗口一起记录,不能只提高批量或缩短间隔来掩盖容量不足。
后台调度默认每秒检查 Weave 和计划、每两秒执行一次执行恢复,每批最多处理100条持久化候选。容量验证后可以通过 `ORBIT_WORKFLOW_TICK_INTERVAL`、`ORBIT_SCHEDULE_TICK_INTERVAL`、`ORBIT_RECOVERY_TICK_INTERVAL` 和 `ORBIT_RUNTIME_BATCH_LIMIT` 调整;间隔只允许 `100ms` 到 `1m`,批量只允许 `1` 到 `100`。`ORBIT_SHUTDOWN_TIMEOUT` 控制 SIGTERM 后的排空和优雅关闭窗口,允许 `1s` 到 `5m`,默认 `10s`。Worker/Event WebSocket 连接上限分别由 `ORBIT_WORKER_CONNECTION_LIMIT`、`ORBIT_EVENT_CONNECTION_LIMIT` 控制,范围 `1` 到 `4096`;每个 Hub 的数据库领取协程和有界内存排队由 `ORBIT_DISPATCH_WORKERS`、`ORBIT_DISPATCH_CAPACITY` 控制,范围分别为 `1` 到 `256`、`1` 到 `65536`。数据库事务对提交前的 1205/1213 只做最多两次短退避重试,提交回执不明绝不自动重放;因此仍必须保留稳定幂等键并核对原操作。参数只影响单节点内存和领取并发,真实授权、租约和配额仍由 MySQL 决定。调优必须和数据库锁等待、连接池、积压及恢复窗口一起记录,不能只提高批量或缩短间隔来掩盖容量不足。
## 3. 滚动停止与替换

View File

@@ -9,6 +9,7 @@
| 本地发布门禁统一入口 | 2026-09-20;`make release-local-check` | PASS;服务端、SDK、独立示例竞态测试、vet、build、当前协议门禁、146个 Markdown 本地链接和 SDK 独立依赖检查均通过;命令明确不替代真实 MySQL 集成与目标生产验收 |
| 发布物静态身份清单 | 2026-09-20;`make release-manifest`;无 `ORBIT_MYSQL_DSN` | PASS;输出构建/协议身份及28条迁移名称与SHA-256校验和,未读取数据库、凭据或业务数据;目标库仍需单独执行 `orbit-admin verify` |
| 发布物与管理二进制绑定校验 | 2026-09-20;`go test -race ./cmd/orbit-admin`;匹配/修改 revision/未知命令场景 | PASS;`verify --manifest` 在数据库连接前拒绝身份、协议或迁移校验和不一致的清单,匹配清单通过;不修改数据库 |
| 提交前事务死锁恢复 | `go test -race -run TestRetryableTransactionErrorOnlyMatchesPreCommitLockFailures ./internal/storage/mysql`;真实 MySQL `TestIntegrationWithTxRetriesPreCommitDeadlock` | PASS;两事务真实锁环触发 1213,失败事务仅在提交前按有界退避重试一次,最终两笔事务各提交一次;Begin/Commit 错误不重放,目标拓扑锁等待和死锁率仍需生产观测 |
| 就绪探针排空门控 | `go test -race ./internal/transport/httpserver ./cmd/orbit`;`TestReadinessEntersDrainBeforeShutdown` | PASS;SIGTERM 关闭路径先将 `/health/ready` 置为 503,再进入限定优雅关闭窗口;响应不泄露内部错误 |
| 健康探针响应契约 | 2026-09-20;`go test ./internal/transport/httpserver && go test -race ./internal/transport/httpserver && make docs-check` | PASS;`/health/live` 与 `/health/ready` 返回 `Cache-Control: no-store`、`X-Content-Type-Options: nosniff`;未就绪和排空状态返回 `Retry-After: 1`,并由 HTTP 契约测试覆盖 |
| 数据模型中文注释门禁 | 2026-09-20;`go test ./internal/storage/mysql -run 'Test(FreshInstallBaselineHasChineseModelComments|FreshInstallBaselineContainsNoReferenceDDL|MigrationsAfterForeignKeyRemovalDoNotReintroduceReferences|PostRemovalMigrationGuard)$'` | PASS;当前无外键基线逐表、逐持久化字段检查中文 `COMMENT`,并继续阻止后续迁移重新引入引用约束 |
@@ -27,6 +28,7 @@
| Go 1.27.1 标准全量隔离回归(优雅下线排空门控后) | 2026-09-20;宿主机 Go 1.27.1 darwin/arm64;`GO=go DOCKER=docker ORBIT_TEST_MYSQL_IMAGE=mysql:8.4 sh scripts/test-mysql.sh`;Docker MySQL 8.4.10 | PASS,退出码0;存储/迁移252.852秒、metrics进程6.762秒、Orbit流程全停接管45.696秒、SDK Worker/Event/任务/流程故障组、24个示例、3个负载工具及 result-inbox/task-outbox 均完成真实服务串跑;就绪排空门控未破坏租约恢复、流程接续或优雅退出,临时数据库已清理 |
| Go 1.27.1 标准全量隔离回归(排空状态指标后) | 2026-09-20;宿主机 Go 1.27.1 darwin/arm64;`GO=go DOCKER=docker ORBIT_TEST_MYSQL_IMAGE=mysql:8.4 sh scripts/test-mysql.sh`;Docker MySQL 8.4.10 | PASS,退出码0;存储/迁移249.882秒、metrics进程6.802秒、Orbit流程全停接管45.554秒、SDK Worker/Event/任务/流程故障组、24个示例、3个负载工具及 result-inbox/task-outbox 均完成真实服务串跑;`orbit_process_draining` 固定 gauge 和关闭路径未破坏既有行为,临时数据库已清理 |
| Go 1.27.1 标准全量隔离回归(Hub 背压配置后) | 2026-09-20;宿主机 Go 1.27.1 darwin/arm64;`GO=go DOCKER=docker ORBIT_TEST_MYSQL_IMAGE=mysql:8.4 make integration`;Docker MySQL 8.4.10 | PASS,退出码0;存储与迁移、metrics 启停、流程全停接管、Worker/Event 租约与 ACK 恢复、队列背压、优先级/键并发、重试与未知结果、定时任务、Weave 流程暂停/信号/定时器/查询/恢复、24 个 SDK 示例、3 个负载工具及 result-inbox/task-outbox 均完成真实服务串跑;Hub 连接上限、派发协程和有界队列配置未破坏既有行为,临时数据库已清理 |
| Go 1.27.1 标准全量隔离回归(提交前事务重试后) | 2026-09-20;宿主机 Go 1.27.1 darwin/arm64;`GO=go DOCKER=docker ORBIT_TEST_MYSQL_IMAGE=mysql:8.4 make integration`;Docker MySQL 8.4.10 | PASS,退出码0;存储/迁移254.604秒,其中真实 `TestIntegrationWithTxRetriesPreCommitDeadlock` 触发两事务锁环并确认 1205/1213 前提交重试;metrics进程7.156秒、流程全停接管45.734秒、SDK Worker/Event/任务进程组130.052秒、流程进程组14.121秒、Workflow SDK29.432秒、result-inbox35.737秒、task-outbox34.500秒,24个示例和3个负载工具均完成真实服务串跑;提交回执不明故障、租约恢复、Weave流程恢复和原幂等行为均保持,临时数据库已清理 |
| MySQL COMMIT 包截断故障 | MySQL 8.4.10 与 Go 测试容器共享网络命名空间;`127.0.0.1` DSN;两组提交前/后切点 | PASS;`TestIntegrationWorkflowCommitFaults` 8个子场景与 `TestIntegrationCredentialCommitFaults` 均通过,预期 `unexpected EOF` 为故障注入结果 |
| Go 1.27.1 Orbit 控制进程全停/接管 | 同一共享网络命名空间;容器内构建 Linux Orbit 二进制;`ORBIT_TEST_SERVER_BINARY=/tmp/orbit go test -race -run '^TestIntegrationWorkflowRuntimeProcess$'` | PASS,42.958 秒;强杀、全停、定时器、轮询、信号、人工复核和双节点接管均通过 |
| Go 1.27.1 SDK Worker/Event 子进程故障 | 同一共享网络命名空间;容器内构建 Orbit 二进制;SDK `TestIntegration(Worker\|Event)Process` | PASS,121.600 秒;派发未送达、租约接管、ACK 提交回执丢失、慢消费者对等节点推进、重试未知结果、计划/优先级/键并发和任务接管均通过 |