---
name: restore
owner: team-lead
version: 1
last-updated: 2026-04-22
---

# SOP: 从 OSS 备份恢复 sqlite 数据

对谁承诺：事故时能在 15 分钟内把 xiangqin 数据恢复到前一日 02:00 备份点。

## 触发条件

- sqlite 文件损坏 / 被误删
- 运维操作失手（rm / schema 破坏 / data drift）
- 想在另一台机器复制 prod 数据用于调试

## 前置

- [ ] epsilon `/root/.local/bin/aliyun` 已配 `xiangqin` profile（见 scripts/provision.sh 和 sop/operations/sms-onboarding.md 风格）
- [ ] OSS 桶 `agentaily-backup-xiangqin-prod` 可访问（`aliyun --profile xiangqin oss ls oss://agentaily-backup-xiangqin-prod/daily/`）

## 步骤

### 非 in-place（默认，安全）

```bash
ssh root@112.124.27.213
bash /opt/xiangqin/scripts/restore.sh
# 默认取最新一份：daily/xiangqin-YYYYMMDD.db.gz
# 或指定日期：bash /opt/xiangqin/scripts/restore.sh 20260422
```

产生目录 `/opt/xiangqin/data/restore-<ts>/xiangqin.db` —— **prod 不动**。用它检查：

```bash
sqlite3 /opt/xiangqin/data/restore-<ts>/xiangqin.db 'SELECT COUNT(*) FROM users'
# 或启动一个并行 uvicorn 实例接这个库做 preview
```

### in-place 覆盖 prod

```bash
bash /opt/xiangqin/scripts/restore.sh --in-place             # 取最新
bash /opt/xiangqin/scripts/restore.sh 20260422 --in-place    # 取指定日期
```

脚本会：
1. 停 systemd
2. `mv` 当前 prod → `xiangqin.db.before-restore-<ts>`（回滚保险）
3. 覆盖 prod
4. 重启 systemd

## 验收

- [ ] `curl http://112.124.27.213/health` 绿
- [ ] `ssh root@112.124.27.213 'sqlite3 /opt/xiangqin/data/xiangqin.db "SELECT COUNT(*) FROM users"'` 和预期相符
- [ ] `cd /Users/yarnb/xiangqin && uv run xq query 'gender=f AND city=hangzhou'` 返回结果

## 失败

- aliyun profile 过期 / 证书被撤 → 到阿里云控制台重新生成 AK/SK → 更新 vault aliyun/main → epsilon 上 `aliyun configure set --profile xiangqin ...`
- OSS 桶挂了 → 阿里云工单。没备份的话只能认（sqlite 本地 WAL 其实也有一定恢复能力，`sqlite3 xiangqin.db .recover`）
