# 双系统切换与回退手册

## 进入切换窗口前

1. 对照 `route-contract-matrix.md`，确认所有仍有调用量的旧契约已经完成客户端迁移或兼容层验证。
2. 连续观察影子指标；`gateway_shadow_errors_total` 不增长，契约 mismatch 已逐项解释，且 shadow 专用 Key 没有生产权限扩张。
3. 使用生产等价数据量执行 `gateway-loadtest`，确认错误率、p95、数据库连接池、critical Redis 延迟和审计队列丢弃数均在阈值内。
4. 冻结旧系统配置写入，执行最后一次幂等数据导入和数量/外键校验。
5. 确认 Go 与旧网关的 `/readyz` 均为成功，并备份 Nginx 活动 upstream include。

## 原子切换

`scripts/cutover.sh` 只修改一个专用 Nginx include。它先探活目标，写临时文件后原子替换，执行 `nginx -t`，再 reload；验证或 reload 失败会恢复备份。

```bash
export NGINX_ACTIVE_INCLUDE=/etc/nginx/conf.d/ai-gateway-active.conf
export GO_UPSTREAM=10.0.0.21:8080
export LEGACY_UPSTREAM=10.0.0.20:8000
export GO_HEALTH_URL=http://10.0.0.21:8080/readyz
export LEGACY_HEALTH_URL=http://10.0.0.20:8000/readyz
export PUBLIC_HEALTH_URL=https://gateway.example.com/readyz

scripts/cutover.sh preflight
scripts/cutover.sh go
scripts/cutover.sh status
```

## 回退触发条件

- 5 分钟窗口错误率超过既定 SLO，或出现鉴权/配额错误的系统性抬升；
- p95/p99 持续超过容量验收阈值；
- 审计队列出现丢弃、critical Redis 不稳定、数据库连接池耗尽；
- 与旧系统相比出现未经批准的响应结构差异或关键业务功能缺失。

执行 `scripts/cutover.sh rollback`。回退只切流量，不回写/删除 Go 数据；outbox 与审计 Worker 保持运行，便于事后核对。确认旧系统恢复后保存本次 include 备份、指标和日志，再分析问题。
