実録!Railsのはまりポイント10選

29,196 views

Published on

0 Comments
90 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
29,196
On SlideShare
0
From Embeds
0
Number of Embeds
2,046
Actions
Shares
0
Downloads
0
Comments
0
Likes
90
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 実録!Railsのはまりポイント10選

    1. 1. 実録!Railsのはまりポイント10選 2012/03/26 第2回 渋谷Edge Rails勉強会 藤田 武雄 Copyright © Drecom Co., Ltd.
    2. 2. 自己紹介• 藤田 武雄• 株式会社ドリコム• ソーシャルゲーム事業本部• アプリケーションエンジニア • 最近は主に社内ライブラリの開発を 担当 Copyright © Drecom Co., Ltd.
    3. 3. 今日の内容• Railsを使用したソーシャルゲームの開 発時にはまったポイントをご紹介• 実際に社内であった事例です• ほとんどRails3採用後の話 Copyright © Drecom Co., Ltd.
    4. 4. Case1and/or Copyright © Drecom Co., Ltd.
    5. 5. はまり例a = b or c • こういう意味にしたかった a = (b or c) • 実際の動きは (a = b) or c • bの値をaに代入し、それが偽であればc を評価する Copyright © Drecom Co., Ltd.
    6. 6. 演算子の優先順位• and/orは優先順位が一番低い• 対策1: 括弧をつける a = (b or c)• 対策2: &&/¦¦を使う a = b || c Copyright © Drecom Co., Ltd.
    7. 7. ちなみにRails本体は• Contributing to Ruby on Rails• 5.3 Follow the Coding Conventionsに は • Prefer &&/¦¦ over and/or• と書かれている• http://guides.rubyonrails.org/ contributing_to_ruby_on_rails.html Copyright © Drecom Co., Ltd.
    8. 8. Case2scope Copyright © Drecom Co., Ltd.
    9. 9. はまり例scope :started, where(‘start_at <= ?’, Time.now) • どういうことが起こりうるか • 開始時刻になってもキャンペーンがス タートしない • 設定した時刻にバナーが切り替わら ない Copyright © Drecom Co., Ltd.
    10. 10. なぜ?• クラスがロードされた時点でwhereの 中にあるTime.nowの評価が行われる• 結果、アプリケーションサーバを起動 した時間で固定されてしまう Copyright © Drecom Co., Ltd.
    11. 11. lambdaを渡すscope :started, lambda { where(start_at<= ?, Time.now) } • lambdaの中身は都度評価される • edgeではwhereを直接書くやり方は deprecatedとなった • https://github.com/rails/rails/commit/ 0a12a5f8169685915cbb7bf4d0a7bb48 2f7f2fd2 Copyright © Drecom Co., Ltd.
    12. 12. Case3クラスのインスタンス変数 Copyright © Drecom Co., Ltd.
    13. 13. インスタンス変数の遅延初期化@foo ||= bar • 初期化の手間を省くためによく使われ る • barの実行結果をキャッシュする効果も • クラスのインスタンス変数では注意が 必要 Copyright © Drecom Co., Ltd.
    14. 14. よくない例class Baz def self.foo @foo ||= bar endend • barがDBから取得した値だとDBを更新 しても返り値が変化しない! Copyright © Drecom Co., Ltd.
    15. 15. 起こったこと• データを追加したのに反映されない• よくわからんからunicornを再起動• うまくいった• そんなこともありました Copyright © Drecom Co., Ltd.
    16. 16. なぜ?• 通常はオブジェクトのインスタンスの 寿命は1リクエストの間だけ• 一方、クラス自体はリクエストを返し た後も生き続ける• クラスのインスタンス変数に入れる とずっと保持される Copyright © Drecom Co., Ltd.
    17. 17. 対策• むやみにクラスのインスタンス変数に 入れない• 意味をわかっていてやるのであればOK Copyright © Drecom Co., Ltd.
    18. 18. Case4textあふれ Copyright © Drecom Co., Ltd.
    19. 19. データ破損• textカラムにJSONを格納していた• ある日突然データが壊れた! Copyright © Drecom Co., Ltd.
    20. 20. 原因• mysqlのtextは65535バイトまで• それ以上のものを保存すると後ろが切 れるが保存自体エラーにはならない• 読み込むとJSONとしては壊れているの でパースエラー Copyright © Drecom Co., Ltd.
    21. 21. 対策• その1: validates_length_ofを設定• その2: 文字数を減らす• その3: mediumtext(16MBまで)などに 変更する• どれぐらい増えるか見積もりしておけば はみ出ないように設計見直しできるはず• AR::Store(YAML)にも気をつけよう Copyright © Drecom Co., Ltd.
    22. 22. Case5index名長さ制限 Copyright © Drecom Co., Ltd.
    23. 23. index名の長さ• 複数カラムにindexを張った時に長さ制 限に引っかかった• mysqlは64バイトまでしか使えないindex_テーブル名_on_カラム名_and_カラム名_… Copyright © Drecom Co., Ltd.
    24. 24. index名を短くする• デフォルトを変更するモンキーパッチ をあてた• テーブルごとにuniqueになればよいの でテーブル名不要カラム名_and_カラム名_... Copyright © Drecom Co., Ltd.
    25. 25. パッチmodule ActiveRecord module ConnectionAdapters module SchemaStatements def index_name(table_name, options) if Hash === options if options[:column] "#{Array.wrap(options[:column]) * _and_}" elsif options[:name] options[:name] else raise ArgumentError, "You must specify the index name" end else index_name(table_name, :column => options) end end end endend Copyright © Drecom Co., Ltd.
    26. 26. Case6tinyint Copyright © Drecom Co., Ltd.
    27. 27. tinyintの扱いの罠• アプリリリース前にDBAの協力の下、 テーブル構成をチューニングした • int → tinyint• するとアプリの動作がおかしくなった• DBに入っている数値は変わってないの に取ってきた値が全然違う! Copyright © Drecom Co., Ltd.
    28. 28. 原因と対策• ActiveRecordはtinyintのカラムを booleanとして扱う• 対策: boolean以外ではtinyintを使わな い(smallintにするなど) Copyright © Drecom Co., Ltd.
    29. 29. Case7参照分散時のミス Copyright © Drecom Co., Ltd.
    30. 30. data_fabric• 参照分散のためのplugin• 利用者数が増え、masterだけでさばききれな くなったため導入• action単位でaround_filterをかけて振り分け • slaveの情報を元にmaster側を更新しない ようにaction単位にした• アクセスの多いマイページなどはslaveへ逃が す Copyright © Drecom Co., Ltd.
    31. 31. 事故発生!• あるときレプリケーション遅延が起 こってslaveの古い情報を元にmasterに 更新がかかってしまった• データ不整合発生!• そんな実装はしていない認識だった• 調査するとマイページのviewで呼び出 しているhelperからupdateが… Copyright © Drecom Co., Ltd.
    32. 32. 対策• helperからupdateしない!• 参照振り分け用around_filterの外側に before_filterを設定し、update処理部 分を移した Copyright © Drecom Co., Ltd.
    33. 33. Case8ランキング集計 Copyright © Drecom Co., Ltd.
    34. 34. ランキング集計• イベントランキングを毎時集計• 一人ずつselectして順位をupdateする実装 になっていた• 参加ユーザ数が増えると集計時間がどんど ん長くなる• 集計時間が30分を超えた時点で状況を検知• 前月までは大丈夫だったのに… Copyright © Drecom Co., Ltd.
    35. 35. 対応前のサーバ負荷• DBサーバのCPU使用率 Copyright © Drecom Co., Ltd.
    36. 36. 対策• 方針としてはtransaction数を減らす• 裏で新規テーブルを作成して、1000件 ずつbulk insert• 全部入ったらrenameで入れ替え• bulk insertにはactiverecord-import を使用 Copyright © Drecom Co., Ltd.
    37. 37. コード例CREATE TABLE IF NOT EXISTStmp_scores LIKE scores;TRUNCATE TABLE tmp_scores;class TmpScore < ActiveRecord::Base; endTmpScore.import([:user_id, :value, :rank],scores)RENAME TABLE scores TO scores_old,tmp_scores TO scores;DROP TABLE scores_old; Copyright © Drecom Co., Ltd.
    38. 38. 対応後のサーバ負荷 Copyright © Drecom Co., Ltd.
    39. 39. 別の方法• Redisのsorted setを使う• リアルタイムにランキングを更新でき る• 数万人規模でも大丈夫• そのままでは同率順位を表現できない Copyright © Drecom Co., Ltd.
    40. 40. Case9double submit protection Copyright © Drecom Co., Ltd.
    41. 41. double submit protection• 二重送信を検知してくれるplugin• 二重送信を防ぎたいフォーム(orリンク) のあるページでsessionにtokenを保存 し、パラメータにも同じ物をつける• 次のページでは両者を付きあわせて、 session内のtokenを無効にする Copyright © Drecom Co., Ltd.
    42. 42. sessionの罠• Railsのsessionの扱いではまった• レスポンスを返すまでsessionの実体が 更新されない!• 処理に時間がかかるとすり抜けてしま う Copyright © Drecom Co., Ltd.
    43. 43. 処理の流れuser app session memcache request load write ココが問題 response write Copyright © Drecom Co., Ltd.
    44. 44. 対策• レスポンス返すまで待っていられない のでsessionを使うのをやめる• pluginに手を入れてtokenをRedisに保 存するように変更 Copyright © Drecom Co., Ltd.
    45. 45. Case10daemon-spawn + resque Copyright © Drecom Co., Ltd.
    46. 46. daemon-spawn• rubyプログラムをデーモン化するため のライブラリ• resqueのworkerをデーモン化した時に 問題が起こった Copyright © Drecom Co., Ltd.
    47. 47. 何が起こったか• rails runnerで起動していた• redisへのコネクションを張るpluginが あった• forkするときにコネクションがコピー されて全workerで共有されてしまい redisへの接続が不安定に Copyright © Drecom Co., Ltd.
    48. 48. 対策• rails runnerを使わなくした• 自前で必要なものをrequireするスクリ プトに書き換え Copyright © Drecom Co., Ltd.
    49. 49. もうひとつの問題• stopした時に処理中のプロセスが落と される• 処理中ステータスの変なデータが残っ てしまった Copyright © Drecom Co., Ltd.
    50. 50. 原因• daemon-spawnのstop時のシグナルが デフォルトではTERM• rescueはTERMを受け取ると処理中で も即終了してしまう Copyright © Drecom Co., Ltd.
    51. 51. 対策• daemon-spawnの起動スクリプトで明 示的にQUITを指定した • ドキュメントに載ってない • 実装を読んでみて指定できることがわ かった Copyright © Drecom Co., Ltd.
    52. 52. まとめ• 社内ではまった例をご紹介しました• pluginではまったときは実装を追いか けることも必要 Copyright © Drecom Co., Ltd.
    53. 53. ご清聴ありがとうございました Copyright © Drecom Co., Ltd.

    ×