feat: provide foreign-key-free fresh install baseline
This commit is contained in:
@@ -126,7 +126,7 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
|
||||
|
||||
000007_task_cancellation 允许尝试证据记录 CANCELLED,不新增实体;取消意图、结果和历史不确定效果的规则见 [取消契约](api/cancellation.md)。
|
||||
|
||||
000008_no_foreign_keys 移除历史 70 个外键,保留索引、唯一约束、CHECK、触发器和数据。旧迁移已在回归库应用,按不可变校验和约定保留原文件;新装按顺序运行至当前版本,最终结构同样为零外键。后续迁移禁止再引入外键。升级旧库时先停用旧服务,应用完整迁移再启动新版;迁移需要 DDL 锁,应在目标部署环境演练维护窗口。
|
||||
000008_no_foreign_keys 移除历史 70 个外键,保留索引、唯一约束、CHECK、触发器和数据。旧迁移已在回归库应用,按不可变校验和约定保留原文件;已有库继续按历史链前向升级。新装库由迁移器使用当前版本的无外键基线直接建表,再登记 1–28 的不可变校验和,因此初始化过程也不会先创建历史外键;基线版本变化时必须随新迁移重新生成并回归。后续迁移禁止再引入外键。升级旧库时先停用旧服务,应用完整迁移再启动新版;迁移需要 DDL 锁,应在目标部署环境演练维护窗口。
|
||||
|
||||
000009_queue_resources 增加 Queue.MaxUnfinishedTasks、UnfinishedTasks、StartWindowUntil、StartsInWindow,Go 字段与 SQL COMMENT 均有中文语义,实体仍为 32。未终结任务按 WAITING/LEASED/RUNNING/NEEDS_REVIEW 回填;窗口初始为空、计数为零。运行计数与任务/开始授权同事务维护,不改变队列配置 revision,配置变更不能重置它们。降额允许已有使用量暂时超过新上限,不删除任务或撤销已有许可;详见 [队列契约](api/queues.md) 和 [资源回归](reports/phase-3-queue-resources.md)。
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ P4/P5定时等待与后续查询轮次的实现及回归记录见[时钟报告](
|
||||
|
||||
### P6 首批实施顺序
|
||||
|
||||
1. 已补[首批可复现故障工具](reports/phase-6-fault-boundaries.md):A04“派发已提交但消息尚未送达”、W01/W13事务提交结果不明,以及W16最终回执/旧PENDING结果重放/下一轮竞争。每个切点必须有实际数据库或进程证据,不能用响应错误推断事务是否已提交,也不能用普通SIGNAL竞争替代最终查询场景。
|
||||
2. 已实现[基础运行观测](api/observability.md)及[有界业务状态与事件采样](reports/phase-6-business-observability.md):HTTP/WS、数据库连接池、后台批次、固定任务/交付/流程/Inbox状态计数、最近事件类别、终态耗时样本、固定事件阶段耗时、最早年龄、截断/新鲜度指标和可离线验证的告警规则;独立采集监听默认关闭,固定指标目录拒绝动态标签;发布前只读结构核验由[`orbit-admin verify`](api/operations.md)提供。仍需端到端外部效果延迟、全局持久吞吐、Inbox从事件产生到推进完成的精确阶段延迟、全局发送队列、多节点聚合及生产采集/通知验证。
|
||||
1. 已补[首批可复现故障工具](reports/phase-6-fault-boundaries.md):A04“派发已提交但消息尚未送达”、W01/W13事务提交结果不明,以及W16最终回执/旧PENDING结果重放/下一轮竞争。迁移器同时提供当前版本无外键新装基线;每个切点必须有实际数据库或进程证据,不能用响应错误推断事务是否已提交,也不能用普通SIGNAL竞争替代最终查询场景。
|
||||
2. 已实现[基础运行观测](api/observability.md)及[有界业务状态与事件采样](reports/phase-6-business-observability.md):HTTP/WS、数据库连接池、后台批次、固定任务/交付/流程/Inbox状态计数、最近事件类别、终态耗时样本、固定事件阶段耗时、最早年龄、截断/新鲜度指标和可离线验证的告警规则;独立采集监听默认关闭,固定指标目录拒绝动态标签;发布前只读结构核验由[`orbit-admin verify`](api/operations.md)提供,运维人员也可用 `orbit-admin observe snapshot` 获取同一有界单节点快照。仍需端到端外部效果延迟、全局持久吞吐、Inbox从事件产生到推进完成的精确阶段延迟、全局发送队列、多节点聚合及生产采集/通知验证。
|
||||
3. 已补齐受保护管理环境中的固定凭据操作文件、轮换、撤销、幂等回执和脱敏管理审计;轮换保留授权绑定并要求新令牌重新建立会话,旧 Worker 会话被排空且立即过期,撤销允许在停用范围内执行且不改写历史记录。已用一次性 MySQL 定向验证旧令牌失效、新令牌重绑和零外键;仍需补齐提交不明故障切点、多节点安全重绑演练、凭据最小权限配置核对和生产密钥托管/审计演练。
|
||||
4. 已补[`orbit-admin retention inventory`](api/retention.md)只读盘点和[阶段报告](reports/phase-6-retention-inventory.md),能区分过期幂等、确定终态、未知效果、未终结流程、已确认/未确认交付、已消费/未消费Inbox、事件和审计规模;它不执行归档或删除。后续仍需评审各类保留周期、归档格式、受保护记录核对,再执行备份恢复、兼容升级和六类容量负载。未确认交付、有效幂等身份、当前/历史执行证据和未终结流程不能被普通清理破坏。生产HA及RPO/RTO继续按实际拓扑验证,不缩减为本机多进程检查。
|
||||
5. 已补[`orbit-load`](api/load.md)有界受理负载工具:固定请求形状、并发上限、每请求超时、无自动重试、延迟百分位和有限错误分类;已完成一次隔离 MySQL + HTTP 小样本实测,证据见[负载报告](reports/phase-6-load.md)。标准 Go 1.27.1 隔离回归仍需在工具链恢复后复跑;该工具只提供受理层测量,真实 Worker/流程/数据库拓扑容量仍需独立报告。
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
| 任务 `priority` 与节点领取池 | P3 已实现有界优先级加速、应用/队列轮转 | [优先级与公平派发](priority-and-fairness.md) |
|
||||
| 凭据校验、初始化、固定操作文件轮换/撤销与回执重放 CLI | P1/P6 已实现;不提供公网管理路由,测试专用提交不明切点已回归 | [鉴权](authentication.md)、[凭据报告](../reports/phase-6-credentials.md) |
|
||||
| `orbit-admin verify` 发布前结构核验 | 已实现只读迁移、零外键和 000026–000028 固定索引检查 | [运维核验](operations.md) |
|
||||
| `orbit-admin observe snapshot` 只读运行快照 | 已实现固定状态、事件窗口和阶段耗时的有界单节点快照 | [运维核验](operations.md) |
|
||||
| `orbit-admin retention inventory` 保留清单 | 已实现带 UTC 截止时间的只读盘点;不执行归档、删除或权限变更 | [保留清单](retention.md) |
|
||||
| `sdk/go/cmd/orbit-load` 有界受理负载工具 | 已实现固定并发、无自动重试、延迟百分位和错误分类;只用于隔离环境 | [负载工具](load.md) |
|
||||
| `scripts/test-backup-restore.sh` 隔离备份恢复演练 | 已实现一次性 MySQL 逻辑备份、独立恢复、核心计数及零外键核验 | [备份恢复演练](backup-restore.md) |
|
||||
|
||||
@@ -16,3 +16,22 @@ ORBIT_MYSQL_DSN='user:password@tcp(127.0.0.1:3306)/orbit' \
|
||||
`complete=true` 表示当前二进制的迁移账本、Orbit 表零外键,以及 000026–000028 的六个有界观测索引均满足要求。命令不会证明备份可恢复、复制已确认、业务副作用已核对或生产 RPO/RTO 达标;这些仍需隔离环境恢复演练和真实拓扑验收。失败时会先输出不含 DSN/密钥的检查结果,再以非零状态退出。
|
||||
|
||||
发布流程应先停止不兼容的旧写入节点,在隔离环境和目标维护窗口应用 `orbit-migrate up`,随后执行 `orbit-admin verify`;只有输出完整且人工核对备份、权限、连接路由后才启动新版本。verify 不是运行时协调器,多个节点可并发读取,但不能替代一次性迁移锁。
|
||||
|
||||
## 2. 只读运行快照
|
||||
|
||||
需要在没有开启指标监听时查看平台积压趋势,可执行:
|
||||
|
||||
```sh
|
||||
ORBIT_MYSQL_DSN='user:password@tcp(127.0.0.1:3306)/orbit' \
|
||||
go run ./cmd/orbit-admin observe snapshot
|
||||
```
|
||||
|
||||
命令只执行固定索引上的只读查询,输出 `snapshot`、`sample_limit`、`event_window_seconds` 和 `scope`。任务、交付、流程、Inbox按固定状态分别采样,每个状态最多10000条;事件和阶段耗时只看最近5分钟。达到上限时 `snapshot.truncated=true`,计数只能解释为本节点有界样本,不能作为全局精确数量。命令不接受租户、状态或表名参数,也不执行修复、归档、删除和权限变更。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{"snapshot":{"task_states":{"WAITING":0,"LEASED":0,"RUNNING":0,"NEEDS_REVIEW":0,"SUCCEEDED":0,"FAILED":0,"CANCELLED":0},"task_oldest_age_seconds":0,"delivery_states":{"PENDING":0,"LEASED":0,"ACKED":0,"EXPIRED":0},"delivery_oldest_age_seconds":0,"workflow_states":{"RUNNING":0,"WAITING":0,"CANCELLING":0,"NEEDS_REVIEW":0,"SUCCEEDED":0,"FAILED":0,"CANCELLED":0},"workflow_oldest_age_seconds":0,"inbox_states":{"PENDING":0,"LEASED":0,"PROCESSED":0,"QUARANTINED":0},"inbox_oldest_age_seconds":0,"truncated":false,"recent_events":{},"task_terminal_latency_seconds":0,"task_terminal_latency_max_seconds":0,"workflow_terminal_latency_seconds":0,"workflow_terminal_latency_max_seconds":0,"task_accept_to_start_latency_seconds":0,"task_accept_to_start_latency_max_seconds":0,"task_accept_to_terminal_latency_seconds":0,"task_accept_to_terminal_latency_max_seconds":0,"workflow_start_to_terminal_latency_seconds":0,"workflow_start_to_terminal_latency_max_seconds":0},"sample_limit":10000,"event_window_seconds":300,"scope":"single database node; bounded sample, not a global exact count"}
|
||||
```
|
||||
|
||||
它与 `/metrics` 共享同一有界读取实现,但不会改变指标注册器或业务状态;遇到数据库超时会返回非零状态,调用方应保留上一次成功快照并排查数据库。
|
||||
|
||||
24
docs/reports/phase-6-fresh-install.md
Normal file
24
docs/reports/phase-6-fresh-install.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# P6 无外键新装基线验证
|
||||
|
||||
验证日期:2026-09-20。此次补齐的是数据库初始化边界:历史迁移文件保持不可变,已有库仍从原账本前向升级;空库由迁移器直接执行当前版本基线,再登记 1–28 的迁移名称和校验和。基线不包含 `FOREIGN KEY`、`REFERENCES`、迁移账本表或固定 `DEFINER`,因此新库初始化不会先创建历史外键。
|
||||
|
||||
## 实现
|
||||
|
||||
- [迁移入口](../../internal/storage/mysql/migrate.go) 在取得数据库级迁移锁后区分空库和已有 Orbit 表;存在业务表但没有账本,或账本为空而已有业务表时拒绝猜测历史。
|
||||
- [无外键基线](../../internal/storage/mysql/baseline.sql) 由当前 v28 最终结构生成,保留表、索引、唯一约束、CHECK、生成列和不可变触发器;不包含外键、引用约束或客户端 `DELIMITER` 脚本。
|
||||
- 基线执行前把所有历史版本写成 `dirty=true`,全部 DDL 成功后统一清除脏标记。中断会阻断后续迁移,必须由运维核对实际结构后处理,不能自动跳过。
|
||||
- [无外键单元检查](../../internal/storage/mysql/no_foreign_keys_test.go) 禁止基线重新出现引用 DDL,并要求基线版本和迁移末版本一致。
|
||||
|
||||
## 实际验证
|
||||
|
||||
| 场景 | 结果 | 证据 |
|
||||
| --- | --- | --- |
|
||||
| 空 MySQL 8.4 数据库执行 `orbit-migrate up` | PASS | `TestIntegrationFreshInstallUsesNoForeignKeys`,迁移账本 28 条、`information_schema.REFERENTIAL_CONSTRAINTS` 为 0、`CheckSchema` 通过 |
|
||||
| v7 历史库升级到当前版本 | PASS | `TestIntegrationNoForeignKeysUpgrade`,历史 70 条外键删除为 0,323 个既有索引组件和任务数据保留,手工外键漂移被拒绝 |
|
||||
| CLI 结构核验 | PASS | `orbit-admin verify` 输出 `schema_version=28`、`migration_count=28`、`foreign_key_count=0`、`complete=true` |
|
||||
|
||||
验证命令使用 Docker `golang:1.25` 临时工具链和 MySQL 8.4 一次性容器;不是 Go 1.27.1 标准全量回归,也不代表复制、切换、RPO/RTO 或生产拓扑验收。
|
||||
|
||||
## 运维边界
|
||||
|
||||
升级旧库仍必须停用不兼容写节点,在维护窗口执行正式迁移并随后运行 `orbit-admin verify`。新装基线只解决初始化阶段的外键约束问题,不会替代应用事务中的命名空间、资源归属和权限校验;数据库关联字段仍由服务端显式核对。
|
||||
Reference in New Issue
Block a user