コンテンツへスキップ

並び替えAPIの「件数が変わっていないか」検証 — 書き込み前のサニティチェック

ドラッグ&ドロップで複数件のデータを並べ替える機能を作るとき、新しい順序をサーバーに送って保存する処理が必要になる。このとき気をつけないと、並べ替えのロジックに小さな欠陥があるだけで、データが消えたり重複したりしたまま上書き保存されてしまう。

補足: ここでの「サニティチェック」とは、処理結果が常識的に正しいはずの条件を満たしているかを確認する簡易チェックのこと。詳細な検証ではなく、「明らかにおかしくないか」を見る最後の網。

並び替えロジックの構造

ドラッグ&ドロップで並べ替えた新しい順序は、IDの配列としてサーバーに送られてくる。サーバー側では、その配列に従って既存データを並べ直す。

# クライアントから送られた順序に従って並べる
reordered = []
placed_ids = set()
for sid in dedup_order:
    if sid in by_id:
        reordered.append(by_id[sid])
        placed_ids.add(sid)

# order に含まれなかった項目は、既存の相対順序を保ったまま末尾に追加
for site in data:
    sid = str(site.get('_id', '')).strip()
    if sid and sid in placed_ids:
        continue
    reordered.append(site)

この2段構成自体は理にかなっている。クライアントが送ってくる並び順は、フィルタや検索で絞り込まれた一部のデータだけかもしれない。その場合、並び順の指定がなかった項目を「既存の順序を保って末尾に追加する」ことで、表示されていなかったデータが消えてしまう事故を防いでいる。

それでも残るリスク

問題は、この「並べ替え」と「残りを追加」という2つのロジックの組み合わせ自体に欠陥があった場合だ。IDの重複処理、文字列化の境界条件、データ形式が不正なエントリの扱いなど、ロジックが少し複雑になるほど「全件が一度だけ出力される」という前提が崩れるリスクが上がる。このロジックにバグがあれば、並べ替え後のデータが元のデータより少なくなったり(消失)、多くなったり(重複)してしまう可能性がある。

対処 — 書き込み直前に件数の一致を検証する

並べ替えロジックの結果をファイルに書き込む直前に、もう一度「入力件数と出力件数が一致しているか」だけを確認する。

# サニティチェック: 件数が一致していること(取りこぼしも複製も無いこと)
if len(reordered) != len(data):
    app.logger.error(
        'reorder_sites: count mismatch (in=%d, out=%d) — refusing to write',
        len(data), len(reordered),
    )
    return jsonify({
        'status': 'error',
        'message': '並び替え処理で件数が一致しなかったため保存を中止しました',
    }), 500

_atomic_write_json(filepath, reordered)

件数が一致しない場合は、エラーログに入力件数と出力件数の両方を記録し、ファイルへの書き込み自体を中止する。並べ替えロジックのどこに欠陥があったのかは特定できなくても、「保存していい状態かどうか」だけは機械的に判定できる。

なぜ「件数の一致」だけで十分な防衛線になるか

この並べ替え処理は、データの内容を変更するわけではなく、既存データの順序を変えるだけだ。つまり、処理の前後で「集合として同じ要素を含んでいる」という性質が必ず保たれているはずだ。件数が変わっていれば、要素が消えたか重複したかのどちらかが起きたことになる。逆に件数が一致していても内容の整合性が完全に保証されるわけではないが、「明らかな破損(消失・重複)」を検出するには、件数比較という軽量な手段で十分機能する。

設計のポイント

これは「並べ替えロジックを完璧に書く」ことを目指すのではなく、「ロジックに欠陥があっても、それがデータ破損につながる前に検出して止める」という設計だ。複雑なロジックほどテストで全パターンを網羅するのは難しいが、「処理の前後で守られるべき不変条件」を1つ見極めて、それだけを最後にチェックするという考え方は、テストでは拾いきれない未知のバグに対する保険として機能する。

まとめ

観点 内容
機能 ドラッグ&ドロップの並べ替え結果を ID 配列で受け取り、既存データを並べ直して保存するAPI
リスク 「並べる」「残りを追加する」の2段ロジックに欠陥があると、データの消失・重複が起きうる
対処 書き込み直前に入力件数と出力件数を比較し、不一致なら書き込みを中止してエラーを返す
設計思想 ロジックを完璧にすることではなく、「不変条件が崩れた状態での書き込み」を機械的に防ぐ

複数件のデータを一括で書き換える処理は、ロジックが正しく動いている前提で実装しがちだが、前提が崩れたときに何が起きるかを考えておくことが、実際のデータ保護につながる。「完了」という言葉が複数の意味を一緒くたにしてしまう罠は、デプロイ完了とgitコミット完了を混同した事故にも共通する。