Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

33,565 views

Published on

  • Be the first to comment

実録!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.

×