Codeforces Problems の不具合対応
概要
Codeforces Problems で特定の問題が大量に表示される不具合が発生しました。 原因はクローラのバグだったので直しました。
はじめに
Codeforces Problems
は私が開発した Codefores で出題された問題およびその各ユーザーごとの正答状況を確認しやすくするための Web アプリケーションです.
AtCoder Problems を模倣して作りました。
最近 Codeforces Round #822 (Div. 2)
が重複して大量に表示されるという不具合が発生しました。
この記事では事象の原因および解決方法について述べます。
発生する事象
以下の画像に示すように同じコンテストが重複して大量に表示されています。
原因の究明
Codeforces Problems
では1日1回クローラにより問題のデータを fetch して json ファイルに書き出しています。
コンテストおよび問題のデータが格納されている contests.json
を確認したところ、#822 (Div. 2) が重複して格納されていました。
$ cat contests.json | grep 1734 "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, "id": 1734, $ cat contests.json | grep 1734 | wc -l 25
数えてみたら25個同じコンテストの情報が格納されていました。
コンテストの情報の書き込み時の処理を見たところ以下のような Python コードで実装されていました。
if idx != -1: prev_contest = contest_json[idx] if prev_contest["problems"] is None: contest_json[idx] = add elif len(add["problems"]) > len(prev_contest["problems"]): contest_json = [add] + contest_json elif len(add["problems"]) == len(prev_contest["problems"]): for i, (prev_problem, add_problem) in enumerate( zip(prev_contest["problems"], add["problems"])): if prev_problem != None and "rating" in prev_problem: continue contest_json[idx]["problems"][i] = add_problem else: contest_json = [add] + contest_json
idx
という変数は更新前のコンテスト情報が格納されているインデックスです。
バグの原因は 1つめ elif ブロックでの記述です。
elif len(add["problems"]) > len(prev_contest["problems"]): contest_json = [add] + contest_json
同じコンテストの情報に関して新しいものと古いものを比較して、新しいほうが古いものより問題数が多かった場合、古い情報とは別にデータを追加しています。
これにより、#822 が無限に書き込まれていました。何を意図したコードなのかは不明です。
以下のコミットによりこのバグを修正しました。
また重複して書き込まれた情報については手で直しました。
終わりに
久しぶりにこのアプリのコードを読んだのですが、今回関連するコードだけ見てもかなりひどかったので近いうちにリファクタリングしたいと思います。
長らく放置している issues の対応もそのうちします。
今回の作業は着手してから記事の執筆も含めて1時間程度で終わったので隙間時間を使ってこれからもちょいちょい手直ししていきたいと思います。