Rejasupoem

deliver value to customers continuously or die;


[Failure teaches success] データの持ち方を失敗した

2014-06-15

社内には障害が起こったりすると、次回失敗しないように "Failure teaches success" っていう知見を蓄積するシステムがあるのだけど、この度 プライベートで書いてるアプリ で障害を起こしてしまったので、知見をブログに書くことにしました。

概要

今日の夕方にmiyagawaさんからAftershowが表示されないと連絡をいただきました。

発生原因

アプリ内でのデータの持ち方にいろいろ問題がありました。

Rebuild.fm for AndroidではEpisodeは端末のsqliteに保存していて、ActiveAndroidで読み書きしていましたが、リスト表示するために何かのカラムでソートする必要があったのだけど、日付は "Jun 15 2014" みたいに入ってくるからソートしづらいなと思って、urlを見てたら "http://rebuild.fm/10" みたいにスラッシュの後ろにエピソード番号が入ってたから、これいいじゃんと思ってIDにしてソートに使ったりしていました。 これが間違いでした。

今日配信されたAftershow 45のurlは "/45a" で、episode_idをINTEGERで持ってたので、変換するときにNumberFormatExceptionが投げられていました。

対応

マイグレーションをしたり、変換に失敗したときに安全側に倒したり、タイトルをパースするのをやめたりしました。

DBのマイグレーションをするのにsqliteはCHANGE COLUMNとかDROP COLUMNができないっていうことを知らずにハマり、最終的にはキャッシュとして扱ってるデータだから仕方なしってことでテーブルをDROPしてCREATEしました。

手元ではうまく動くことを確認。(実際にうまく動くか心配)

再発防止策

テーブルを設計するときには以下の二つをよく考えること。また気をつけてレビューすること。

  • 端末に保持するデータは妥当か
    • 今回だとurlの末尾を一意と見なしてIDとして持っていてけど、それで良かったのかとか、url変わったらどうするんだとか、あまり考慮されていなかった思います。
  • 保持するデータの型は妥当か
    • sqliteだとALTER TABLEで型を変えることはできないので、慎重に決める必要がありました。今回はキャッシュだったのでDROP AND CREATEしたけど、これがキャッシュじゃなくてユーザが書いた下書きとか、端末内にしかないデータだったら消失することになって、これがエンタープライズだったら死。

まとめ

業務ではリリース前にロールバック用のapk (リリースされた安定バージョンからバージョンをインクリメントしたもの) を用意しているのだけど、アップデートでDBの構造が変わった場合はロールバック (本当はロールバックではない) ができないので、そもそもテーブルのALTERは諦める (とはいえどうしてもやらないといけないときは方法なくはないので根性「Guts」でなんとかしますが) という運用をしてるけど、プライベートでも業務でも本当に気をつけた方がいい。(教訓)