☰
青砚 OJ 测试实践(四):异步判题、请求幂等与暂停恢复验证
2026/11/2 1:57:50 网站建设 项目流程

1. 测试背景

上一轮完成了题目查询、发布、判题结果和历史记录的核心业务回归。本轮进一步关注异步处理中的几个问题:

  • 提交请求返回成功后,后台任务是否真的完成?
  • 用户重复点击或网络重试,会不会创建重复任务?
  • 后台暂时停止处理时,任务是否能够保留?
  • 服务恢复后,原来的任务是否还能继续完成?

本轮围绕这些问题,完成正常异步处理、请求幂等、Worker 暂停恢复和 Web 重启后的任务保留验证。测试以接口响应、任务编号和数据库状态为主要证据,不进行性能压测,也不覆盖强杀进程、数据库宕机等复杂故障。

2. 先理解平台的异步判题流程

当前 OJ 将接收提交与后台判题分开处理:

用户提交代码 ↓ Web 服务检查并保存任务 ↓ 返回 202 和任务编号 ↓ 任务保存在 MySQL 队列中 ↓ Worker 领取任务 ↓ 编译服务编译、执行并比较结果 ↓ Worker 保存最终结果 ↓ 网页查询并展示结果

各组件的职责如下:

组件主要职责
Web 服务接收提交、检查参数和身份、提供任务查询
MySQL保存排队任务、代码、题目快照、状态和结果
Worker领取任务,调用编译服务,保存结果
编译服务编译代码、执行程序、生成判题结果
网页提交任务,轮询状态,展示最终结果

这里采用 MySQL 保存任务队列,没有使用 Redis 或 RabbitMQ。

异步判题并不代表答案会立刻返回,而是提交接口先完成“任务受理”,后台再按处理能力执行。这样可以避免提交请求一直等待编译运行,也便于分别维护 Web 和 Worker。

3. 测试思路与范围

本轮从四个方向进行检查:

方向主要问题验证思路
正常异步处理受理后能否得到最终结果?提交后按任务编号查询,等待终态
请求幂等重复发送会不会多建任务?重放相同请求,比较返回编号
暂停处理Worker 停止后任务会不会丢失?停止 Worker,再提交并检查排队状态
恢复处理服务恢复后原任务能否完成?重启 Web、恢复 Worker,持续核对原任务

恢复测试安排在自己使用的环境中进行,暂停 Worker 时不同时执行其他回归或压测。

4. 正常异步处理:受理不等于完成

提交代码后,接口返回 HTTP 202,表示服务器已经受理任务。此时不能直接判断“判题成功”,还需要根据任务编号继续查询。

任务状态包括:

状态含义
QUEUED已保存,等待处理
RUNNING后台正在处理
FINISHED已得到判题结论
ERROR系统处理故障或任务中断等异常

其中,FINISHED 不一定是答案正确。WA、CE、RE、TLE 等也可以是正常完成判题后的结论。

本轮正常场景使用正确参考代码,重点检查:

  1. 提交返回 202,并提供任务编号和查询地址。
  2. 后续查询始终针对同一个任务。
  3. 最终状态为 FINISHED,判题结果为 AC。
  4. 状态查询响应不包含提交代码或题目内部快照。

快速任务可能直接被查询到完成状态,因此没有要求每次都必须观察到 RUNNING。判断依据是任务最终状态和结果,而不是是否截取到了每个中间阶段。

5. 请求幂等:重复发送仍对应同一个任务

5.1 为什么需要幂等

考虑以下情况:

网页提交代码 ↓ 服务器已经创建任务 ↓ 网络异常,网页没有收到响应 ↓ 用户点击重试

如果每收到一次请求就创建新任务,同一次提交可能被重复判题。

因此,客户端为一次提交生成request_id。重试原请求时,继续使用相同编号和内容,服务器返回原任务。

5.2 本轮检查的几种情况

请求情况预期
同账号、同编号、同内容再次发送返回原任务编号,标记为复用
同账号、同编号,但修改代码或输入返回 409,幂等冲突
多个相同请求并发到达返回同一个任务编号
不同账号使用相同编号分别创建各自的任务

本轮使用 6 个并发相同请求,检查返回的任务编号是否一致。这是在验证并发条件下的去重正确性。

相同代码使用新的请求编号,仍然可以创建新提交。系统去重的是“同一次请求”,不是禁止用户反复提交同一份代码。

另外,客户端幂等键request_id与响应中的X-Request-ID用途不同:前者标识一次业务提交,后者用于关联单次 HTTP 请求日志。

6. Worker 暂停:任务应保留在队列中

Worker 负责领取和处理任务。正常停止 Worker 后,Web 和数据库仍然运行,因此提交接口仍可接收符合条件的任务。

本轮在 Worker 停止后,使用专用测试账号提交任务:

  • 前 4 个任务返回 202;
  • 任务状态为 QUEUED;
  • 第 5 个新任务返回 429,业务码为TOO_MANY_PENDING;
  • 重放第一条已受理请求,仍能返回原任务,而不是再创建一条。

这里的 429 表示达到单用户待完成任务数量限制,与账号模块每分钟认证次数限制不同。

这个场景同时检查了两件事:

  1. 后台暂时不处理时,任务能够保存并排队。
  2. 系统不会无限接收同一用户的新任务,但已有任务的重试仍可正常查询和复用。

通过数据库只读查询,进一步核对本轮四条任务确实存在,且编号与接口返回一致、状态为 QUEUED。

7. Web 重启:已受理任务应继续存在

保持 Worker 停止,再重启 Web 服务。

这样安排是为了避免任务在验证期间被后台快速处理掉,便于对比重启前后的排队记录。

Web 恢复就绪后,重新登录测试账号,重放原来的提交请求并查询任务,检查:

  • 返回的仍然是原任务编号;
  • 没有因为重放创建新任务;
  • 原任务仍处于 QUEUED;
  • 数据库中的任务归属、题目版本和创建时间等信息保持不变。

排队耗时会随着时间增加,因此没有将它作为“重启前后必须完全相同”的字段。

这一场景验证的是:任务已经保存在数据库中,不依赖 Web 进程内存维持。Web 重启后,仍可以读取和操作原有任务记录。

8. 恢复 Worker:原任务应完成

重新启动 Worker 后,继续检查前面保存的同一批任务。

恢复成功的判断不能只看服务变为 active,还需要确认真实业务恢复:

  1. 原任务能够被 Worker 领取。
  2. 查询的仍然是原任务编号。
  3. 四条任务最终均为 FINISHED,判题结果为 AC。
  4. 数据库中开始时间和完成时间有值。
  5. Web、Worker、编译服务和数据库恢复正常运行。

本轮使用正确参考代码,因此最终预期为 AC。如果使用错误答案,恢复成功也可能对应 WA,不能把所有场景的恢复标准都固定为答案正确。

9. 测试结果与结论边界

本轮正常异步、请求幂等、Worker 暂停、Web 重启和 Worker 恢复检查均按预期通过。

通过这组测试,验证了:

  • 提交受理与最终结果查询能够正确衔接;
  • 相同业务请求可以复用原任务;
  • 修改内容复用请求编号时能够识别冲突;
  • 小规模并发重复请求不会产生不同任务编号;
  • Worker 停止期间,任务能够保留在数据库中;
  • Web 重启后,原排队任务仍可查询和复用;
  • Worker 恢复后,原任务能够继续处理并完成。

需要明确的是,本轮采用正常停止和启动服务,不代表已经验证进程崩溃、数据库故障、网络分区或机器掉电等情况,也不能据此宣称实现了任何故障下的“恰好执行一次”。

此外,Web 的就绪接口主要检查 Web 与数据库。Worker 停止时,该接口仍可能返回就绪,因此还需要结合 Worker 状态、队列变化和真实任务结果判断判题能力是否恢复。

后续性能测试将继续区分任务受理速度、排队时间和最终完成能力,避免只看提交接口返回快,就认为整个判题系统性能良好。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询