Things To Avoid, Episode 1: INSERT IGNORE
Legacy code strikes back
ドキュメント化されていない言語機能に依存することはかなり危険です。 そして、それが INSERT IGNORE と混在すると、事態は本当に悪くなります。
2016 年初めの OS アップグレードの 1 つで、Perl は 5.14 から 5.18 へとアップグレードされました。 残念ながら、ハッシュの取り扱いが変わり、かなり特殊なシナリオでのキーの順序が維持されなくなり、コードが壊れました。 そのとおり、Perl は順序を約束していませんでしたが、私たちのコードはいくつかの実装の仕様に依存していました。
By default, two distinct hash variables with identical keys and values may provide their contents in a different order where it was previously identical.
なぜ問題になったのでしょう。 コードは、データベースに挿入されるハッシュのリストを準備していました。 次に、最初の項目を使用して、その後に続く INSERT クエリに渡される列名のリストを準備しました。 しかし、送信された各INSERTクエリは、挿入する値のリストを準備するために異なるハッシュを使用していました。 これは次のようになります。
INSERT IGNORE INTO foo (id, date) VALUES (1, ‘2006-10-11`), (‘2007-12-09’, 2), …
基本的に、列名とそれぞれの値の順序は保持されませんでした。 もちろん、MySQL は通常、整数カラムに日付を挿入しようとするとエラーを発生させます。 しかし、MySQLのドキュメントからの引用を覚えていますよね? これらのエラーは、INSERT IGNORE が使用され、サイレント データ変換が行われる場合には発生しません。
Data consistently inconsistent
Perl のアップグレード後はスクリプトが正常に動作していましたが(少なくともその標準エラーは静かでした)、数日後に Perl で動くテーブルを使っている内部ツールがおかしいというバグ レポートが届きました。 その報告から判断すると、何かがかなり怪しい。 そして、私たちの心配は SELECT クエリによって証明されました:
では、何があるのでしょう。 タイムスタンプと整数値を文字列に変換してユーザー名として挿入し、年数を整数カラムに格納(2016年は編集回数か、最終編集の年か)。 3645>
あなたは同意する必要はありませんが、私にとっては、上記の行のような「データ」ほど良いデータはありません。 それらを再生成することはできますが、長いプロセスです。 この記事を読んでいる間にも、スクリプトは実行されています。 すべて 1 つのクエリの IGNORE のおかげです。
Lessons learned
- INSERT IGNORE は、データを意味のない値の集合に変えるためにあなたの間違いを待っている静かな野獣です。 大きな声を出してください。 文書化されていない言語機能 (Perl とハッシュキーの順序は、多数の例のうちの 1 つに過ぎません) に依存することは、危険なゲームです。 今すぐあなたのコードをチェックするか、INSERT IGNORE クエリのトラフィックを ngrep してください。