背景:pull-based dispatch 的 landing 契約是 at-least-once,不是 exactly-once。dispatch/job.py 的 complete_job 會在兩種情況重呼叫注入的 landing callable:
- lease 過期 → reclaim → 換一台 runner 重跑整份 submission(既有系統本來就會發生);
- landing 寫到一半拋例外 →
complete_restore 還原 leased → runner 依 retry 規則重送 → 全量重跑,不是接續。
第 2 點在 Back-End#358 的 review 被指出:目前 Submission.process_result 先 update() 寫 Submission,再由 finish_judging() 累加 User 計數、append homework 的 submissionIds,分多次 save 且無交易保護。後段 save 失敗時,重試會重複前段已成功的副作用(計數被灌兩次、submissionIds 出現重複)。
dispatch/ 端已用 test_complete_retry_after_partial_write_reinvokes_in_full 把這個語意釘在 seam 上——該測試明確斷言副作用會發生兩次,由 mongo 端吸收。
範圍:mongo/submission.py 的 process_result / finish_judging,以及它們碰到的 User、Homework.student_status。不動 dispatch/。
Done:
process_result 對「完整重跑」與「partial write 後全量重試」皆冪等:重複執行後 User 計數、homework submissionIds、student_status 與只執行一次的結果相同。
- 有測試涵蓋:同一 submission 連續
process_result 兩次;以及在 finish_judging 中途注入例外後重試。
- 若改用 set/upsert 語意取代累加,需確認既有 rejudge 路徑行為不變。
依賴:無,可獨立進行;但必須早於 keystone 接線(Normal-OJ/Normal-OJ#66)。
參考:spec §17.2「process_result 重算容忍性」、ADR-0003 Consequences、Back-End#358 review thread。
背景:pull-based dispatch 的 landing 契約是 at-least-once,不是 exactly-once。
dispatch/job.py的complete_job會在兩種情況重呼叫注入的 landing callable:complete_restore還原leased→ runner 依 retry 規則重送 → 全量重跑,不是接續。第 2 點在 Back-End#358 的 review 被指出:目前
Submission.process_result先update()寫 Submission,再由finish_judging()累加User計數、append homework 的submissionIds,分多次 save 且無交易保護。後段 save 失敗時,重試會重複前段已成功的副作用(計數被灌兩次、submissionIds出現重複)。dispatch/端已用test_complete_retry_after_partial_write_reinvokes_in_full把這個語意釘在 seam 上——該測試明確斷言副作用會發生兩次,由 mongo 端吸收。範圍:
mongo/submission.py的process_result/finish_judging,以及它們碰到的User、Homework.student_status。不動dispatch/。Done:
process_result對「完整重跑」與「partial write 後全量重試」皆冪等:重複執行後User計數、homeworksubmissionIds、student_status與只執行一次的結果相同。process_result兩次;以及在finish_judging中途注入例外後重試。依賴:無,可獨立進行;但必須早於 keystone 接線(Normal-OJ/Normal-OJ#66)。
參考:spec §17.2「
process_result重算容忍性」、ADR-0003 Consequences、Back-End#358 review thread。