「本番環境を壊した」第二章:消えるフレンドリスト
揺らがぬ PHP への怒りが芽生えたきっかけとして、まだ記憶に残っている本番環境障害の話です。
Zynga Poker で消えるフレンドリスト
Zynga Poker は Zynga の最も古いゲームです。何年も前にそのチームの一員として働かせていただいていた時、古代から積んできた PHP の堆積層が危うくダクトテープで繋いだ Java ベースの TCP ソケットサーバーのフランケンシュタインが、ゲームのバックエンドでした。「セクシー」という愛称で呼ばれた、Memcached を MySQL のフロントキャッシュにした自家製データーレイヤがこれら二つのアプリサーバーを支えていました。どちらからもセクシーを直接アクセスできるように、セクシーのクライアントは PHP と Java に対応した実装が存在しました。
他のユーザーデータと同じく、フリエンドリストもセクシーに保存されており、ゲームクライアントがそのデータを Java サーバーから取得していました。ところが、データの流れが遠回りで、なぜか PHP の部分も含まれていたのです。大まかにこんな感じでした:
Flashゲームクライアント -> Java -> PHP -> セクシー -> PHP -> Java -> Flashゲームクライアント
理由は詳しく思い出せませんが、なぜか PHP の部分を抜こうと思いました:
Flashゲームクライアント -> Java -> セクシー -> Java -> Flashゲームクライアント
PHP を Java に書き直すという単純労働が、うまくいかないはずがないんじゃないかと。
ちなみに、フレンドリストのデータ構造がこんな感じでした:
{
// SNS -> ユーザーIDの配列
"1": ["ユーザーID 1", "ユーザーID 2"], // "Facebook"フレンド
"29": ["ユーザーID 3", "ユーザーID 4"] // "某SNS"フレンド
}
が、ごく一部のユーザーのデータは「0」インデックスの配列も謎に含まれていたのです:
{
"0": [], // おそらく、前史のデータ移行バグ
"1": ["ユーザーID 1", "ユーザーID 2"], // "Facebook"フレンド
"29": ["ユーザーID 3", "ユーザーID 4"] // "某SNS"フレンド
}
ユーザーに影響がなかったので、それでも良かったんですが、Facebook のフレンドしかいないユーザーのデータは不思議な現象が起きます!
PHP のjson_encodeが黙々とこんなものを返してくれます:
[[], ["ユーザーID 1", "ユーザーID 2"]]
PHP 配列の特徴のおかげで、シリアライズ後のデータが、配列としてもオブジェクトとしても存在しうるのです!PHP を通してデータの読み書きを行えばなんの問題もありません。
待ち構えている災害に全く気づかず、コードを Java に書き直し、本番環境に出しました。
後処理
出血を止めるのは簡単でした。記憶が正しければ、使い勝手が良く、柔軟性で富んでいる Jackson JSON をうまく使い、PHP によってシリアライズされたデータとの互換性を維持することに成功しました。
が、まだ青い僕はそれ以上何もできなかったのです!とても頼れるテックリードがアナリティクスチームと連携を取り、フレンドリストの再構築に成功し、助けていただきました。
その頃の僕は、非常時に他チームと連携を取るためのコミュ力と行動力が不足しており、今回の特徴的な本番環境障害を最後の最後まで解決に向かわせることができなかったのです。とはいえ、その時から数多くの本番環境障害の経験を経ってきた今の僕は、少しぐらいは成長してきたんじゃないかと思っています。
「壊れていないものを直すな」
英語で「If it ain’t broke, don’t fix it」ということわざがありますが、「壊れていないものを治すな」を意味します。今回の出来事はその一例ではないかと考えるのが自然かもしれませんが、僕が思うに、ことわざ自体は単純すぎて、誤解しやすいのです。ソフトウェアの変化に対する恐怖心を煽り、開発者の満足度と、ソフトウェアプロジェクトの保守性の妨げになりかねないのです。