故障处理与恢复演练手册
本页按现象给出只读排查、安全修复、验证与恢复边界。命令中的 qa-fixture、qa-backup、测试数据库和连接别名均为隔离演练示例,不指向真实用户数据。本次编写没有运行这些命令或执行恢复。首先记录时间、平台、版本、网络、操作阶段与脱敏错误;不要收集密码、验证码、verifier、票据、访问 token、记忆正文或完整带参数回跳 URL。
处理顺序与共同保护措施
- 确定影响范围:单设备、单账号、单资源、全部 API、身份上游、媒体或存储。
- 保留当前基线和原始错误,用只读检查确认实际运行版本、配置来源、资源与请求阶段。
- 对能重现的最小场景准备合成夹具;涉及写入、权限、密钥和迁移时先制定可验证的恢复点。
- 修复后重复原触发路径,再测拒绝、断网、重启与另一账号;记录实得,不只记录进程恢复。
- 关闭事件前确认未产生数据覆盖、重复业务对象或滞留资源,并注明未覆盖设备和依赖。
本地健康探测只说明对应层:Account /api/health 执行数据库查询,聊天 /health 仅说明 HTTP 进程响应。公开入口还需分别验证 DNS、TLS、代理和业务。正常退出使用进程管理器的有序停止,让服务处理信号;不要把强杀、重装或删除数据库作为默认动作。
授权网页成功但未回到 App
| 阶段 | 只读检查 | 安全处理与验证 |
|---|---|---|
| 开始 | App 目标聊天地址、授权 URL 主机、pending 保存结果 | 修复环境或存储错误后重新发起;不要手工拼接他人回调 |
| Account | 用户是否完成身份验证与应用授权、系统浏览器是否仍显示确认 | 区分系统“继续”提示、登录表单和实际授权完成;不因表单出现而结案 |
| HTTPS 回调 | 回调路由阶段、上游交换状态、是否过期或取消 | 对照客户端登记和当前 issuer 修复;一次性票据不反复消费 |
| App scheme | scheme/host 注册、当前构建、返回时进程状态 | 用授权测试账号重新完成流程;实际杀进程后的回跳单独记录 |
| 换票 | proof、state 的校验阶段、HTTP 状态、是否切换账号 | 只比较状态,不输出秘密;失败后保持原有效会话并重新授权 |
Android NativeAuth.kt 只允许正式 Account HTTPS;覆盖聊天地址或端口转发不能建立隔离 Account 原生登录。iOS Debug 可接受受限本机环境,但 Release 不读取该覆盖。恢复验证应覆盖正常授权、取消、过期、错 state、重放、临时离线与主动退出后的迟到响应。Android 已有一次真实授权冷启动历史,iOS 完整真实账号及审核 iPad 仍需独立证据。
会话突然 401 或临时无法连接
先确认 401 来自聊天 /me 或业务接口,而不是网页、媒体服务或反向代理。聊天 session 默认 24 小时,当前没有 refresh-token 接口。核对设备时间、环境、最近退出和升级;不要复制原 token 到其他设备,也不要把 Account token 当聊天 token。
真实 401 应清理对应失效会话并重新登录;网络超时、503 或临时离线应保留凭据。检查错误分类与当前账号 generation,避免旧请求清掉新账号。离线退出只证明本机已退出,远端退出失败时旧 session 可能持续到期限。修复后的验证包括重开恢复、断网恢复、账号切换与另一设备不受无关清理影响,见客户端生命周期。
消息重复、发送结果不明或事件似乎丢失
先区分两次独立点击和一次提交丢响应。记录合成用例的范围与 clientId,核对服务端是否返回已有消息;不能把相同文字当作同一业务动作。原请求重试沿用原 clientId、正文与 mediaId;换内容或范围后复用旧 ID 会返回 CLIENT_ID_REUSED。修复协调器或队列后模拟“提交已成功但响应丢失”,断言服务端与双方列表各只有一条。
事件是刷新信号,不是完整消息正文。比较当前快照、普通事件游标、请求 after 与返回 cursor,确认没有使用消息 ID 或 MLS sequence 代替。长轮询等待者在单进程内,断线或多实例场景要检查后续查询是否取到已提交事件;不能把没有即时唤醒等同于数据已丢失。恢复时重新读取有权限的列表与历史,随后从正确游标继续;不要倒改数据库 ID、删除事件或清空消息“重同步”。
媒体下载拒绝、位置不更新与城市为空
媒体 401 先处理会话;403/404 核对所有者、可见引用消息、当前群成员与个人历史可见边界。普通媒体的单聊读取不额外检查好友或屏蔽状态,既有可见引用不会仅因屏蔽/删好友自动失效;不能把发送限制当读取限制。上传成功不自动向其他账号公开,删除自己的可见历史后也可能失去引用授权。新事媒体的权限与 Range 另有契约。恢复时修正错误引用或客户端状态,不扩大为匿名公开下载;以发送者、接收者和外人三账号验证。
城市为空先区分拒绝定位、IP 兜底、目录未收录、系统解析失败和离线库损坏。只查目录版本、摘要与可信代理规则,不记录设备坐标,不信任任意转发头,也不使用附近城市伪造当前位置。坏库允许返回空值;素材恢复后重新载入并验证,检查离开会话时迟到结果被丢弃。
实时位置要分别检查系统采集、服务端更新和界面刷新。记录最后更新时间,确认用户已经显式开始,PUT 不应替用户自动开始。当前服务端记录没有自动过期字段,停止本机采集与删除共享记录必须协调;断网时不能把最后一帧说成实时移动。iOS 退出没有逐范围 DELETE,共享不会随聊天 session 注销或到期自动删除;其他仍有资格成员可能读取最后位置。Android bestEffort 远端清理失败也可能残留。安全处理应在授权账号可用时使用明确停止入口,并复查远端记录与其他成员视图;不能仅以退出界面或通知消失结案。验证停止、离群、权限撤回、前后台和重启,详见位置共享。
来电不响、接听后无声或通话未结束
按创建、邀请、系统推送、接听、媒体 token、入房、轨道、输出设备、结束逐段检查。active 只表示信令状态;LiveKit 本机入房也不能证明蜂窝网络双向媒体。核对成员资格、媒体服务配置、token 期限与网络路径;若参与者已离开,不应为其签发新媒体凭证。
iOS 另核对签名环境、APNs sandbox/production、VoIP topic、设备注册是否绑定有效 session,以及提供商返回的脱敏状态。提供商接受不能替代锁屏系统来电到达。修复后测双方音画、静音、扬声器、蓝牙、切后台、网络切换、多设备竞争与挂断后的迟到任务。结束状态异常时先复查房间人数与宽限期,不能因一次查询失败就宣告空房。不要将恢复通话状态用于发出虚构 VoIP 来电。
记忆损坏、密钥不可用或转出失败
iOS loadError 与 Android memoryLocked 表示应保护原库,读取失败不能转换成可写空库。先确认设备是否解锁、是否刚升级、文件与密钥是否仍匹配;用文件存在性、大小和密文摘要辅助诊断,不解密真实内容作为报告。禁止清 Keychain/Keystore、卸载、生成新密钥覆盖或删掉“坏文件”后重建。
修复只针对确定的解码、权限或兼容问题,并先在合成副本重现。已有密钥丢失时,服务端聊天备份不能重建本机记忆;没有成功导出的资料不能承诺恢复。转出失败还应区分聊天许可桥接、当前 Account 契约与系统保存位置。现行 Account 主源码缺少对应 redeem 路由,旧副本和本地 fixture 不能证明生产可用。清理仅针对本次生成的部分临时输出,验证取消与失败不损坏原库,见记忆转出。
SQLite 锁、磁盘满与一致性故障
SQLITE_BUSY 先检查是否存在重复写实例、长事务或备份/迁移任务;当前数据库使用 WAL、外键与 5 秒 busy timeout。磁盘满先检查所在卷空间、配额和权限,不能先删数据库或 WAL 腾空间。/health 200 不保证新事务能写入,必须用合成业务验证。出现损坏信号时停止继续写入,保留数据库与相关 WAL 状态,交由授权维护者处理。
只对已经取得的隔离快照做检查:
set -eu
test -f qa-backup/chat.sqlite
sqlite3 -readonly qa-backup/chat.sqlite 'PRAGMA quick_check;'
sqlite3 -readonly qa-backup/chat.sqlite 'PRAGMA integrity_check; PRAGMA foreign_key_check;'
integrity_check 返回 ok 不检查外键关系,需另运行 foreign_key_check;空输出表示没有报告外键违例。结构完整也不代表账号映射、加密密钥和权限语义正确。不要对故障原件运行通用 .recover 后直接上线,更不能手工禁用外键掩盖不一致。SQLite PRAGMA 说明给出了检查范围。
仅用合成数据进行备份与恢复演练
- 在新 QA 环境建立两合成账号、好友、群、消息、媒体与私有新事,记录预期关系和少量摘要。
- 明确镜像/源码、数据库结构、当前稳定密钥的版本标识,暂停该演练环境的结构变更。在线备份使用 SQLite Backup API,不在写入时随手复制主文件。CLI
.backup与快照机制见官方命令行说明和备份说明。 - 源文件必须存在,目的文件必须是新的演练文件;示例只读取合成源并写入新备份:
set -eu
test -f qa-fixture/chat.sqlite
mkdir -p qa-backup
test ! -e qa-backup/chat.sqlite
sqlite3 -readonly qa-fixture/chat.sqlite ".backup 'qa-backup/chat.sqlite'"
shasum -a 256 qa-backup/chat.sqlite
- 运行前述结构与外键检查,记录工具版本和退出码。复制备份到新的隔离恢复目录,拒绝覆盖已有目的库;在恢复环境使用配套密钥与匹配的 Account 测试映射,不能指向原服务或真实云投递。
- 启动恢复环境后验证两账号资料、历史、未读、外人拒绝、媒体摘要、群成员边界及合成新写入。API 验证由获授权的测试操作完成,不把源库与恢复库同时当作生产写入口。
- 对照恢复点记录可接受的数据损失范围和恢复用时。任何结构、权限或解密差异未解决时,不进行正式切换。
Account MySQL 要另行演练,一致性数据备份与 IDENTITY_SECRET、PII 密钥、签名材料配套保护。示例连接别名需预先指向独立 QA 实例,凭据通过受保护客户端配置提供:
set -eu
test -d qa-backup
test ! -e qa-backup/identity.sql
mysqldump --login-path=qa-identity --single-transaction \
--routines --triggers --events identity_qa > qa-backup/identity.sql
mysql --login-path=qa-identity-restore identity_qa_restored < qa-backup/identity.sql
导入对象必须是事先批准的新隔离数据库,不是现有业务库;备份命令失败时应立即停止,不能拿空文件继续导入。备份文件本身是敏感数据,不进入公开站点。PII 旧数据迁移需先验证备份,再明确审批,错误密钥不能用审批开关绕过。正式恢复由维护窗口停旧实例后切换,必要时撤销旧资格;回退数据库可能丢弃恢复点之后的写入,应单独决策并记录。更多门禁见发行清单。