Barbarism begins at internet:2021年12月28日分

2021/12/28(Tue)

[どうでもいい話] 元現場猫のひとりごと

もう終わりだよこの2021年、2週間後の日本は NY(New Year)というタイミングでこの現場猫案件、京大データ喪失でなく京大生DT喪失ならよかったのにね…

そもそも/LARGE0から/LARGE1へのミラーリングはバックアップとよべるものじゃないのでデータ喪失なんぞ起きて当然の約束された事故なのだけど、後者の文章に

弊社 100%の責任により

とあるのでサービスレベルアグリーメントと設計に乖離があったってことっすかね、現場猫も現場猫なら営業猫も営業猫だよヨシ!

そんで現場猫の方のやらかしは

3 ファイル消失が発生した原因
バックアップスクリプトには、find コマンドにより 10 日以上古いログファイルを削除する処
理が含まれています。スクリプトの機能改善と合わせて、find コマンドの削除処理に渡す変数名
を視認性・可読性を高めるため変更いたしましたが、この修正したスクリプトのリリース手順に
考慮不足がありました。
bash は、シェルスクリプトの実行中に適時シェルスクリプトを読み込みます。この挙動によ
る副作用を認識できておらず、実行中のスクリプトが存在している状態でスクリプトの上書きに
よりリリースしてしまったことで、途中から修正したシェルスクリプトの再読み込みが発生し、
結果的に未定義の変数を含む find コマンドが実行されてしまいました。この結果、本来のログ
ディレクトリに保存されたファイルの削除をする処理ではなく、/LARGE0 のファイルを削除し
てしまいました。

うーん、古より伝わるUNIX(TM)園児にゃぁの最も簡単な自殺法「rm -rf $TMP/*実行時に$TMPをセットし忘れる」とほぼ同じというね、うーんクラッシック。

これ読む限りでは

find ${logpath}/ -mtime -10 -delete

的なコードだったのが問題で、bashは再読み込みするから実行中にファイル書換えたことで云々は軽率ではあるけど根本的な問題ではなく

if [ -n "$logpath" ]; then
	find ${logpath}/ -mtime -10 -delete
fi

というフェールセーフを思いつくほどの頭がコーダーにしろコードレビュアー(まぁ存在したかは怪しい)に無かったのが問題だったという気がする、というかlogrotate(8)使わんの?log4jといいオレオレ実装はわりとろくでもない結果にしかならんぞ。

(追記) よく考えると if [ -n "$logfile" ]; then のある行より先にファイルポジションが進んでたら結局アウトか、申し訳ないこれはワイの責任HP(Hundled Percent)でございます。

同様の理由で set -u もアウト、特にこっちはスクリプトの先頭で宣言するだろうから、元スクリプトに無かったら無理だわ。

まぁワイはシェルスクリプト怖いのでshebang含めて3行以上のシェルスクリプトは書かない主義なのでこういう頓珍漢なことを書いても許してチョンマゲ。

@隙在自語

後世への戒めとしてワイがかつて遭遇したfind(1)使った事故についての怪談をしよう。

昔々とある組織の基幹サーバーで夜間バッチが動いてたそうじゃ、このバッチの目的は

  • ある特定のディレクトリ以下に蓄積される大量の日次ファイル
  • ↑のタイムスタンプを確認し一定期間より古いものはすべて削除する

というものじゃった。

この日次ファイル、ファイル名には空白文字を含まないという暗黙の了解があったのだが

  • 日次ファイルはクライアント端末からアップロードされる
  • ファイル名はその端末およびユーザー固有情報より組み立てられる
  • 端末およびユーザー固有情報はおま環で空白文字を含むものがわずかに存在した
  • クライアント/サーバー側どちらも空白文字の存在チェックは行っていない

といういい加減な仕様と運用によるイレギュラーにより、全体の0.1%くらいはファイル名に空白文字を含むものがあったんですわ。

そして削除スクリプト書いた現場猫はそもそもfind(1)に-print0オプションがあることすら知らないレベルだったと思われる。

よってこの0.1%のイレギュラーは削除されることなくどんどん蓄積されていくことに。 ファイルサイズ自体はとても小さいのでディスクスペースを圧迫し容量不足でアラートを上げるようなことも無く、誰にも気づかれることは無かった、そうこれを読んでるオッサンどもの血中脂肪のようにね。

そして数年が過ぎ開発担当者はとっくに別の現場に異動になりこのスクリプトの存在を知るものは誰もいなくなった頃、満を持したのかのように爆弾は破裂した。

もうお判りですね?そう大量の削除漏れファイルによってinodeが枯渇して、ディスクに空きがあってもファイルが作れない状態になってしまったのだ。

その結果として夜間バッチのほぼ全てが異常終了し、ジョブマネージャーの管理画面が真っ赤に染まりオペレーターは絶叫しサイレンは鳴り響き重大障害として取締役が呼ばれるまでエスカレート、そんでワイが後始末をぜんぶ押し付けられて深夜に呼び出され、殺気立った面々に罵倒され熱く焼かれた鉄板の上でアチアチ踊りさせられるというね、あーほんとクソ。

ちなみにこのスクリプトにはもうひとつ面白い話がある、なぜかよりにもよってC Shellつまりcsh(1)で書かれたスクリプトなのだ。 これはジョブ管理にとある会社のスターウォーズの登場人物っぽい名前のミドルウェアが採用されてたのだが、なぜかスクリプトはよりにもよって(2回目)C Shellスクリプトで書かなくてはならないという謎ルールが上から降りてきたためとのこと。

あまりに理不尽な話なので、なぜよりにもよって(3回目)C ShellなのかBorne Shellで開発すべきなのではと何度と無く苦情を言ったのだが、このミドルウェアの開発元がC Shellを推奨してるんだからそうしろという回答が帰ってくるのみ。 もちろんマニュアルを読んでもそんなトンデモな制限は無い *1

そしてある日はたと気づいた、これは営業猫同士の伝言ゲームによって「Cで書かれたプログラムあるいはシェルスクリプトを推奨」が「Cシェルスクリプトを推奨」に化けたんだこれ! 現実世界でパタリロの「さきほどの伝言の意味を知らせよ、あるいは暗号か」→ 「さっきはどの田楽も実は白子よ、蟻は貧乏か」をやるんじゃねえよクソァ!

というかこの現場、他にもろくでもない思い出が一杯あるのだがそれはまた別の機会ということで。

*1:ちなみにそのミドルウェア、正式なビルドイン言語はなんと時代遅れにもほどがあるTclだった、えぇ…