@chenguangyao/devflow-kit 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +232 -0
- package/LICENSE +21 -0
- package/README.md +539 -0
- package/bin/devflow.js +9 -0
- package/docs/RFC-001-devflow-kit.md +617 -0
- package/docs/RFC-002-workflow-kernel.md +134 -0
- package/docs/enterprise-integration-supplement.md +274 -0
- package/docs/internal-gitlab-setup.md +426 -0
- package/docs/marketplace-skills.md +231 -0
- package/docs/migration-from-arb.md +232 -0
- package/docs/tooling-overview.md +774 -0
- package/docs/workflow-orchestration.md +695 -0
- package/docs/workflow-ui-prototype.html +271 -0
- package/package.json +52 -0
- package/schemas/config.schema.json +51 -0
- package/schemas/delta.schema.json +22 -0
- package/schemas/state.schema.json +130 -0
- package/schemas/status-surface.schema.json +197 -0
- package/schemas/workflow-confirmation-surface.schema.json +70 -0
- package/schemas/workflow-picker.schema.json +94 -0
- package/scripts/postinstall.js +101 -0
- package/scripts/render-workflow-ui-prototype.js +271 -0
- package/skills/apply/SKILL.md +313 -0
- package/skills/apply/references/discipline-checklist.md +145 -0
- package/skills/apply/references/subagent-implementer-prompt.md +113 -0
- package/skills/apply/references/subagent-orchestration.md +150 -0
- package/skills/apply/references/subagent-reviewer-prompt.md +180 -0
- package/skills/apply/references/tdd-loop.md +287 -0
- package/skills/apply/references/when-plan-is-wrong.md +279 -0
- package/skills/apply/references/worktree-swarm.md +292 -0
- package/skills/archive/SKILL.md +229 -0
- package/skills/archive/references/conflict-resolution.md +336 -0
- package/skills/archive/references/knowledge-deposit.md +381 -0
- package/skills/archive/references/spec-merge.md +365 -0
- package/skills/brainstorm/SKILL.md +123 -0
- package/skills/brainstorm/references/proposal-template.md +244 -0
- package/skills/brainstorm/references/question-catalog.md +168 -0
- package/skills/brainstorm/references/session-template.md +184 -0
- package/skills/ci-fix/SKILL.md +63 -0
- package/skills/ci-fix/references/loop.md +25 -0
- package/skills/code-review/SKILL.md +279 -0
- package/skills/code-review/references/escalation-playbook.md +192 -0
- package/skills/code-review/references/language-cheatsheets/go.md +175 -0
- package/skills/code-review/references/language-cheatsheets/java-spring-mybatis.md +246 -0
- package/skills/code-review/references/language-cheatsheets/python.md +170 -0
- package/skills/code-review/references/language-cheatsheets/vue.md +199 -0
- package/skills/code-review/references/output-template.md +275 -0
- package/skills/code-review/references/review-checklist.md +251 -0
- package/skills/complexity-grading/SKILL.md +259 -0
- package/skills/deliver/SKILL.md +271 -0
- package/skills/deliver/references/delivery-modes.md +299 -0
- package/skills/deliver/references/notify.md +359 -0
- package/skills/deliver/references/pr-description.md +319 -0
- package/skills/dependency-upgrade/SKILL.md +57 -0
- package/skills/dependency-upgrade/references/risk-matrix.md +38 -0
- package/skills/df-orchestrator/SKILL.md +407 -0
- package/skills/df-orchestrator/references/complexity-grading.md +177 -0
- package/skills/df-orchestrator/references/escalation-matrix.md +191 -0
- package/skills/df-orchestrator/references/routing-rules.md +290 -0
- package/skills/df-orchestrator/references/workflow-state-machine.md +208 -0
- package/skills/frontend-quality/SKILL.md +61 -0
- package/skills/frontend-quality/references/checklist.md +35 -0
- package/skills/handoff-resume/SKILL.md +59 -0
- package/skills/handoff-resume/references/handoff-template.md +54 -0
- package/skills/plan/SKILL.md +166 -0
- package/skills/plan/references/task-breakdown.md +207 -0
- package/skills/plan/references/task-sequencing.md +143 -0
- package/skills/plan/references/task-template.md +248 -0
- package/skills/requirement-analysis/SKILL.md +499 -0
- package/skills/requirement-analysis/references/acceptance-criteria.md +183 -0
- package/skills/requirement-analysis/references/code-recon.md +151 -0
- package/skills/requirement-analysis/references/edge-case-catalog.md +164 -0
- package/skills/requirement-analysis/references/requirement-template.md +339 -0
- package/skills/requirement-analysis/references/scope-negotiation.md +162 -0
- package/skills/security-hardening/SKILL.md +60 -0
- package/skills/security-hardening/references/checklist.md +42 -0
- package/skills/tech-spec/SKILL.md +388 -0
- package/skills/tech-spec/references/api-contract-design.md +172 -0
- package/skills/tech-spec/references/decision-records.md +110 -0
- package/skills/tech-spec/references/design-template.md +301 -0
- package/skills/tech-spec/references/rollout-and-rollback.md +203 -0
- package/skills/tech-spec/references/spec-delta-conventions.md +250 -0
- package/skills/tech-spec/references/transaction-patterns.md +212 -0
- package/skills/test-spec/SKILL.md +219 -0
- package/skills/test-spec/references/coverage-strategy.md +218 -0
- package/skills/test-spec/references/edge-case-to-test.md +143 -0
- package/skills/test-spec/references/test-case-template.md +276 -0
- package/skills/verify/SKILL.md +232 -0
- package/skills/verify/references/nfr-verification.md +292 -0
- package/skills/verify/references/report-templates.md +510 -0
- package/skills/verify/references/self-test-guide.md +240 -0
- package/skills/verify/references/verify-rollback-map.md +247 -0
- package/src/cli/commands/_helpers.js +108 -0
- package/src/cli/commands/_submit.js +718 -0
- package/src/cli/commands/apply.js +198 -0
- package/src/cli/commands/archive.js +180 -0
- package/src/cli/commands/checkpoint.js +113 -0
- package/src/cli/commands/deliver.js +377 -0
- package/src/cli/commands/deploy.js +504 -0
- package/src/cli/commands/design.js +158 -0
- package/src/cli/commands/disable.js +21 -0
- package/src/cli/commands/doctor.js +178 -0
- package/src/cli/commands/enable.js +21 -0
- package/src/cli/commands/flow.js +645 -0
- package/src/cli/commands/help.js +93 -0
- package/src/cli/commands/ingest.js +602 -0
- package/src/cli/commands/init.js +341 -0
- package/src/cli/commands/knowledge.js +523 -0
- package/src/cli/commands/logs.js +43 -0
- package/src/cli/commands/new.js +202 -0
- package/src/cli/commands/plan.js +49 -0
- package/src/cli/commands/propose.js +27 -0
- package/src/cli/commands/provider.js +698 -0
- package/src/cli/commands/report.js +143 -0
- package/src/cli/commands/requirement.js +227 -0
- package/src/cli/commands/review.js +301 -0
- package/src/cli/commands/skills.js +457 -0
- package/src/cli/commands/status.js +925 -0
- package/src/cli/commands/switch.js +27 -0
- package/src/cli/commands/sync.js +47 -0
- package/src/cli/commands/test.js +366 -0
- package/src/cli/commands/uninstall.js +32 -0
- package/src/cli/commands/update.js +74 -0
- package/src/cli/commands/verify.js +354 -0
- package/src/cli/commands/worktree.js +78 -0
- package/src/cli/index.js +72 -0
- package/src/cli/parse-args.js +102 -0
- package/src/core/autodetect.js +271 -0
- package/src/core/change.js +208 -0
- package/src/core/checkpoint.js +217 -0
- package/src/core/config.js +60 -0
- package/src/core/delta.js +290 -0
- package/src/core/markers.js +59 -0
- package/src/core/paths.js +173 -0
- package/src/core/plan-tasks.js +36 -0
- package/src/core/project-routing.js +285 -0
- package/src/core/projects.js +200 -0
- package/src/core/state.js +200 -0
- package/src/core/workflow-check.js +177 -0
- package/src/core/workflow-init.js +34 -0
- package/src/core/workflow-picker.js +154 -0
- package/src/core/workflow-policy.js +119 -0
- package/src/core/workflow-suggest.js +181 -0
- package/src/core/workflow-verify.js +88 -0
- package/src/core/workflow.js +433 -0
- package/src/core/worktree.js +241 -0
- package/src/knowledge/categories.js +107 -0
- package/src/knowledge/classify.js +125 -0
- package/src/knowledge/deposit.js +414 -0
- package/src/knowledge/migrate.js +149 -0
- package/src/knowledge/mr.js +219 -0
- package/src/knowledge/query.js +131 -0
- package/src/knowledge/registry.js +151 -0
- package/src/knowledge/sync.js +179 -0
- package/src/providers/base.js +74 -0
- package/src/providers/drivers/api-yapi.js +78 -0
- package/src/providers/drivers/ci-jenkins.js +109 -0
- package/src/providers/drivers/intake-confluence.js +544 -0
- package/src/providers/drivers/kb-git.js +549 -0
- package/src/providers/drivers/kb-weknora.js +472 -0
- package/src/providers/drivers/notify-smtp.js +515 -0
- package/src/providers/drivers/observability-oss.js +43 -0
- package/src/providers/drivers/observability-sls.js +50 -0
- package/src/providers/lifecycle.js +135 -0
- package/src/providers/loader.js +132 -0
- package/src/providers/local.js +190 -0
- package/src/providers/userconfig.js +283 -0
- package/src/reports/aggregate.js +185 -0
- package/src/reports/coverage.js +163 -0
- package/src/reports/detect.js +143 -0
- package/src/reports/parse.js +236 -0
- package/src/templates/files/ci/github.yml +38 -0
- package/src/templates/files/ci/gitlab.yml +27 -0
- package/src/templates/files/design.md +63 -0
- package/src/templates/files/ide/devflow-workflow.md +58 -0
- package/src/templates/files/ide/project-overview-reference.md +1 -0
- package/src/templates/files/ide/project-overview.md +27 -0
- package/src/templates/files/knowledge-index.json +17 -0
- package/src/templates/files/knowledge.md +28 -0
- package/src/templates/files/meta.json +8 -0
- package/src/templates/files/plan.md +38 -0
- package/src/templates/files/proposal.md +33 -0
- package/src/templates/files/reports/contract-test.md +40 -0
- package/src/templates/files/reports/e2e-test.md +30 -0
- package/src/templates/files/reports/integration-test.md +36 -0
- package/src/templates/files/reports/joint-test.md +58 -0
- package/src/templates/files/reports/perf.md +24 -0
- package/src/templates/files/reports/regression.md +20 -0
- package/src/templates/files/reports/remote-test.md +55 -0
- package/src/templates/files/reports/self-test.md +43 -0
- package/src/templates/files/reports/smoke-test.md +22 -0
- package/src/templates/files/reports/unit-test.md +36 -0
- package/src/templates/files/requirement.md +51 -0
- package/src/templates/files/review.md +38 -0
- package/src/templates/files/tests.md +36 -0
- package/src/templates/files/verify.md +32 -0
- package/src/templates/index.js +21 -0
- package/src/utils/log.js +37 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# tech-spec / rollout-and-rollback
|
|
2
|
+
|
|
3
|
+
灰度 / 回滚 / 双写的模板与决策树。design.md §10 的写作依据。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 核心原则
|
|
8
|
+
|
|
9
|
+
1. **不能回滚 = 不能上线**。没有回滚预案的设计不允许进入 plan。
|
|
10
|
+
2. **灰度要有停止条件**,不是"先 1% 再说"。
|
|
11
|
+
3. **数据迁移要有对账**,不依赖"应该没问题"。
|
|
12
|
+
4. **上线前演练过回滚**(至少在 staging 演练一次)才算真的能回滚。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 灰度策略选择
|
|
17
|
+
|
|
18
|
+
| 方式 | 粒度 | 典型场景 |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| **Feature flag**(按配置开关) | 布尔全局 / 按百分比 / 按标签 | 接口新逻辑、UI 新功能 |
|
|
21
|
+
| **按用户 hash 分流** | 按 userId % N | 新旧路径对比实验、A/B test |
|
|
22
|
+
| **按机房 / 可用区分流** | 整机房 | 基础设施 / 中间件替换 |
|
|
23
|
+
| **按版本分流** | 客户端版本号 | 客户端兼容切换 |
|
|
24
|
+
| **按环境灰度** | dev → staging → prod 前哨 → prod 全量 | 全新功能 |
|
|
25
|
+
|
|
26
|
+
选哪种决定于**变更的粒度 + 回滚速度需求**:
|
|
27
|
+
|
|
28
|
+
- 秒级回滚 → Feature flag(改配置即可)
|
|
29
|
+
- 分钟级回滚 → 按百分比分流(改分流比例)
|
|
30
|
+
- 小时级回滚 → 灰度机房切换(运维介入)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Feature flag 模板
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
# Apollo / config center
|
|
38
|
+
feature.coupon.batch.enabled: false # 默认关
|
|
39
|
+
feature.coupon.batch.whitelist: # 白名单灰度
|
|
40
|
+
- operator_id_001
|
|
41
|
+
- operator_id_002
|
|
42
|
+
feature.coupon.batch.percent: 0 # 百分比灰度(0-100)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**代码里**:
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
if (!flagService.isEnabled("coupon.batch.enabled", operatorId)) {
|
|
49
|
+
throw new ServiceUnavailableException("SERVICE_DISABLED");
|
|
50
|
+
}
|
|
51
|
+
// 新逻辑
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**关键**:
|
|
55
|
+
|
|
56
|
+
- flag 读取要**缓存**(不要每请求读远程)
|
|
57
|
+
- flag 变更**立即生效**(≤ 30 秒)
|
|
58
|
+
- flag 有**默认值**(远程不可用时降级到默认)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 灰度阶段模板
|
|
63
|
+
|
|
64
|
+
```markdown
|
|
65
|
+
## 灰度计划
|
|
66
|
+
|
|
67
|
+
### 阶段 0:预发(staging)
|
|
68
|
+
- 在预发跑 smoke + e2e,内部 QA 验收
|
|
69
|
+
- 条件:通过 → 进入阶段 1
|
|
70
|
+
|
|
71
|
+
### 阶段 1:内部白名单(5 账号)
|
|
72
|
+
- flag: `whitelist: [opr001, opr002, opr003, opr004, opr005]`
|
|
73
|
+
- 观测:5 个账号的**使用反馈** + **指标**(error_rate, P99)
|
|
74
|
+
- 时长:≥ 24h(至少覆盖一个工作日高峰)
|
|
75
|
+
- 条件:无阻塞性问题 → 阶段 2
|
|
76
|
+
|
|
77
|
+
### 阶段 2:1% 灰度
|
|
78
|
+
- flag: `percent: 1`
|
|
79
|
+
- 观测:error_rate < 0.1%,P99 < 3s,对照组无退化
|
|
80
|
+
- 时长:≥ 2h
|
|
81
|
+
- 条件:通过 → 阶段 3
|
|
82
|
+
|
|
83
|
+
### 阶段 3:10% 灰度
|
|
84
|
+
- flag: `percent: 10`
|
|
85
|
+
- 观测:同上
|
|
86
|
+
- 时长:≥ 4h,涵盖一次发券峰值
|
|
87
|
+
- 条件:通过 → 阶段 4
|
|
88
|
+
|
|
89
|
+
### 阶段 4:50% 灰度
|
|
90
|
+
- flag: `percent: 50`
|
|
91
|
+
- 观测:同上
|
|
92
|
+
- 时长:≥ 12h
|
|
93
|
+
- 条件:通过 → 全量
|
|
94
|
+
|
|
95
|
+
### 阶段 5:100% 全量
|
|
96
|
+
- flag: `percent: 100`
|
|
97
|
+
- 观测:同上
|
|
98
|
+
- 保持 flag 1 周不移除,方便紧急回滚
|
|
99
|
+
|
|
100
|
+
### 清理(灰度完成后 1 周)
|
|
101
|
+
- 删除 flag 分支代码 + 移除 flag 配置
|
|
102
|
+
- 清理检查清单见:<清理任务列表>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 回滚预案(必写)
|
|
108
|
+
|
|
109
|
+
每个设计至少要写:
|
|
110
|
+
|
|
111
|
+
1. **触发条件**:什么情况下触发回滚
|
|
112
|
+
2. **回滚步骤**:具体命令 / 按钮 / 操作
|
|
113
|
+
3. **回滚影响**:回滚后的用户看到什么、数据状态什么样
|
|
114
|
+
4. **回滚耗时**:从触发到生效多久
|
|
115
|
+
|
|
116
|
+
### 模板
|
|
117
|
+
|
|
118
|
+
```markdown
|
|
119
|
+
## 回滚预案
|
|
120
|
+
|
|
121
|
+
### 触发条件(任一命中)
|
|
122
|
+
- error_rate > 1% 持续 5min
|
|
123
|
+
- P99 > 5s 持续 10min
|
|
124
|
+
- 收到运营侧 P0 反馈(影响 > 5 个业务场景)
|
|
125
|
+
|
|
126
|
+
### 回滚步骤(按严重度从轻到重)
|
|
127
|
+
#### 软回滚(改配置)
|
|
128
|
+
1. Apollo 设置 `coupon.batch.enabled=false`
|
|
129
|
+
2. 等待 30 秒生效
|
|
130
|
+
3. 验证:新请求返回 503 SERVICE_DISABLED
|
|
131
|
+
耗时:1 分钟
|
|
132
|
+
|
|
133
|
+
#### 硬回滚(切回旧版本 tag)
|
|
134
|
+
1. Jenkins 触发回滚作业 `coupon-service-rollback` 指定上一 tag
|
|
135
|
+
2. 观察 5 分钟确认服务 healthy
|
|
136
|
+
耗时:10 分钟
|
|
137
|
+
|
|
138
|
+
### 回滚影响
|
|
139
|
+
- 用户看到:批量接口返回 503,提示"暂不可用,请稍后";前端提示降级到单发操作
|
|
140
|
+
- 数据:已发放的券不回收;正在处理中的批次(极少,因为是同步)可能写了部分 grant_log,但不影响兼容(batch_id 列可为 NULL)
|
|
141
|
+
- 指标:回滚后 `coupon.batch.traffic` 掉到 0
|
|
142
|
+
|
|
143
|
+
### 回滚验证
|
|
144
|
+
- 回滚后 30 分钟内,对照旧单发接口的基线指标,所有指标应回归基线 ±5%
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 双写 / 数据迁移模板
|
|
150
|
+
|
|
151
|
+
### 场景:从旧表 old_coupon 迁到新表 coupon
|
|
152
|
+
|
|
153
|
+
**不要**:一次性 stop-the-world 迁移。
|
|
154
|
+
|
|
155
|
+
**要**:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
阶段 0:新表建好 + 新代码只写新表(开关 write_new=true)
|
|
159
|
+
阶段 1:新表 + 旧表双写(write_both=true);读仍旧表
|
|
160
|
+
阶段 2:跑历史数据回填(backfill job),把旧表数据同步到新表
|
|
161
|
+
阶段 3:对账 job 跑 24-72 小时,确认 diff_count ≤ 阈值
|
|
162
|
+
阶段 4:读切新表(read_new=true);旧表仍双写(防紧急回滚)
|
|
163
|
+
阶段 5:观察 ≥ 1 周 → 停旧表写入(write_old=false)
|
|
164
|
+
阶段 6:归档旧表(不删,至少保留 1 个月)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**关键**:
|
|
168
|
+
|
|
169
|
+
- 每个阶段的**切换动作**是可秒级回滚的(改 flag)
|
|
170
|
+
- 对账是**必须的**,不是 "看起来没问题就切"
|
|
171
|
+
- 数据迁移的**验证条件**要量化(count 差异 / 字段 hash 对比)
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 监控面板(灰度期强制)
|
|
176
|
+
|
|
177
|
+
灰度期必有一个**专用 dashboard**:
|
|
178
|
+
|
|
179
|
+
- **流量分布**:新 / 旧路径的 QPS 百分比(应符合 flag 比例)
|
|
180
|
+
- **错误率对照**:新 / 旧路径的 error_rate 对比
|
|
181
|
+
- **延迟对照**:新 / 旧路径的 P50 / P99 对比
|
|
182
|
+
- **业务指标**:发券量、退款量、投诉量(漂移要告警)
|
|
183
|
+
- **日志异常**:新路径的 log.error 数量
|
|
184
|
+
|
|
185
|
+
无对照 dashboard = 灰度形同虚设。
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 回滚演练
|
|
190
|
+
|
|
191
|
+
L3 任务建议上线前在 staging 做一次**完整回滚演练**:
|
|
192
|
+
|
|
193
|
+
1. 在 staging 部署新版本并灰度到 50%
|
|
194
|
+
2. 人为触发异常(如关闭下游 RiskService)
|
|
195
|
+
3. 执行回滚步骤
|
|
196
|
+
4. 验证:指标回归基线、无数据丢失 / 错位
|
|
197
|
+
5. 记录演练报告 → `devflow/changes/<slug>/rollback-drill.md`
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 本文与 SKILL.md 的关系
|
|
202
|
+
|
|
203
|
+
SKILL.md 给了灰度 / 回滚的硬规则("L2/L3 必须有可操作的灰度步骤和回滚预案")。本文给完整模板 + 阶段划分 + 双写流程 + 监控要求 + 演练流程。tech-spec 写 §10 时应以本文为模板。
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# tech-spec / spec-delta-conventions
|
|
2
|
+
|
|
3
|
+
OpenSpec 风格的 spec delta 写作规范。delta 在 archive 阶段会合入 `devflow/specs/<业务域>/spec.md`。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 什么是 OpenSpec Delta
|
|
8
|
+
|
|
9
|
+
OpenSpec delta 是机器可读 + 人类可读的能力变更描述文件。它记录"系统能**做什么**",而不是"代码**怎么写**"。
|
|
10
|
+
|
|
11
|
+
OpenSpec delta 的价值:
|
|
12
|
+
|
|
13
|
+
- `SHALL/MUST/SHOULD` 明确系统承诺,可直接支撑验收和测试
|
|
14
|
+
- `Scenario` 描述关键业务场景,避免 spec 退化成接口字段流水账
|
|
15
|
+
- 按业务域聚合到 `specs/<域>/spec.md`,同域能力持续沉淀到一个真源
|
|
16
|
+
- `REJECTED Alternatives` 记录决策背景,防止未来重蹈覆辙
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 什么时候要写 delta
|
|
21
|
+
|
|
22
|
+
只有在本次改动**改变了系统对外能力**时才写:
|
|
23
|
+
|
|
24
|
+
| 场景 | 是否写 delta |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| 新增一个对外接口 / 新模块 / 新事件 | 是(ADDED) |
|
|
27
|
+
| 改变现有接口的行为语义(字段 / 错误码 / SLO 变了) | 是(MODIFIED) |
|
|
28
|
+
| 废弃 / 删除现有接口 | 是(REMOVED) |
|
|
29
|
+
| 只改实现、不改契约(重构 / 性能优化) | 否 |
|
|
30
|
+
| 改文案 / UI 微调 / 加日志 / 加指标 | 否 |
|
|
31
|
+
| 紧急 bug 修复(恢复本来就应该的行为) | 否 |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 文件位置与目标 spec 路径
|
|
36
|
+
|
|
37
|
+
delta 文件位置:
|
|
38
|
+
```
|
|
39
|
+
devflow/changes/<slug>/delta/
|
|
40
|
+
├── <capability-a>.md ← 顶层文件
|
|
41
|
+
├── added/
|
|
42
|
+
│ └── <capability-b>.md ← 可用子目录分类(不影响落盘路径)
|
|
43
|
+
└── modified/
|
|
44
|
+
└── <capability-c>.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**落盘路径由 frontmatter `capability` 字段决定**(不是文件名):
|
|
48
|
+
|
|
49
|
+
| frontmatter | 落盘目标 |
|
|
50
|
+
|----------------------------------|-----------------------------------|
|
|
51
|
+
| `capability: coupon/grant` | `specs/coupon/spec.md` |
|
|
52
|
+
| `capability: order/create` | `specs/order/spec.md` |
|
|
53
|
+
| `capability: payment/alipay/v2` | `specs/payment/spec.md` |
|
|
54
|
+
| 无 `capability` 字段 | `specs/<文件名>.md`(兜底) |
|
|
55
|
+
|
|
56
|
+
这样不同业务域的 spec 自动分目录存放,不会堆在同一层:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
devflow/specs/
|
|
60
|
+
├── coupon/
|
|
61
|
+
│ └── spec.md
|
|
62
|
+
├── order/
|
|
63
|
+
│ └── spec.md
|
|
64
|
+
└── payment/
|
|
65
|
+
└── spec.md
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
规则:
|
|
69
|
+
- 一个 capability 一个 delta file
|
|
70
|
+
- 一个 change 可产生多个 delta file(涉及多个 capability)
|
|
71
|
+
- delta 子目录(`added/`、`modified/`)仅用于可读性分类,不影响 archive 行为
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## OpenSpec Delta 完整格式
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
---
|
|
79
|
+
slug: <change-slug>
|
|
80
|
+
capability: <capability-name>
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
# delta: ADDED specs/<domain>/spec.md
|
|
84
|
+
|
|
85
|
+
> OpenSpec 风格 delta — `devflow archive` 时按业务域落盘到 `devflow/specs/<domain>/spec.md`。
|
|
86
|
+
|
|
87
|
+
## ADDED Requirements
|
|
88
|
+
|
|
89
|
+
### Requirement: <业务能力名>
|
|
90
|
+
|
|
91
|
+
系统 SHALL <描述该业务能力对外承诺>。
|
|
92
|
+
|
|
93
|
+
1. 系统 MUST <约束 1>
|
|
94
|
+
2. 系统 MUST <约束 2>
|
|
95
|
+
3. 系统 SHOULD <约束 3>
|
|
96
|
+
|
|
97
|
+
#### Scenario: <场景名>
|
|
98
|
+
|
|
99
|
+
- **WHEN** <触发条件>
|
|
100
|
+
- **THEN** 系统 MUST <结果>
|
|
101
|
+
- **AND** <补充断言>
|
|
102
|
+
|
|
103
|
+
## MODIFIED Requirements
|
|
104
|
+
|
|
105
|
+
### Requirement: <既有业务能力名>
|
|
106
|
+
|
|
107
|
+
<!-- heading 必须与 specs/<domain>/spec.md 中的 ### 一字不差 -->
|
|
108
|
+
- ~~旧约束~~
|
|
109
|
+
- 新约束
|
|
110
|
+
|
|
111
|
+
## REMOVED Requirements
|
|
112
|
+
|
|
113
|
+
### Requirement: <废弃业务能力名>
|
|
114
|
+
|
|
115
|
+
下线原因:...
|
|
116
|
+
下线时间:YYYY-QX
|
|
117
|
+
|
|
118
|
+
## REJECTED Alternatives
|
|
119
|
+
|
|
120
|
+
### 备选方案名称
|
|
121
|
+
|
|
122
|
+
**否决原因**:...
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 三类标记详解
|
|
128
|
+
|
|
129
|
+
### ADDED Requirements
|
|
130
|
+
|
|
131
|
+
新增能力,archive 后 **append** 到目标 spec 文件。
|
|
132
|
+
|
|
133
|
+
每个 `### Requirement: <业务能力名>` 块必须包含:
|
|
134
|
+
- 一句 `系统 SHALL ...` 总体能力承诺
|
|
135
|
+
- 若干条 `系统 MUST/SHOULD ...` 约束
|
|
136
|
+
- 至少一个 `#### Scenario: ...`,使用 `WHEN/THEN/AND` 描述验收场景
|
|
137
|
+
- 接口、错误码、幂等、SLO 如影响对外契约,可写入对应 Requirement 的约束或 Scenario
|
|
138
|
+
|
|
139
|
+
### MODIFIED Requirements
|
|
140
|
+
|
|
141
|
+
修改现有能力。archive 时**替换**目标 spec 中对应的 `### heading` 块。
|
|
142
|
+
|
|
143
|
+
**关键规则**:
|
|
144
|
+
- `### heading` 必须与目标 spec **一字不差**(含空格、标点、大小写)
|
|
145
|
+
- 要删除的旧内容用 `~~strikethrough~~`,archive 时真正删除
|
|
146
|
+
- **不要**直接改 heading 文字;heading 改名 = REMOVED 旧 + ADDED 新
|
|
147
|
+
|
|
148
|
+
**反例**:
|
|
149
|
+
```markdown
|
|
150
|
+
# specs/order.md 里是:
|
|
151
|
+
### `order.create`
|
|
152
|
+
|
|
153
|
+
# delta 里写成:
|
|
154
|
+
### `order.create` v2 <!-- heading 改了 → delta_conflict! -->
|
|
155
|
+
### `order.create` <!-- 正确 -->
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### REMOVED Requirements
|
|
159
|
+
|
|
160
|
+
废弃现有能力,archive 时**整段删除**目标 spec 中对应块。
|
|
161
|
+
|
|
162
|
+
必须注明下线原因和时间,便于历史追溯。
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## REJECTED Alternatives
|
|
167
|
+
|
|
168
|
+
记录**否决的备选方案**,目的是让后人理解为什么没选另一条路。
|
|
169
|
+
|
|
170
|
+
每个否决方案写:
|
|
171
|
+
- 方案名(`### 方案描述`)
|
|
172
|
+
- **否决原因**(具体、可验证)
|
|
173
|
+
|
|
174
|
+
archive 后会合入 spec 文件的 `## REJECTED Alternatives` 段,成为永久设计记录。
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## heading 对齐规则(关键)
|
|
179
|
+
|
|
180
|
+
MODIFIED / REMOVED 的 `### <heading>` 必须与目标 spec **一字不差**(含空格、标点)。
|
|
181
|
+
|
|
182
|
+
**对齐失败 → archive 报 `delta_conflict`,停止合并。**
|
|
183
|
+
|
|
184
|
+
推荐做法:
|
|
185
|
+
1. 先读 `devflow/specs/<域>/spec.md`,复制现有 heading 粘贴到 delta
|
|
186
|
+
2. 不改 heading 的大小写、空格、标点
|
|
187
|
+
3. 改 heading 文字的需求 → 拆成 `REMOVED 旧 heading` + `ADDED 新 heading`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 接口契约补充写法(可选)
|
|
192
|
+
|
|
193
|
+
- 只有当字段级契约会影响验收或兼容性时,才在 Requirement 下补充 JSON Schema draft-07(`type`, `required`, `properties`, `enum`, `format`, `$ref`)
|
|
194
|
+
- 每个字段加 `description`,明确语义和约束
|
|
195
|
+
- 枚举值用 `enum`(不要写成字符串描述)
|
|
196
|
+
- 日期时间用 `{ "type": "string", "format": "date-time" }`
|
|
197
|
+
- 数字类型区分 `integer` / `number`
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"type": "object",
|
|
202
|
+
"required": ["userId", "channel"],
|
|
203
|
+
"properties": {
|
|
204
|
+
"userId": { "type": "string", "minLength": 1, "description": "用户 ID" },
|
|
205
|
+
"channel": { "enum": ["APP", "H5", "MINI"], "description": "来源渠道" },
|
|
206
|
+
"amount": { "type": "integer", "minimum": 1, "description": "金额(分)" }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## SLO 写作要点
|
|
214
|
+
|
|
215
|
+
SLO 必须是**可量化、可验证**的数字,不写"系统应尽快响应"这类模糊描述:
|
|
216
|
+
|
|
217
|
+
```markdown
|
|
218
|
+
#### SLO
|
|
219
|
+
- P95 < 50ms,P99 < 100ms(不含网络延迟)
|
|
220
|
+
- 可用率 ≥ 99.95%(单 region,滚动 30 天)
|
|
221
|
+
- 错误率 ≤ 0.01%(不含业务 4xx)
|
|
222
|
+
- 吞吐量:峰值 ≥ 1000 QPS
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## archive 合并语义
|
|
228
|
+
|
|
229
|
+
`devflow archive` 做:
|
|
230
|
+
|
|
231
|
+
- `ADDED Requirements` → append 到 `specs/<域>/spec.md` 的 `## Requirements`
|
|
232
|
+
- `MODIFIED Requirements` → 按 heading **替换**目标段落(删旧加新)
|
|
233
|
+
- `REMOVED Requirements` → 按 heading **整段删除**
|
|
234
|
+
- `REJECTED Alternatives` → append 到 spec 的 `## REJECTED Alternatives` 段
|
|
235
|
+
|
|
236
|
+
合并失败场景:
|
|
237
|
+
- heading 未找到 → 冲突,停止合并,报告 `delta_conflict`
|
|
238
|
+
- 并行 change 同时 MODIFY 同一 heading → 需人工合并
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 完整示例
|
|
243
|
+
|
|
244
|
+
参见 `examples/coupon-grant-api/devflow/changes/coupon-grant-api/delta/added/coupon-grant.md`。
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 与 SKILL.md 的关系
|
|
249
|
+
|
|
250
|
+
SKILL.md 给了触发时机与阶段入口。本文给:何时写 delta、格式规范(OpenSpec Requirements)、heading 对齐硬规则、场景写法、合并语义。
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# tech-spec / transaction-patterns
|
|
2
|
+
|
|
3
|
+
事务 / 并发 / 一致性的决策矩阵。design.md §8 的写作依据。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 先问两个问题
|
|
8
|
+
|
|
9
|
+
1. **一致性要求多强**?(强一致 / 读写一致 / 最终一致 / 容忍丢失)
|
|
10
|
+
2. **涉及几个边界**?(单 DB / 多 DB / 跨服务 / 跨数据中心)
|
|
11
|
+
|
|
12
|
+
答案组合决定用哪种 pattern。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Pattern 矩阵
|
|
17
|
+
|
|
18
|
+
| 一致性 \ 边界 | 单 DB | 多 DB | 跨服务 |
|
|
19
|
+
| --- | --- | --- | --- |
|
|
20
|
+
| 强一致(严格 ACID) | 本地事务 ✓ | 不可能(除非 XA;极少用) | 不可能 |
|
|
21
|
+
| 读写一致(线性化) | 本地事务 + 隔离级别 RR | 2PC / XA / Calvin | Saga + 一致性窗口 |
|
|
22
|
+
| 最终一致(BASE) | 事件驱动 + 重试 | 事件驱动 / CDC | Saga / 消息 + 补偿 |
|
|
23
|
+
| 容忍丢失(补偿 ok) | at-least-once | at-least-once | at-least-once + 对账 |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 本地事务(@Transactional / BEGIN...COMMIT)
|
|
28
|
+
|
|
29
|
+
**适用**:同库同连接,整笔操作要么全成要么全失败。
|
|
30
|
+
|
|
31
|
+
**硬约束**:
|
|
32
|
+
|
|
33
|
+
- 事务内**不要**调**外部 RPC**(RPC 慢 → 长事务 → 行锁积压)。把 RPC 移出事务前后。
|
|
34
|
+
- 事务内**不要**调**阻塞 I/O**(文件、网络,除数据库本身)。
|
|
35
|
+
- 事务越短越好:大批量 / 循环放在事务外,事务内只做必要的读写。
|
|
36
|
+
|
|
37
|
+
**常见陷阱**:
|
|
38
|
+
|
|
39
|
+
| 陷阱 | 描述 | 对策 |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Spring @Transactional 失效 | self-invocation、非 public 方法、非 proxy 调用 | 用 TransactionTemplate / AOP 代理调用 |
|
|
42
|
+
| 嵌套事务 | Propagation 默认 REQUIRED,回滚牵连父事务 | 明确 REQUIRES_NEW 或 NESTED |
|
|
43
|
+
| 长事务 | 循环调外部 API 在事务内 | 拆分:读在事务外,写在事务内 |
|
|
44
|
+
| 异常吞没 | catch 了异常但事务不回滚 | rollbackFor=Exception.class 或主动 setRollbackOnly |
|
|
45
|
+
| 隔离级别误选 | 用 READ_COMMITTED 期待 REPEATABLE_READ 效果 | 明确隔离级别 + 说明理由 |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 幂等(Idempotency)
|
|
50
|
+
|
|
51
|
+
**适用**:任何"客户端可能重试"的接口,特别是 POST(钱 / 券 / 订单)。
|
|
52
|
+
|
|
53
|
+
**实现手段**:
|
|
54
|
+
|
|
55
|
+
| 手段 | 适用 | 实现 |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| 唯一键 upsert | 有天然唯一 key | `INSERT ... ON DUPLICATE KEY UPDATE` / `ON CONFLICT DO NOTHING` |
|
|
58
|
+
| 请求 nonce / requestId | 无天然唯一 key | 客户端带 requestId;服务端 Redis/DB 存"已处理",过期 1-7 天 |
|
|
59
|
+
| 版本号 / CAS | 更新类 | `UPDATE t SET v=v+1, ... WHERE id=? AND v=?` |
|
|
60
|
+
| 状态机 | 状态流转类 | 前置状态校验,非法转移直接返回 |
|
|
61
|
+
|
|
62
|
+
**常见陷阱**:
|
|
63
|
+
|
|
64
|
+
- 幂等 key 用 userId 而不是 orderId → 同一用户第 2 单被误拒
|
|
65
|
+
- Redis 标记与 DB 事务不一致:Redis 写了 DB 失败 → 死锁。先 DB 后 Redis,或用可回滚的"预留"
|
|
66
|
+
- TTL 太短:用户/系统真的过很久重试 → 被当新请求处理
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 乐观锁(Optimistic Locking)
|
|
71
|
+
|
|
72
|
+
**适用**:低冲突(并发改同一行概率小)。
|
|
73
|
+
|
|
74
|
+
**实现**:
|
|
75
|
+
|
|
76
|
+
```sql
|
|
77
|
+
UPDATE account SET balance = balance - 100, version = version + 1
|
|
78
|
+
WHERE id = ? AND version = ?
|
|
79
|
+
-- if affected rows == 0 → retry or return 409
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**对比悲观锁**:
|
|
83
|
+
|
|
84
|
+
- 乐观锁:不阻塞;冲突时重试;适合低冲突读多写少
|
|
85
|
+
- 悲观锁(SELECT ... FOR UPDATE):阻塞;适合高冲突(如秒杀);注意死锁与超时
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 分布式锁
|
|
90
|
+
|
|
91
|
+
**适用**:多实例 / 跨进程 互斥(如"同一批次只能有一个 consumer 处理")。
|
|
92
|
+
|
|
93
|
+
**实现**:
|
|
94
|
+
|
|
95
|
+
| 实现 | 优 | 劣 |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| Redis SET NX + PX | 快、部署简单 | 主从切换可能丢锁;需要 Redlock 才完美 |
|
|
98
|
+
| Zookeeper / etcd | 强一致 | 运维成本;延迟高 |
|
|
99
|
+
| 数据库(SELECT FOR UPDATE) | 无额外依赖 | 性能差;锁粒度和事务绑定 |
|
|
100
|
+
|
|
101
|
+
**硬约束**:
|
|
102
|
+
|
|
103
|
+
- **必须带 TTL**(防持锁者崩溃后永不释放)
|
|
104
|
+
- **释放要验持锁者**(value=uuid,释放前 CAS 比对,防止错误释放别人的锁)
|
|
105
|
+
- **锁期间业务逻辑不能超过 TTL**,否则锁可能被下一个抢走还没释放
|
|
106
|
+
|
|
107
|
+
**Redis 锁模板**:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
SET lock:<key> <uuid> NX PX 30000 -- 30s TTL
|
|
111
|
+
-- 执行业务
|
|
112
|
+
-- 释放(Lua script 原子执行)
|
|
113
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
114
|
+
return redis.call("del", KEYS[1])
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Saga / 补偿事务
|
|
121
|
+
|
|
122
|
+
**适用**:跨服务的业务事务(如"下单=扣库存+扣余额+发消息"),不能用 XA。
|
|
123
|
+
|
|
124
|
+
**两种模式**:
|
|
125
|
+
|
|
126
|
+
- **Choreography**(协同):每服务监听事件、决定下一步,无中心协调器。松耦合、难调试。
|
|
127
|
+
- **Orchestration**(编排):有中心 saga 引擎,调各参与者。易追踪、可观测性好、有单点。
|
|
128
|
+
|
|
129
|
+
**必须设计**:
|
|
130
|
+
|
|
131
|
+
- 每步**都有一个补偿操作**(Cancel / Refund)
|
|
132
|
+
- **补偿必须幂等**(重复补偿不出错)
|
|
133
|
+
- 支持**超时**(某步超过 N 分钟 → 触发补偿)
|
|
134
|
+
- 记录**完整 saga 轨迹**(发生过的 step + pending / compensating)
|
|
135
|
+
|
|
136
|
+
**Saga 模板**(Orchestration 伪代码):
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
saga = new Saga(sagaId)
|
|
140
|
+
try:
|
|
141
|
+
result1 = inventoryService.reserve(...) # step 1
|
|
142
|
+
result2 = balanceService.deduct(...) # step 2
|
|
143
|
+
result3 = notification.send(...) # step 3
|
|
144
|
+
saga.markCompleted()
|
|
145
|
+
except Exception:
|
|
146
|
+
if step3 failed:
|
|
147
|
+
balanceService.refund(result2)
|
|
148
|
+
inventoryService.release(result1)
|
|
149
|
+
elif step2 failed:
|
|
150
|
+
inventoryService.release(result1)
|
|
151
|
+
saga.markFailed()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 批量 / 分片 / 限流
|
|
157
|
+
|
|
158
|
+
### 批量发放的事务策略(以 `add-coupon-batch-grant` 为例)
|
|
159
|
+
|
|
160
|
+
**反例**:外层 @Transactional 包 1000 条 INSERT → 长事务 + 行锁风暴
|
|
161
|
+
|
|
162
|
+
**正例**:
|
|
163
|
+
|
|
164
|
+
```java
|
|
165
|
+
for (String userId : userIds) {
|
|
166
|
+
try {
|
|
167
|
+
grantOneInNewTx(userId); // @Transactional(propagation=REQUIRES_NEW)
|
|
168
|
+
succeeded.add(userId);
|
|
169
|
+
} catch (RiskRejectedException e) {
|
|
170
|
+
failed.add(new FailedUser(userId, "RISK_HIT", e.getHitCode()));
|
|
171
|
+
} catch (Exception e) {
|
|
172
|
+
failed.add(new FailedUser(userId, "SYSTEM_ERROR", e.getMessage()));
|
|
173
|
+
log.error("grant fail user={}", userId, e);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- 每个 userId 独立短事务 → 不互相拖累
|
|
179
|
+
- 单个失败不影响整批
|
|
180
|
+
- 失败按类型归类,返回给前端
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 最终一致 + 对账
|
|
185
|
+
|
|
186
|
+
跨系统最终一致的常见设计:
|
|
187
|
+
|
|
188
|
+
1. **双写**:本地 DB + 发消息 / 写另一系统
|
|
189
|
+
2. **消息重试**:失败消息入死信 → 告警 + 人工介入
|
|
190
|
+
3. **定时对账**:T+1 跑对账 job,不一致部分重发 / 补偿 / 告警
|
|
191
|
+
4. **监控**:对账差异 > 阈值 → P1 告警
|
|
192
|
+
|
|
193
|
+
**关键**:"最终一致"不代表"不一致也 OK";要有清晰的 **RPO(可容忍数据丢失时间)/ RTO(恢复时间)**。
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 隔离级别速查
|
|
198
|
+
|
|
199
|
+
| 级别 | 读到的问题 |
|
|
200
|
+
| --- | --- |
|
|
201
|
+
| READ_UNCOMMITTED | 脏读 / 不可重复读 / 幻读 |
|
|
202
|
+
| READ_COMMITTED | 不可重复读 / 幻读 |
|
|
203
|
+
| REPEATABLE_READ(MySQL InnoDB 默认) | 幻读(MySQL 的 gap-lock 实际上也避免了幻读) |
|
|
204
|
+
| SERIALIZABLE | 无,但性能差 |
|
|
205
|
+
|
|
206
|
+
通常用 RC 或 RR;选 RR 时注意 gap lock 对并发插入的影响。
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## 本文与 SKILL.md 的关系
|
|
211
|
+
|
|
212
|
+
SKILL.md 给了事务 pattern 的表(单库 / 分布式 / 幂等 / 乐观锁 / 分布式锁)。本文给每种 pattern 的**硬约束**、**常见陷阱**、**伪代码模板**。tech-spec 写 §8 时对每个事务边界按本文逐条过。
|