モデル設計を適当にやるとどうなるのか

408 views

Published on

Rails アプリケーションの保守開発をターゲットとして、
データ構造が壊れていく過程と、それをどのように本番運用しながら直していくかについて

Published in: Engineering
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
408
On SlideShare
0
From Embeds
0
Number of Embeds
19
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • この度は、MedBeer にお集まりいただき、そして登壇させていただき、ありがとうございます。まずは1点、謝罪からさせてください。
  • スンマセン。弊社、まだ rails 5.1 じゃないっす!本当スンマセン!
    えー、そんなわけでですね
  • 今日は、 Rails を使ったウェブサービスの保守開発における一般論について、かなりエモい話を、ある開発者の経験したストーリーをお話ししたいと思います。
  • と、いうわけで、「モデル設計を適当にやるとどうなるのか?Railsにおけるデータ構造の変更記録」というタイトルで簡単に喋らせていただきます。よろしくお願いいたします。
  • まずは自己紹介から、やすだあつしという、どこにでもいる普通の Rails アプリケーションエンジニアです。
    CrowdWorks という web サービスがあるのですが、あんな機能とかこんな機能を作ったり壊したり直したりしています。
    Rails は 2010年から2年くらいやった後、期間が空いてトータル4年半くらいやってます。
  • あとですね、今日のまとめは「小さく、コツコツやっていこう」というエモい感じの結論になりますので、よろしくお願いいたします。
  • さて、突然ですが質問させてください。
    「ワークアラウンド」って悪いものじゃないですよね??
  • Ruby on Rails を使ったプロダクトを開発しているということは、多分 Web 関連の何かだとは思うのですが、何にせよいつかはサービスをリリースしますよね?
    そして、サービスが成功すればするほど、長〜い保守開発が始まるかと思います。で、その保守開発の中で
  • (ここはそのまま読む)

    ある程度、サービスとして安定しているなら時間はそこそこ取れるかと思うのですが、
  • 特に、リリース直後なんて、時間とってゆっくりとバグ修正したり、UX改善したり、負荷対策したり、新機能の実装したりできるケースってあんまりないかと思います。
  • と、いうわけで、たいていの場合、まぁ、レベル感にもよるんですけど、一時しのぎなコードを書くわけですよね。
    QCD で言えば、 Cost と Delivery に寄せて、Quority はそこそこのコードです。
  • そんなわけで、ワークアラウンドは、まぁ、悪いものじゃないわけです。
    特に、後で治すのであれば。後で治すのであれば、後で治すのであれば。
  • ワークアラウンドは、少なくとも絶対悪ではないと熱く語ったところで、次の話題なのですが
  • 運用しているサービスのデータ構造、もっと抽象的なところで言えばモデルクラスの設計とか、ドメインモデルとかって、きちんと全体の合意を得つつ、維持し続けるのって難しいですよね??
    ちょっとフィクションをお話ししますが、
  • あるところにオンラインで仕事ができるサイトがあったとしてですね。
    ユーザ間でメッセージをやり取りできる機能。みたいのを作ったとしましょう。
    User と Message の間に中間テーブルがあって、 Rails 的には has many through でやり取りできそうな、そんな感じの設計ですよね。
  • で、返信機能が欲しいと言われたので、messages に parent_message_id を増やして、ツリーを作れるようにしたと。
  • で、まぁ、案の定、返信リストを見る画面、みたいのが重くなるので仮装スレッドみたいなモデルを新設して、返信ツリーは1つのスレッドに所属するみたいな設計をしたと。
    負荷対策なので、急いでやる必要があったんでしょうね。
  • で、やり取りも増えてきたし、「お気に入りのフラグ」みたいのをつけられるようになって
  • その頃、進化の途中で作られた「スレッド一覧画面」が無視できない重さに、なったので、検索効率を上げるために User と Message の中間テーブルに
    送信者と受信者の id を追加して
  • みたいな進化を遂げた結果、進化したそんなサービスがあったと。繰り返しになりますが、フィクションです。
  • フィクションなんですけど、UI的にはこんな風なスレッド一覧があって、一つのスレッドをクリックすると
  • スレッドの内容詳細画面がある、みたいな UI だったとします。フィクションです。さて、想像してみてください
  • 途中でジョインしたとして、新機能を作ろうにも、これ以上の負荷対策しようにもある程度はリファクタリングしてからじゃないとどうしようもなくなった、そんな時の気持ちを。
  • 無言、10秒。
  • 無言、10秒

    とりあえず、ですが、短いエンジニア人生の経験から言えるのは、
  • これは
  • まぁ、やめておいた方が良いでしょう。

    よくあるパターンとしては、「バージョンアップ」みたいな響きが社内に伝わり、
    なぜか夢の仕様がいっぱい盛り込まれ、新しい大きなブランチが切られ、
    その間にも既存のコードにガンガン変更が入ることで追随が辛くなり、
    バグを再現する必要とか、どう見てもいらなそうなコードの扱いとかに苦労しながらも、
    上司殿に「バージョン2、期待してるよ」とか言われて・・・

    みたいな夢を見そうです。

    じゃあ、どうすりゃいいんだって話だとは思うのですが、
  • とりあえず、最初の一歩は、これがベストです。
    バカバカしくも現状把握が大事かなぁと思います。

    何がどう問題で、なぜそんな風になっていて、どう直すのがベストで、どういうアプローチで治していくのか。
    みたいなものをバカバカしくも言語化していくのがベストです。
  • 特に複雑に感じる既存のコードを読んでいく際は、手書きでいいので何かメモを残しながら進むのがベターかなぁという感じです。
    アウトプットとしてメモを書いている途中にも意外に理解は進んでいきます。

    あと、大事なことなのですが、一見「難易度高いな」と思うコードでも、たいていの場合、読み切って脳内メモリに乗っちゃうと
    何か読めちゃって、どこがわかりにくかったかわかんなくなってしまうので
    理解が進まないうちからメモを取るのは大事です。
  • その上で、どう変えたいか、理想形についても言語化するととても良いです。
    何が良いかというと、夢が膨らむのと、モチベーションが上がる点です。
  • あとは、できれば、コメントを増やすとベターです。
    とりあえず、コメントはないよりはあった方がマシだと思います。
  • あと、これも努力目標なんですけど、現状把握の調査中に、どう見ても使っていない何かがあったら、細かく消していくと良いです。
  • とまぁ、コード面は、このあとリファクタリング重ねていけば、何とかなるんですけど、
  • 今回のテーマである、DBのテーブル構造がどうもくさっ・・・改善したいな、という時の、ベストな方法なんですけど、
  • ありません。リファクタリングと同じく、少しずつ進めるしかないかなぁと思う感じです。
  • 理想のデータ構造に近づけるのも、一つずつ進むのがよくて、例えば、新しいテーブルを追加する、みたいな、テーブルAにあるカラムhogeをテーブルBに移す、見たいのも一つずつ進んだ方がベターです。
  • プルリ1回、システムメンテナンス1回でリリース!みたいのはやっぱり無理があります。
    私、これで3回くらいゴメンナサイしました。

    特に、運用しているサービスでは、既存のデータをうまく移行する必要があるケースがよくあるのですが
    Update が終わらないとか、まれに、よくあります。
    あとは、そうですね。
    フィクションなんですが、例えば first_name と last_name みたいなカラムをくっつけて name にするみたいなメンテナンスに失敗した時
    「うっそ、元データ残ってないの?マジで?」
    みたいな時も、たまにあります。
  • で、どこまでバラしてリリースしていくのか、みたいな話になるのかと思うですが、例えば、(読み上げる)の4ステップに分けるとかがいいのかなと最近は思ってます。
  • まずは、とりあえず create teble なり、 add_column だけしておくと。
    最終的に rename が、したいのであれば、新しい命名のカラムを add しておき、古いカラムは残しておくとか、しておく。
    そん時にできれば事前にクエリを見ておくとさらにベターかなと
  • 例えば、こんなマイグレーションならこんなクエリだな、みたいのを事前に見ておく心理的安全性の向上に寄与します。
    具体的にどう言うマイグレーションか忘れたんですが、rename したかっただけなのに、drop index が走り、 create index が
    走る、みたいな夢を見た記憶があります。
  • これは直近2年で2回ほど、「メンテナス時間内に終わりませんでした」みたいなことをやらかした上での教訓なんですけど、
    特に RDS とか使っているなら、スナップショットからインスタンスを復元して、クエリの実行時間を計ってみるとか
    やっておくと便利です。
    特に mysql の alter table はたいていの場合、新スキーマの一時テーブルを作り、データの移行が終わってから旧テーブルとの入れ替える
    みたいな挙動をするのですが、リハーサルをやると、alter 中にストレージを使い切って死ぬとかわかって便利です。
    僕はそれで1回、ボスから怒られました。
  • 新しいテーブルなりカラムなりを作ったら、rails のコールバックなりトリガなりで insert とか update とかするたびに
    少しずつデータ移行できるようにすると便利です。
    個人的には Rails 層でコールバックでやる方が運用のしやすさと、
    まだ移行途中だよ、これは技術的投資なんだ、あとで消すんだみたいなコメントを書きやすくて便利です。
  • なお、このループでパターン化できた時には、すでにスキーマ変更は全部終わっていたのですが。
  • 面白くもなんともない結論なんですが、
  • 複数スキーマに対応するコードは難易度高すぎる
  • モデル設計を適当にやるとどうなるのか

    1. 1. 1Confidential Crowdworks, Inc. All Rights Reserved. モデル設計を適当にやると どうなるのか? “Rails におけるデータ構造の変更記録” 2017年6月28日/MedBeer – Rails 5.1での開発について 株式会社クラウドワークス エンジニア 安田篤史
    2. 2. 2Confidential Crowdworks, Inc. All Rights Reserved. [ayasuda]$ cd src/crowdworks [ayasuda]$ bundle exec rails –v Rails 4.2.8
    3. 3. 3Confidential Crowdworks, Inc. All Rights Reserved.
    4. 4. 4Confidential Crowdworks, Inc. All Rights Reserved. 今日は 「Rails x 保守 x 一般論」 をば。
    5. 5. 5Confidential Crowdworks, Inc. All Rights Reserved. モデル設計を適当にやると どうなるのか? “Rails におけるデータ構造の変更記録” 2017年6月28日/MedBeer – Rails 5.1での開発について 株式会社クラウドワークス エンジニア 安田篤史
    6. 6. 6Confidential Crowdworks, Inc. All Rights Reserved. 自己紹介 • やすだあつし/ayasuda • 普通の Rails アプリケーションエンジニア • 「CrowdWorks」を作ったり、壊したり、 直したり。 • Rails 歴はトータル4年半くらい
    7. 7. 7Confidential Crowdworks, Inc. All Rights Reserved. まとめ。 小さく、コツコツやっていこう。
    8. 8. 8Confidential Crowdworks, Inc. All Rights Reserved. 突然ですが質問です。 「ワークアラウンド」って、 悪いものじゃないですよね?
    9. 9. 9Confidential Crowdworks, Inc. All Rights Reserved. 新規に開発したサービスも いつかはリリースして 保守が始まる。
    10. 10. 10Confidential Crowdworks, Inc. All Rights Reserved. 「あっ、バグった。直さなきゃ」 「UI・UX改善したいなぁ」 「ここ、思ったよりも負荷高いからチューニ ングしよう」 「あの機能よく使われてるから、機能の追加 しよう」 なーんてこと、 よくありますよね?
    11. 11. 11Confidential Crowdworks, Inc. All Rights Reserved. 特に、リリース直後とか、忙しい。
    12. 12. 12Confidential Crowdworks, Inc. All Rights Reserved. ワークアラウンドとは、コン ピューターにおいてシステム上で 問題が発生した際の応急処置のこ と。一時しのぎに過ぎないので 後々抜本的対策が必要となる。 -wikipedia
    13. 13. 13Confidential Crowdworks, Inc. All Rights Reserved. ワークアラウンドは・・・ BAD 時間が経つと、負債化する BUT 時間をかけられない時、必要なも のでもある
    14. 14. 14Confidential Crowdworks, Inc. All Rights Reserved. ところで
    15. 15. 15Confidential Crowdworks, Inc. All Rights Reserved. 保守開発で、 データ構造を 手抜きせずに きちんと保ち続けるの 難しいですよね?
    16. 16. 16Confidential Crowdworks, Inc. All Rights Reserved. 例えば、ユーザ間でメッセージが 送れる機能があるとして・・・
    17. 17. 17Confidential Crowdworks, Inc. All Rights Reserved. 返信ができるようにしたとし て・・・ Message#parent_message_id を追加し、親メッセージが設定でき るようにすることで、返信を実現
    18. 18. 18Confidential Crowdworks, Inc. All Rights Reserved. 「返信リストを見る画面」の負荷 対策が必要になったとして・・・ Message は MessageVirtualThread に所属するみたいな
    19. 19. 19Confidential Crowdworks, Inc. All Rights Reserved. スレッドに「お気に入りフラグ」 をつけられるようになったとして・・ お気に入りフラグ専門の テーブルを1件追加
    20. 20. 20Confidential Crowdworks, Inc. All Rights Reserved. 「スレッド一覧画面」の負荷対策 が必要になったとして・・・ sender_user_id と receiver_user_id を 負荷対策として追加
    21. 21. 21Confidential Crowdworks, Inc. All Rights Reserved. リリースから3年半、 そこには・・・
    22. 22. 22Confidential Crowdworks, Inc. All Rights Reserved. UI的にはシンプル。普通の機能。 スレッド一覧があって・・・
    23. 23. 23Confidential Crowdworks, Inc. All Rights Reserved. そしてスレッド詳細があるだけ。
    24. 24. 24Confidential Crowdworks, Inc. All Rights Reserved. さて、想像してみよう。 君は、今、上司から 初めて「リファクタリングしてく れない?」と相談された。
    25. 25. 25Confidential Crowdworks, Inc. All Rights Reserved.
    26. 26. 26Confidential Crowdworks, Inc. All Rights Reserved. (心を穏やかにするために美しい風景 をお楽しみください)
    27. 27. 27Confidential Crowdworks, Inc. All Rights Reserved. ちくしょう!全部作り直してやる!
    28. 28. 28Confidential Crowdworks, Inc. All Rights Reserved. は、やめておきましょう。 セカンドシステム症候群 バグを再現する苦悩 盛り込まれる新仕様 「あ、最近、既存コード 増やしたんですよ」 というか、いるのか? この既存コード。 「バージョン2、期待してるよ」
    29. 29. 29Confidential Crowdworks, Inc. All Rights Reserved. どんな時だって、まずは現状把握 から。  そもそも何がどう問題なのか?  わかりにくいなと感じるコードは、な ぜわかりにくいのか?  どう直したいのか?  ゴールをどこにするのか?
    30. 30. 30Confidential Crowdworks, Inc. All Rights Reserved. メモを書こう。 (手書きだっていい)
    31. 31. 31Confidential Crowdworks, Inc. All Rights Reserved. 理想形も書こう。
    32. 32. 32Confidential Crowdworks, Inc. All Rights Reserved. コメントも増やしていこう
    33. 33. 33Confidential Crowdworks, Inc. All Rights Reserved. 使ってないコードは、見つけたら 消していこう。
    34. 34. 34Confidential Crowdworks, Inc. All Rights Reserved. あとは地道なリファクタリング で、コードは何とかなる。
    35. 35. 35Confidential Crowdworks, Inc. All Rights Reserved. DBどうするん?
    36. 36. 36Confidential Crowdworks, Inc. All Rights Reserved. 小さく、コツコツ。 (銀の弾丸がないことで傷ついた心を穏やかに するために、可愛い猫をお楽しみください)
    37. 37. 37Confidential Crowdworks, Inc. All Rights Reserved. 例えば、中間テーブルを新設する。 スレッドの一覧取 得高速化のために こいつを新設する
    38. 38. 38Confidential Crowdworks, Inc. All Rights Reserved. プルリ1回、メンテ1回でリリース! は、無理がある。  データサイズ的に既存データの移行が終わ らないケースはあり得る  リリースする変更量が多ければ多いほどリ スキーになる  いざって時は戻れるように変更していきま しょう
    39. 39. 39Confidential Crowdworks, Inc. All Rights Reserved. 例えばこんなリリースプラン 1. まずはスキーマ変更だけリリース 2. 新スキーマのデータも作成されるよう にする 3. 既存データのマイグレーションバッチ を用意して、裏で回す 4. データ移行が全部終わったら、新ス キーマを前提としたコードに直す
    40. 40. 40Confidential Crowdworks, Inc. All Rights Reserved. 1. まずスキーマ変更だけリリース • create table だけとか、alter table だけとか • メンテナンスが必要ならメンテナンス • マイグレーションで発行されるクエリも よく見ておくとベター • そして事前に実行時間を調査しておくと ベター
    41. 41. 41Confidential Crowdworks, Inc. All Rights Reserved. class SomeMigration < ActiveRecord::Migration def change rename_column :foos, :old, :new end end log/development.log からクエリを確認しておく #=> ALTER TABLE `foos` CHANGE `old` `new`
    42. 42. 42Confidential Crowdworks, Inc. All Rights Reserved. 事前に実行時間を調べておくと、 いろいろわかって便利。 特に mysql なら ALTER TABLE 時に 一時的なコピーを 作る場合と作らない 場合があるので…
    43. 43. 43Confidential Crowdworks, Inc. All Rights Reserved. 2. 新スキーマのデータも作成され るようにする • Rails ならコールバックを使えば割と簡単に できる • データ移行期間中の一時的なコールバッ ク • コメントはつけておきましょう • DB層でやるならトリガーとか • Rails でやる方が、運用のしやすさからは楽
    44. 44. 44Confidential Crowdworks, Inc. All Rights Reserved. 3. 既存データのマイグレーション バッチを用意して裏で回す • `rails run` コマンド本当に便利 • `find_each` 本当に便利 • なんだかんだでダラダラと1レコードずつ処 理していくが楽ちん • ロギングとエラー処理は気をつけておきま しょう • バッチサーバに「自分にslack通知送るコマ ンド」用意しておくと便利です
    45. 45. 45Confidential Crowdworks, Inc. All Rights Reserved. 4. データ移行が終わったら • データ層は整合性が取れているはず • なので、バリデーション追加したり • データの参照方法を入れ替えたり • コード面での修正を淡々とやっていきま しょう
    46. 46. 46Confidential Crowdworks, Inc. All Rights Reserved. こんな感じのループを少しずつ回 していく。 1. まずはスキーマ変更だけリリース 2. 新スキーマのデータも作成されるよう にする 3. 既存データのマイグレーションバッチ を用意して、裏で回す 4. データ移行が全部終わったら、新ス キーマを前提としたコードに直す
    47. 47. 47Confidential Crowdworks, Inc. All Rights Reserved. やればできるよ! (見積もり3か月、現実1年かかった)
    48. 48. 48Confidential Crowdworks, Inc. All Rights Reserved. まとめ。 小さく、コツコツやっていこう。
    49. 49. 49Confidential Crowdworks, Inc. All Rights Reserved. 小さく、コツコツ。 • 大きく変更は理想なんだけど、難しい • 最初の調査と理想形作ったりするのは 短期間で集中してできる(し、楽し い) • 実装は一歩ずつゆっくりコツコツ、気 長に(飽きないように他の仕事もやっ たほうがいいっす)
    50. 50. 50Confidential Crowdworks, Inc. All Rights Reserved. 反省点 • スキーマ変更ツール導入しておくと少し 楽になるかも • Percona toolkit とか github/gh-ost とか • とはいえ、導入コストが。。。 • やりたいことや、目の前の環境によっ てソリューション異なるなぁ(小並感
    51. 51. 51Confidential Crowdworks, Inc. All Rights Reserved. ご静聴ありがとうございました

    ×