猫の魔法

主にruby系の技術メモを記載

個人的メモ:Rails AntiPatterns(17)

Solution: Move Processing into Background Jobs

  • 処理時間がwebでの応答に不適切なくらい掛かる場合はcronやキューイングといった。バックグラウンドJobの仕組みを検討した方がいい

  • cronの場合スクリプトを別に作ってそれを動かす。

    • 例えば、テーブルのカウントをするのに毎回3分かかるような処理があるなら、事情が許すならcronで30分置きにカウントした結果を別のテーブルに格納する方がパフォーマンスの向上に繋がる。

    • cronは簡単だが、対象をcronスクリプト側で特定しなければならない。可能性であればキューイングを検討する方がいい

  • キューイングは画面からcsvをダウンロードする機能のような、バックグラウンドで纏まった処理をしなければならない物に向いている。

  • キューイングの機能としてはdelayed_jobとResqueが有名。

    • Redisを使っているのでなければdelayed_jobを使った方がいい。
      • Rails 4.2からはActive Jobがあるので、普通はそっちを使う。
  • キューイングやcronはは便利な機能だが、使い過ぎるとシステムが複雑になる。節度を持って使う事

    • ★ 関係ないが「exercise restraint」で「抑止力を働かす」というのが面白い

Chapter9 Databasesの手間まで読了

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(16)

忙し過ぎて時間が空いてしまった。

CHAPTER 8 Scaling and Deploying

AntiPattern: Painful Performance

  • 単純なパフォーマンスの問題を防ぐ為の事例を紹介する。

Solution: Don’t Do in Ruby What You Can Do in SQL

  • RubySQLで出来る事をやらせないようにする
  • レコードの総件数を求める方法は3つ考えられるが、どれも裏側の動きが異なる
@article.comments.count 
@article.comments.length
@article.comments.size
  • 一つ目の例は、DBのcount関数を実行する。
  • 二つ目の例は、lengthがActiveRecordの関連で無い為、すべてのレコードをDBから取得する
  • 三つ目の例はすでにレコードがロードされていればそれを使い、そうでない場合はDBのcountを使う。
  • ケースに応じて使い分ける事が重要。

  • SQLを上手く使えていないパターンとして以下の2パターンがある。

    • SQLで実現する方法を知っているがRubyで書いてしまう。
      • 例えば下記のような例(コードは抜粋)
@account = Account.find(3) 
@users = @account.users.sort { |a,b| a.name.downcase <=> b.name.downcase }.first(5)
  • 上記のコードよりも下のコードの方が、処理をよりDBに移譲できる為パフォーマンスを高められる
@users = @account.users.order('LCASE(name)').limit(5)
  • SQLで実現する方法を知らない為Rubyで書いてしまう。
    • 例えば下記のような例(コードは抜粋)
class User < ActiveRecord::Base
 has_many :comments has_many :articles, :through => :comments

 def collaborators 
   articles.collect { |a| a.users }.flatten.uniq.reject {|u| u == self } 
 end 
end
  • 上記よりも、下記の方がより処理をDBに任せる事が出来る。
    • このパターンはflattenを使うような処理になってしまっている場合に置き換えが出来る事が多い
    • ★?の部分は、順番に配列の第2,第3要素が入る。
class User < ActiveRecord::Base
 has_many :comments
 has_many :articles, :through => :comments
 
 def collaborators User.select("DISTINCT users.*").
        joins(:comments => [:user, {:article => :comments}]).
           where(["articles.id in ? AND users.id != ?", self.article_ids, self.id]) 
 end 
end

Solution: Move Processing into Background jobsの前まで読了。

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(15)

CHAPTER 8 Scaling and Deploying

AntiPattern: Sluggish SQL

Solution: Reassess Your Domain Model

  • 高度に正規化されたテーブルはテーブル間の結合によってパフォーマンスの問題を起こしやすい。
  • 単純に結合させるのではなく必要な要素をrails側で取得するようにするとパフォーマンスを改善する事が出来る。
    • ★UserがPrefとCityと関連がある場合、下記のように取る事で負荷が減らせる場合がある。
search_pref = Pref.find_by_name('Kanagawa') 
search_city = City.find_by_name('Yokohama')
User.where(pref: search_pref,city: search_city)
  • 大きなサイズのテーブルになる事が分かっているなら、テーブル自体非正規化した方がパフォーマンスを向上出来る。

AntiPattern:Painful Performanceの前まで読了。

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(14)

CHAPTER 8 Scaling and Deploying

AntiPattern: Sluggish SQL

  • パフォーマンス最もネックになるのはDB。indexの貼り方と副問い合わせを使った方がいいシチュエーション、ドメインモデルを使った方がいいシチュエーションを考える。

Solution: Add Indexes

  • 主キーは自動的にインデックスが貼られる
  • 外部キーについても基本は自動的にインデックスが貼られる。
  • 検索で複数のカラムを主キーとして扱うような場合は、複合indexを貼ることを検討する。

    • ★内容的には1対多の多側テーブルが他にも多側になる外部キー存在する場合の話をしているが、途中から複合indexの話に読めた。
  • uniqu識別子が付いているカラムにはインデックスをつけると早くなる。

  • STIを使ってる場合、typeカラムには継承先のクラス名が入る。このtypeにはインデックスを貼ったほうがいい。
    • STIは初めて知った。オブジェクトの継承関係をDBにもたす事で、継承元のcontrollerのみ書けばよくなる仕組みのよう*1
  • その他のカラムも検索とかで使用しているならインデックスを貼ってみるとよい。

    • ★後でも書いてあるが、インデックスを貼りまくるとupdateやinsertが極端に遅くなる。特にローが多いテーブルはインデックスの追加に慎重になった方がいいというのが個人的な考え。
  • 状態を管理するカラム、フラグ的なカラムも場合によってはインデックスを貼った方がいい。

    • ★普通インデックスはインデックスを貼るカラムの値がより離散していた方が効果が高い。なのでフラグカラムにインデックスを貼るとか正気かと思ったが、そのカラムが検索に使われていて、圧倒的に少ない方の値ならという話だった(それでも個人的にはSQL的にアンチパターンだと思うが)
  • created_atのような日付カラムもそれを使ってorder byをしていて且つwhere句に使ってるならインデックスを貼るといい
  • インデックスを貼りすぎるとupdateやinsertが遅くなる
  • どのカラムにindexをつけるかは、継続的に調査する必要がある。railsプラグインであるLimerick Rakeなどを使うか、DB自体の機能で遅いクエリを取得する機能を使うと良い。

Solution: Reassess Your Domain Modelの手前まで読了

### 関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(13)

今日はあまり進まず。

CHAPTER 8 Scaling and Deploying

AntiPattern: Disappearing Assets

  • Capistranoなどのデプロイツールを使ってアプリをリリースする際にユーザの添付ファイルなど、関係ないファイルもデプロイ対象になる
  • Capisrranoの設定で回避出来る。

Solution: Make Use of the System Directory

  • CapistranoのsharedディレクトリはRAILS_ROOT/public/systemを向いているのでそこにファイルを配置すればよい。
    • ★意味が分かってない。そもそもCapistranoの2から3で大きな変更があったようなので今も通用する話なのか要調査。

AntiPattern: Sluggish SQLの手前まで読了

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(12)

CHAPTER 8 Scaling and Deploying

  • ソフト作った後保守にも非常に労力が掛かる。
  • この章ではスケーリングについての問題を考える

AntiPattern: Scaling Roadblocks

  • 不必要なスケーリングを考慮する必要はないが、簡単に出来る方法でスケーリングを確保する方法を考える

Solution: Build to Scale from the Start

  • データの保管は設定ファイルで簡単にスケーリング出来るよい例
  • 添付ファイル取り扱うPaperclipというプラグインは保存場所としてクラウドを設定出来る
  • linuxファイルシステムはその構造上32000までのファイルしか1つのディレクトリに入れられない(ext3の話。ext4や、xfsはこの上限に当たらない)
    • ファイル名の1文字をディレクトリに当てることで上記の制約回避出来る。これはPaperclipの設定で設定する事が出来る。(例: 0901というファイルなら0/9/0/の下に1というファイルを作る)
      • ディレクトリ毎に保存するストレージを変えることでスケーラビリティ確保できる。

AntiPattern: Disappearing Assetsの手前まで読了

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(11)

CHAPTER7 Testing

AntiPattern: Unprotected Jewels

  • プラグインやgemはよくテストされている事が重要だが、実際にプラグインや、gemを作る際にテストを作るのは簡単ではない。
    • gemやプラグインは不特定多数の物から呼ばれるため、テストを行う際にテスト用の別アプリケーションを立ててテストするのが簡単だが、保守性の面から考えるとよくない
  • 作ったgemやプラグインrailsとどれだけ疎結合になっているかで、3種類のテスト戦略がある。

Solution: Write Normal Unit Tests Without Rails

  • 完全にrailsから分離されているgemやプラグインはテスト自体をその機能の単体テストに閉じれる。
    • 出来ればこの形を目指せるとよい

Solution: Load Only the Parts of Rails You Need

  • railsの機能の一部使用しているgemやプラグインは必要なrailsのgemのみを使用するようにする。
    • ActiveRecodeが必要なら、それだけをtest_helper.rbに含める

Solution: Break Out the Atom Bomb

  • Rails全体を使用するgemやプラグインの場合は、上記の解決策は取れない。
  • そのような場合はtest以下にテスト用のrailsアプリケーションを構築する
    • test以下のrailsアプリケーションはプラグインの実態をそのままでは見れないので、environment.rb内にconfig.plugin_pathsを使ってpluginの場所指定してあげる
      • ★pluginの場合はそうだがgemの場合はどうするのか?

C HAPTER 8 Scaling and Deployingの手前まで読了

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(一覧)

記事が増えてきたので目次用のページを作成

*個人的メモ:Rails AntiPatterns(21):最後 - 猫の魔法

個人的メモ:Rails AntiPatterns(10)

CHAPTER7 Testing

AntiPattern: Mock Suffocation

Solution: Tell, Don’t Ask

  • スタブが多く出来る物はスタブになっている側の機能が特定の機能を実行するのに多くの質問をしている可能性がある。
  • 質問として分かれてしまっているmethodを意味のある単位に集約したmethodを用意することで、スタブ数を少なくできる
    • ★stubってなんだと思って調べたらRspecの機能だった。*1

AntiPattern: Untested Rake

  • rakeタスクをテストしようとする際に、一番簡単な方法はテストでシステムコマンドを発行すること
  • しかしながら、そのようなやり方はテストの複雑化(システムコマンドで作成されたファイルやDB上のデータのクリーンアップ)を招く。

    Solution: Extract to a Class Method

  • テスト用のmodelクラスを作り、その中でrakeタスクを処理するタスク入れてあげるようにする。
  • そうすることで通常のmodelを用いたテストと同じようにテストを行う事が出来る。
    • ★この手法はDSLを使うようなアプリのテストだったら適用出来そう

AntiPattern: Unprotected Jewelsの手前まで読了。

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法

個人的メモ:Rails AntiPatterns(9)

最近仕事が忙しく読む速度が落ちがち。

CHAPTER7 Testing

AntiPattern: Lost in Isolation

  • モックを使いすぎると改修の際にテストが漏れる事がある
  • 特にモジュール間の結合点は今回の事象が起こりやすい
    • ★あんまり理解出来てないが、前後の話を統合すると結合テスト以上の統合テストをする必要があると言っているように思える。

Solution: Watch Your Integration Points

  • テストフレームワークにCucumberを使ってユーザーストーリの視点からのテストを行う
  • ★Cucumberを後で調べる
  • なおこの試みは既存のコードに対してやると、むしろ状況を悪化させる可能性がある。

AntiPattern: Mock Suffocation

  • モックやスタブが多様されているプロジェクトは、その設計自体問題がある可能性がある。

Solution: Tell, Don’t Askの手前まで読了

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法