matrixorigin / matrixorigin/matrixone
[Bug]: Concurrent COPY ALTER serializes on the global SNAPSHOT gate and times out after lineage protection added in #25882
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 311
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 768
Description
### Is there an existing issue for the same bug?
- [x] 已检索开放和关闭的 SNAPSHOT / ALTER / timeout / global-lock 问题。
与 #28317 相关但应分开跟踪:#28317 是 View/SNAPSHOT 反向锁顺序导致死锁;本 Issue 是不同表的 COPY ALTER 在同一个 SNAPSHOT 全局锁上串行排队,造成高延迟和客户端超时。修复锁顺序不一定消除串行化瓶颈。
### Branch Name
main;当前 4.2-dev 也包含该全局锁路径。
### Commit ID
- 本次 Main nightly:`9ae27d218dc952571ec2ce2b5575e2e61e844279`。
- 核对 4.2-dev 代码:`c34850aa0a2b75892ce01b90a3578b20d76d34a4`,确认包含该机制,**未将源码存在等同于在该 revision 完成相同负载复现**。
- COPY ALTER 全局 SNAPSHOT 写锁机制引入 commit:`2ddf1d4c37c64e9b074aee9cab27a3234a667bc5`,PR #25882。
### Other Environment Information
- 原 Main 定时回归(并非新 Family PR 专属):https://github.com/matrixorigin/mo-nightly-regression/actions/runs/34041258710/job/101614397045
- Case:并发 `alter_add_pk` / `alter_drop_pk`,涉及 `table_1` 等不同业务表。
- Namespace:`mo-main-commit-9ae27d218-20260906`。
- 问题窗口:2026-09-07 04:28–04:35 UTC 左右;客户端错误从 04:32:54 UTC 开始密集出现。
- 这里讨论 COPY ALTER 的目录锁竞争,不是大规模 UPDATE 主键的数据锁粗化问题。
### Actual Behavior
并发 ADD/DROP PRIMARY KEY 的多个请求在系统租户同一条 SNAPSHOT feature-registry 记录上排队。一个 ALTER 等待全局锁时仍占着自己的表目录锁,访问该表的其他 ALTER 再排在它后面,形成全局队列及逐表的队首阻塞。
现场对比汇总如下(来自本次问题提交前的两日分析;不是 #25882 合入前后的受控 A/B 性能实验):
| 指标 | 前一日 | 本次 |
|---|---:|---:|
| ADD 失败 | 0 | 21 |
| DROP 失败 | 0 | 57 |
| ADD 平均耗时 | 10.51 秒 | 31.35 秒 |
| DROP 平均耗时 | 10.94 秒 | 35.03 秒 |
| 最大耗时 | 约 15 秒 | 约 100 秒 |
上述分析报告两日测试代码一致,预期的 MySQL 1068/1091 已被测试接受,不计入这些失败。当前保留的 GitHub 日志确认实际 unexpected 错误是:
```text
2026-09-07 04:32:54
TxnName : alter_add_pk
Statement : alter table table_98 add primary key(col1);
ErrorCode : 0
ErrorMessage : Communications link failure
Expected : false
2026-09-07 04:33:01
TxnName : alter_drop_pk
Statement : alter table table_49 drop primary key;
ErrorCode : 0
ErrorMessage : Communications link failure
Expected : false
```
不能单凭 `Communications link failure` 判网络故障;需要与服务端等待关联。本次现场提供的完整请求追踪为:
1. `table_71` 的 ALTER 持有本表 catalog 锁,等待全局 SNAPSHOT 行锁。
2. 其他访问 `table_71` 的 ALTER 在它后面等待。
3. 接近 100 秒才获得全局锁,后续实际 CREATE/INSERT/DROP 仅数十毫秒。
4. 客户端已经超时断开,服务端随后观察到 `context canceled`。
5. 上游两个 holder 分别持全局锁约 12.2 秒、13 秒;对应 TN commit 约 5.8ms、6.5ms。
**证据边界:**上面 holder/table_71 的详细计时来自提交者提供的现场追踪结论,本次发单未重新采集其原始锁时间线。短 commit 时长只排除这两个请求的提交阶段是主要耗时,不能据此排除它们整个执行过程中的 TN/存储/其他等待。仍需把 holder 的 12–13 秒按阶段拆开。
### 引入 PR 与代码依据
#### #25882 引入了本 Issue 所指的全局串行化机制
PR:https://github.com/matrixorigin/matrixone/pull/25882
- 标题:`feat(frontend): support schema evolution in data branch diff/merge`。
- 合并时间:2026-07-29 17:04:40 UTC(北京时间 7 月 30 日)。
- Merge SHA:`2ddf1d4c37c64e9b074aee9cab27a3234a667bc5`。
该 PR 新建 [lineage_publication_lock.go](https://github.com/matrixorigin/matrixone/blob/2ddf1d4c37c64e9b074aee9cab27a3234a667bc5/pkg/frontend/databranchutils/lineage_publication_lock.go),引入:
```sql
update mo_catalog.mo_feature_registry
set scope_spec = scope_spec, updated_at = updated_at
where feature_code = 'SNAPSHOT';
```
并在 [COPY ALTER](https://github.com/matrixorigin/matrixone/blob/2ddf1d4c37c64e9b074aee9cab27a3234a667bc5/pkg/sql/compile/alter.go#L1138) 中调用:
```go
if err = c.lockDataBranchLineageOwnerPublication(); err != nil {
return err
}
lineagePlan, err = c.prepareAlterDataBranchLineage(oldId, dbName, tblName)
```
关键点:**先获取全局锁,之后才判断该表的 lineage/history 参与情况**。因此普通不同表的 COPY ALTER 也会进入同一系统行的竞争;这不是每张表各自独立的一把锁。该写入使用 ALTER 的事务,不能在内部 SQL 返回后简单认为已经释放。
父版本没有该 COPY ALTER 调用;核对 `v4.1.4` 也未发现该路径。这证明机制不是一直存在,但不证明旧版本没有其他 DDL 瓶颈。
#### #27826 是后续扩展,不是 COPY ALTER 首次引入点
https://github.com/matrixorigin/matrixone/pull/27826
Main commit `f91ce0647f5e1d8d9dcfb38a4d87063c739f074c`(2026-08-30)将 Publication 命名改为 Lifecycle,并统一/扩大至 restore、GC、普通 DROP 等入口。COPY ALTER 在此之前已经获取该锁,不应把最初引入错误归到 #27826。
当前 [4.2-dev COPY ALTER](https://github.com/matrixorigin/matrixone/blob/c34850aa0a2b75892ce01b90a3578b20d76d34a4/pkg/sql/compile/alter.go#L1156) 仍包含 Publication 版本的获取路径。因此不是 Main 独有,也不应表述为已在所有 4.2 发行版本完成复现。
### 为什么有这把锁,以及缺陷边界
这把锁承担真实正确性职责:协调 Snapshot/PITR owner 发布与 ALTER 的历史引用/lineage 决策,避免检查历史引用之后又被并发发布打破判断。
因此修复不能直接删锁,也不能未经完整协议分析就改成仅按业务表锁。问题在于全局保护覆盖完整 DDL 工作期间,可能使不同表的合法操作因共享目录保护发生严重串行排队。
已确认:新增全局串行化路径、当前实际超时失败、现场分析将请求主要等待关联到该锁。
尚未完成:#25882 前后版本在相同机器/负载/数据下的性能 A/B;holder 全阶段耗时分解;两日差异是否由其他代码/环境进一步放大。**#25882 是机制引入点,不等于已经证明它单独解释本次全部性能下降。**
### Expected Behavior
不同表的合法 COPY ALTER 在并发下应有合理的完成能力,不应由于过宽的全局目录保护大面积等待到客户端超时。必要的历史引用一致性必须保留,但应评估更短的安全临界区或更精确的协调方式。
预期的 ADD/DROP 主键冲突可以按测试既有合同接受;将客户端超时、context canceled 也一律当作 PASS 不合理。单纯增大客户端超时或降低 Runner 并发不能证明性能问题解决。
### Steps to Reproduce / 后续验证
1. 使用上述 Main nightly 的相同 mo-load 工具 revision、ADD/DROP PK Case、并发参数及数据规模复跑,保存完整参数;不要把 #28317 的三行 DDL 死锁脚本当作本性能问题的复现。
2. 对不同表并发执行 ADD/DROP PRIMARY KEY,分别统计 1068/1091 和非预期错误、吞吐、P50/P95/max、客户端超时。
3. 关联等待事务、SNAPSHOT lock holder、本表 catalog waiters,拆分 acquire wait、持锁工作、copy SQL、commit 和 cancellation。
4. 增加独立表单 worker 控制组与多个 worker 组;对比 #25882 父版本和引入版本,使用分别初始化的独立数据目录。
5. 保留 Snapshot/PITR publication 与 ALTER 的正确性并发测试,避免性能优化破坏历史数据引用安全。
6. 当前 Main 的 #27826 扩展和后续 View gate 可能放大等待,应分别评估,避免将多个机制混在同一个因果结论里。
### Additional information
按性能/可用性回归问题登记,暂不宣称数据损坏或无条件 100% 复现。与 #28317 分开跟踪;#28079 是另一个 View gate 引起的账号生命周期队列问题;#27825 是 restore/GC 死锁,均不直接覆盖本报告的 COPY ALTER 吞吐与超时问题。
Contributor guide
Assessment
This issue has not been assessed yet.