一家维修合作社想给仓库调拨增加预约功能。门店提交申请,仓库确认后预留备件,接收门店点收,财务可查调拨历史。系统已经在服务旧门店客户端,库存表每天也有其他任务写入。一次改动会同时碰到 API、数据库和后台任务,验收不能只看新增页面能不能打开。以下以虚构的两间门店和十件备件为例,演练从约定业务规则到回滚演习的全过程。
先冻结契约和不能破坏的规则
和门店及仓库人员一起写出调拨单的状态。申请时记录来源、去向和数量。确认时才占用可用库存。点收结束后,库存从在途转入目的门店。取消时只释放还没出库的预留。对每一步,记录执行角色、允许的前置状态、状态变化、相关事件和错误响应。确定是否允许部分收货,有争议的规则在开发前交给业务负责人确认。
契约给客户端写清字段类型、必填条件、错误结构、分页以及重复提交的结果。为在途单据约定稳定的标识,后续读取和追踪都沿用它。旧客户端可能仍按原结构解析状态。新增状态前,用真实的旧版消费方解析一份脱敏样本。若它无法识别新增值,先保留对旧客户端的映射,或明确安排版本升级,再启用新行为。
把库存不变量写成可以核对的断言。库存计数不得为负,确认总量不超过当时可预留量,每条库存流水能找到对应调拨单。取同一仓库的十件备件,给两家门店各建一条预留七件的申请,同时确认。验收记录应能解释哪一条成功、另一条为什么失败,以及最后剩余多少。数据库写入应原子地更新可预留量与单据状态。生成外部通知的步骤单独记账,避免发送失败导致一次已提交的预留被重复执行。
把实现切成可以单独部署的小段
先实现读取调拨单和状态映射,用一份固定夹具核对来源仓库、目的门店及时间字段。再实现确认操作,核对事务边界、锁的竞争和幂等行为。为客户端生成一个提交键。服务端收到重复请求时,应按契约返回原处理结果。若同一个键对应不同的请求内容,测试应证明系统拒绝这次冲突且没有多扣库存。不要把网络超时当作操作一定失败,允许客户端以原键查询或重试。
异步通知可以由后台任务从已持久化的事件记录读取。给事件唯一编号,处理端记录已消费结果。测试时先让通知沙盒断开,再恢复,观察任务重试、队列积压和最终状态。排查时用调拨编号串起 API 日志、库存流水及通知回执。若任务支持重复投递,验收应覆盖消息重复到达但对同一门店只产生一条业务通知的情况。
落地时让架构设计能力先从流程图指出事务边界与外部依赖。实现辅助能力接收已确认的契约与代码目录,只为选定的改动生成差异。审查能力以差异、权限矩阵和数据变更计划为输入,检查有没有旧客户端破坏、库存不变量缺口及日志泄露。先让它们分别完成一个可人工确认的小样,再决定是否交付更大任务。代理产出的 SQL 和环境命令需要在执行前核对目标、权限与影响。
数据库改动要在混合版本下演练
假设需要将早期使用的自由文本目的地逐步迁移为受约束的门店编号。先增加允许为空的新字段和所需索引,保留原字段供旧版本读取。确认现有表规模、索引构建方式和数据库版本下的锁行为。先在隔离环境从脱敏快照恢复,测量每个迁移步骤的持续时间,检查写入延迟与复制积压。演练一边跑旧版本、一边跑新版本,验证它们都能读取已有记录,并写明新旧写入在过渡期如何保持一致。
回填分批执行,按稳定的主键记录进度。每批统计已补齐、需要人工匹配和无法处理的记录。给任务一个可以重复执行的条件,例如仅处理新字段为空且旧文本能唯一对应门店的行。遇到相同名称匹配多个门店时写入异常清单,保留原记录,交给数据负责人裁决。批次提交后检查总量与抽样明细。旧实例全部下线且差异清零后,再把读取切向新字段。收紧约束和移除旧字段放到后续独立发布,并记录回退窗口结束的依据。
测试一次中断后的恢复。执行两批后暂停任务,模拟期间仍有人创建调拨单,随后从最近进度继续。最后核对新记录与历史记录是否都完成映射。如果新字段已成为写入唯一来源,旧版本应用可能不能直接接管。上线计划中分别写清应用回退和数据库纠错的方法,由负责人在演练中证明需要多久。对已经发生的业务写入,不用全库恢复掩盖局部问题,先定位影响行并确认可用的补偿方案。
把权限检查放在数据真正被读取的地方
为门店、仓库和财务建立权限矩阵。门店只能申请和读取本店单据,仓库负责确认和出库,财务仅可读指定统计。后台按当前身份和资源归属判断,不能依赖前端菜单是否隐藏。为查询、列表、批量导出和异步下载分别测试。先使用本店合成账号跑通允许路径,再只改变门店或角色。下载结果也要检查内容,确认其他店的单据不会混入其中。
给后台任务单独的服务身份和最小数据权限。库存同步任务不需要修改用户角色,报表任务无需更新库存。把配置和密钥从代码及测试样本移出,日志只保留追踪编号和必要字段。如果发现接口拒绝操作但库存仍改变,按请求标识找到事务提交位置和异步事件,安排回归检查同时覆盖数据库与队列。审查过的预期与复现步骤保留在同一份变更记录里。
测到有证据再调性能
从真实的调用模式设计负载。门店列表查询会分页,仓库确认集中在收货时段,后台还有批量回填。先记录普通工作负载的请求数、错误率、响应分位数、查询次数、锁等待和队列积压。造一批分布接近实际的脱敏数据,比如几间热门门店占大多数记录,另有少量包含大量明细的单据。只有均匀小样本时,某些索引或序列化开销可能不会出现。
假设确认操作在高峰明显变慢。先沿慢请求的追踪标识比较应用处理、数据库等待和下游响应。若时间主要花在库存行的竞争,先评估能否缩短事务,再看调拨确认是否需要更细的资源划分。若列表出现随明细数增长的重复查询,检查查询计划并复跑固定分页场景。每次只改一个因素,按相同数据和并发条件做前后对照,并验证库存规则和权限边界。将测量版本、参数和结论写进发布记录,便于同事复跑。
用接近生产的条件验收和回退
单元测试覆盖状态规则,契约测试覆盖旧客户端,集成测试使用与线上相同系列的数据库引擎并带上真实迁移脚本。排练时配齐后台任务、权限角色、异常告警和重试配置。给正常确认、拒绝确认、回填中断、通知恢复和应用回退各一条可重跑用例。每条保留版本、种子记录、请求标识和结果。不要让统一的通过数盖住未运行的用例。把未执行原因与责任人记在发布单。
从小范围门店启用。启用前记下库存差异、错误率和队列长度基线。限定观察窗口和停止条件,例如开始出现无法解释的负库存、权限越界或队列持续增长。条件触发时停止扩大范围,先关闭新入口并保留证据,再按演练过的步骤回退。若数据库已写入新格式记录,先核对旧版本是否仍能读取,再决定是否回退应用或继续运行兼容版本。发布结束后确认告警恢复,跟踪仍待人工处理的调拨单与迁移差异。
常见问题
可以先改数据表,再补接口契约吗?
先定义客户端和业务要依赖的规则。实现可以随着验证调整,但没有契约,就难以判断新增字段、错误响应或默认值是否破坏旧调用方。先用一条旧版请求和一条新版请求验证过渡行为,再推进后续迁移。
回填显示成功,为什么还要核对差异?
任务成功可能只表示扫描完成。对照新旧记录的数量、未映射清单与抽样结果,并检查运行期间新增的单据。还要证明读取新字段的应用仍遵循原来确认过的权限规则。
测试结果应该交给谁?
将契约变化交给消费方确认,库存规则与异常单据交给业务负责人,迁移记录、延迟数据和回退证据交给运行负责人。每项结论保留对应的构建号与测试记录。需要补测的条目写清触发条件和下一位负责人。
用交接记录验证值班人员能处理部分成功
演练时安排一名未参与开发的值班人员接手。给他一个客户端超时的调拨编号和一次后台任务告警,请他判断仓库是否已经预留、通知是否已经发送、门店接下来该查询还是重试。值班手册应写明可以访问的日志和只读查询、每条状态的含义、需要交给谁处理,以及哪一步需要业务确认。若只能依靠开发人员进入数据库手工查表,补上可审计的查询或状态视图,再请另一位同事重走同一场景。
再做一次版本交接演练。将一个仍在途的调拨单留给新版本处理,随后关掉新入口并启动旧客户端。确认旧客户端至少能读取经契约允许的状态,后台仍可处理已经持久化的任务,财务也能查到对应库存流水。记录每一步花费时间和任何人工操作。若启用或回退导致通知被重新发送,找到负责去重的环节并加上跨版本复测。把最后一次成功结果和实际可回退的版本一起附到发版单。