サービスリニューアル時に経験した失敗談

前田です。
年末に向けて忙しくなってきましたが皆さんはいかがでしょうか。

先日、受託しているWebサービスの大幅なリニューアルを弊社で実施しました。
そのリニューアル作業時に遭遇した失敗と反省を共有させて頂きたいと思います。

リニューアルの概要

プロジェクトの内容は一般的なCtoCのWebサービスでした。
既存でPHPのSlimフレームワークで実装されているアプリケーションを、Railsに置き換えるという難易度の高いプロジェクトでした。

リニューアルの内容は主に下記の3つです。

  • 既存のインフラや開発環境を見直し、再構築出来るようにインフラのコード化(Chef)やCIを利用した継続的デプロイが出来るよう改善
  • PHPのソースコードで実装されているロジックをRailsアプリケーションで実装する
  • 既存のデータベースをリニューアルのDBに最適化して移行する

当日のデータ移行手順

当日のリプレイス作業はサービスの稼働帯域が少ない深夜に行いました。
アプリケーションのデプロイやインフラの構築はその時点ではすでに実施済みだったので、主な作業はデータ移行関係です。

データ(DB)移行のざくっとした手順は下記の通りです。

  1. 既存環境でダンプデータを取る(多少データを整形)
  2. ダンプデータをリニューアル環境に持っていく
  3. リニューアル環境に用意していたデータ移行用のテンポラリDBにダンプデータを流しこむ
  4. テンポラリDBから本番DBにデータ移行バッチ(ruby)でデータを流し込む

この4のデータ移行バッチの作成は主に私が担当しました。

対象データの概要

移行したかったデータは下記のようなデータです。

  • ユーザー
  • アイテム
  • ユーザーのお気に入りアイテム

そして、「アイテムのお気に入り数が多い順にソートする」という要件を満たす為、アイテムにはユーザーのお気に入り数を持つカラム(favorite_count)を持たせました。
FavoriteUserItemモデルには、レコードが作成された時にItemのfavorite_countをインクリメント・デクリメントするように、counter_cacheを使いました。

そしてデータ移行を行った時に起こったアクシデントは、このfavorite_countが移行されないというトラブルでした。
実は、既存のデータでFavoriteUserItemのレコード数と、Itemモデルのfavorite_countの整合性が取れていない状態だったので、データ移行時にcounter_cacheを使用して、きちんとしたデータにする予定でした。

バグが発生したデータ移行バッチ

当初実装していたデータ移行コードは下記のようなコードでした。
※ FavoriteUserItemOldは、移行元のテーブルを読み込んだActiveRecordのクラス

初期状態のコード

FavoriteUserItemOld.each do |f|
  FavoriteUserItem.create(user_id: f.user_id, item_id: f.item_id)
end

このコードのままであれば問題無くデータ移行できたのですが。。
リニューアルの数日前に、私がこんな改修をしてしまいました。

改修後のコード

ary = []
FavoriteUserItemOld.each do |f|
  ary << FavoriteUserItem.new(user_id: f.user_id, item_id: f.item_id)
end
FavoriteUserItem.import ary

データ移行バッチがこのポイントで大幅に時間をロスしていたので、すでに導入していたバルクインサートをするgem(activerecord-import)を使用して、バルクインサートをする処理に変えてしまったのです。

バルクインサートをすると、当然ActiveRecordのカウンターキャッシュ機能は使用出来ない訳で。。
データ移行した後に、「あれ?アイテムのfavorite_count(お気に入り数)が移行されていないぞ?」となった訳です。

この問題に気付き、データ移行バッチをすぐに修正して再度データ移行を流し直しましたが、データ移行バッチ自体に結構時間を要するので、大幅に時間をロスしてしまいました。。

なぜこの問題が起こった?

バルクインサートをする処理にコードを改修してしまったからですが、その改修をしてしまったのはカウンターキャッシュを使用しているという認識が抜け落ちていたから、です。

初期状態のコードも私が実装したのですが、その時は当然カウンターキャッシュを使用して、favorite_countに値を入れる、というしっかりとした目的を持ってそのように設計・実装しました。
しかし、時が経過して同じコードを見た時にそのことをすっかり忘れてしまって、データ移行バッチにかかる時間を短縮したい、という安易な目的の為に改修してしまったのです。

ですので、バッチのコードに一言コメント「カウンターキャッシュを使用して、Itemのfavorite_countをカウントアップしています」といった内容のコメントがあれば、初期コードから改修はしなかった訳です。
単純に意識が足りていなかった、というだけかもしれませんが。。(;´∀`)
また、リニューアル時に使用する使い捨てのバッチということもあり、テストコードを書いていなかったことも、気づけなかった要因の1つだと思います。

こういった先の事を見越してソースコードにコメントをする、というのは難易度が高いと思います。
が、この経験を活かし、もっと先を読んでコードを実装するように心がけていきたいと改めて思いました。

まとめ

リニューアル作業時にはこのトラブル以外にもいくつかトラブルがあり、リニューアル作業が終わった後軽く打ち上げする予定だったのが、反省会になってしまいました。。
同じ轍を踏まないように、もっと精進し、次回こそ打ち上げ(?)が出来るように頑張ります!╭( ・ㅂ・)و

このエントリーをはてなブックマークに追加