Codeforces Problems の不具合対応

概要

Codeforces Problems で特定の問題が大量に表示される不具合が発生しました。 原因はクローラのバグだったので直しました。

はじめに

Codeforces Problems は私が開発した Codefores で出題された問題およびその各ユーザーごとの正答状況を確認しやすくするための Web アプリケーションです. AtCoder Problems を模倣して作りました。

cf.kira924age.com

最近 Codeforces Round #822 (Div. 2) が重複して大量に表示されるという不具合が発生しました。

この記事では事象の原因および解決方法について述べます。

発生する事象

以下の画像に示すように同じコンテストが重複して大量に表示されています。

大量に表示される #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 コードで実装されていました。

source: https://github.com/kira924age/CodeforcesProblems/blob/5b2f521117e98481f6581c113ef1b6dc26c1a740/cf-problems-crawler/update_data.py#L37-L52

    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 が無限に書き込まれていました。何を意図したコードなのかは不明です。

以下のコミットによりこのバグを修正しました。

github.com

また重複して書き込まれた情報については手で直しました。

github.com

終わりに

久しぶりにこのアプリのコードを読んだのですが、今回関連するコードだけ見てもかなりひどかったので近いうちにリファクタリングしたいと思います。

長らく放置している issues の対応もそのうちします。

今回の作業は着手してから記事の執筆も含めて1時間程度で終わったので隙間時間を使ってこれからもちょいちょい手直ししていきたいと思います。