この記事は本番環境でやらかしちゃった人のアドベントカレンダー9日目の記事です。
https://qiita.com/advent-calendar/2019/yarakashi-production
もう15年以上前の事なので記憶も定かではないところがありますが、ご容赦下さい。
当時の状況
当時自分は30人くらいの、孫請・曾孫請を中心に受託開発を行う小さなSIerに居ました。
この会社、自社製品も一応あるのですが売上のメインは圧倒的に受託開発で、
PHPやPerlでのガラケーサイトの開発やら、Javaや.NETを使った業務アプリケーションの開発、大手プロバイダシステムの開発保守など、わずか3年ほどの在籍期間でしたが、実に多彩な開発案件があったように思います。
プロジェクト内容
あるWebサイトのシステム移行でした。
Windows Server上に構築されたIIS+ASP+SQL Serverなシステムから
Linux上のApache+Tomcat+Oracleな環境への移行だったように思います。
そのサイトの利用者は会員登録をしてホテルや飲食店などの予約ができたり、各店舗からのメルマガが受信できるようになっています。
自分は前年にOracle Master 9i GOLDを取得したこともあって、DBAとしてプロジェクトに参加し、
* 既存DB(SQL Server)のテーブル構成の調査
* 移行先のOracleの設計と構築
* アプリケーションが書くSQLの作成
* 既存のSQL ServerからOracleへのデータ移行やそのためのプログラム作成
なんかを1人で担当していました。
当時のメンバーは
* アプリケーション開発担当者が2〜3名
* DBA(自分)が1名
* プロマネ(上司)が1名
という構成だったと思います。
上記の他に先方の開発担当者が3名いらっしゃいました。
DBAは自分だけだったので、念の為上司にもDBA権限を付与
していました。
事件発生
ある日、いつものように実際にデータを流し込みながらデータ移行プログラムの検証を行ってたところで事件が起こりました。
その日はそれまで100〜1000人程度で検証していた会員情報の移行処理を、
一気に全員分(数万人)流し込んで、データの取りこぼしが無いか、また実行時間はどのくらいになるかを検証しようとしてました。
私「バッチ実行〜ポチッとな」
私「(ん〜?なんか今日はこれまでより件数の消化遅くないか…?)」
私「(メモリに乗り切らなくてswapが発生してるのか?ちょっと調べるか…)」
…数分経過
先方開発担当者A「なんか、メールが飛んできたんですけどなんでかわかります?」
私「(開発サイドが何かやらかしたかな?)」
先方開発担当者B「ほんとだ!自分のとこにも来ました!なんだこれ?」
同僚エンジニアa「本当ですね…アプリケーションではメール送信処理なんて実装してないんですが…」
先方開発担当者C「これ、外部にも飛んじゃってますよね…」
同僚エンジニアb「至急調べてみます!まずは停止させないと…」
…数分経過
先方開発担当者A「データ移行が何か関係してないですかね?」
私「えっ?」
先方開発担当者A「このメールアドレスとか、自分が立ち上げ初期に登録したやつなんですよ。そこにメールが来てるんです。」
私「え?え?え?(なんでだなんでだなんでだ?)」
私「と、とりあえずデータ移行止めてみますね!」
私「バッチ停止」
…数分経過
ALL「止まったみたいですね…」
何が起こったか
『おれはSQL ServerからデータをOracleに移行していると
思ったらいつのまにか4,000人のユーザーにメールを送信していた』
実は前日、(そしてこの日不在の)上司がOracle上にPL/SQLでメール配信の処理を書き、
それを会員テーブルのINSERT Triggerに設定していたのでした。
誰にも言わずに…
それにより、私のバッチ実行で会員テーブルにINSERT処理が発生、
上司のセットしたTriggerが反応し、見事にバッチを止めるまでの全員に対し「会員登録ありがとうございます」メールが送信されてしまったのでした。
何を学ぶか
1. 報・連・相をしっかりやる
まぁ、これに尽きるんじゃないかと思うんですが、
上司はメール送信処理をPL/SQLで作成しTrigger設定したことを、
アプリケーションエンジニア側にも、DBA側にも伝えていませんでした。
上司は本プロジェクトには片足突っ込んでの参加で普段は他の案件に居たこと、
逆に他の面々は常駐して顔を合わせて作業していたので
情報共有に大きな差が生まれていたことは事実でした。
情報に格差が生じていることを誰かが気づいてケアしていれば防げた問題だと思います。
その上で、付け加えるならば
2. ビジネスロジックを分散させない
「DB側にストアドプロシージャ作ってそこにビジネスロジック書いてTriggerでキックさせるなんてやらねーよ」
とか思うかもしれませんが、例えばRailsで予期せぬafter_commit
とかが居ると同じことが起こるわけで。。。
アプリケーションエンジニア(Java側)が知らない箇所(PL/SQL)に会員登録メール送信処理が居たことで、
どこでメール送信が発生しているのかわからず、発見と停止が遅れました。
会員登録後のメール送信という処理が、普段から顔を合わせているアプリケーションエンジニアで担当していれば、
Triggerの罠に引っかかることもなかったでしょうし、
データ移行の検証時にも「これメール送信しないかテストしてからやろう」となったかもしれません。
※因みにPL/SQLで作った理由について当時上司は「会員登録後にアプリケーション側が意識せずに確実にメールが送られるようにしたかったから」みたいなことを言っていたように思います。
3. 目的の処理だけが実行されることを保証した環境をつくる
これを意識したり実現するのはなかなか難しいことだとは思います。
当時、移行プログラムを何でどう作っていたのかも記憶が定かではないのですが、
例えばOracleであればダイレクト・パス・ロードの使用を検討したり、
予めTriggerを使えないように無効化しておくなどが考えられたなーと思います。
例えばRailsで言えば、Callbackを走らせたい場合なのか万が一にも走らせたく無いのか、
走らせたく無いのであればskip_callback
等を検討する、などでしょうか。
最後に
この一件から数カ月後に転職したのですが、
この日以降、転職するその日まで上司とはまともに口を利かなくなった気がします。。。
I田さん、元気にしてますか?