ラベル gem の投稿を表示しています。 すべての投稿を表示
ラベル gem の投稿を表示しています。 すべての投稿を表示

2014年10月24日金曜日

multi_dbを使ってレプリケーションのDBに接続を振り分ける方法

最初、レプリケーションされた、DB環境でのマスターDBトスレーブDBの接続の振り分けにOctopusを使おうか考えて色々と調べてみましたが、
スレーブでトラブルが起きた時にも、スレーブDBを参照しにいこうとしてしまうようなので、
それは、いかがなものかと思い、こちらの、Git Hub - multi_dbに切り替えました。
このmulti_dbは、スレーブが停止しても、マスターを見るように切り替わってくれるので、その点安心です。
それでは、設定方法を説明していきます。

database.ymlにスレーブの設定追加

スレーブ用のDBの接続情報をdatabase.ymlに追加します。
追加する、スレーブのdatabase.ymlでの名前の命名ルールは以下のようにします。
[environment]_slave_database[_optional_name]
例えば、production環境の1番スレーブDBの場合は、以下のようにします。
production_slave_database_1
[environment]は、下記のいずれか
  • development
  • staging
  • production
[_optional_name] は、数字がわかりやすいとおもいます。また、スレーブ一台構成の時などは、省略も可能です。
  • _1
  • _2
実際の、database.ymlは以下のようになります。
development:
  adapter: postgresql
  encoding: utf8
  database: development_master
  pool: 5
  username: postgres
  password: <%= ENV['DB_PASSWORD'] %>
  port: 5432
  host: <%= ENV['DB_HOST'] %>
  timeout: 5000

production_slave_database: # that would be a slave
  adapter: postgresql
  encoding: utf8
  database: development_slave
  pool: 5
  username: postgres
  password: <%= ENV['DB_PASSWORD'] %>
  port: 5432
  host: <%= ENV['DB_HOST'] %>
  timeout: 5000

初期化処理の追加

passenger未使用の場合

config/environments/[environment].rbのconfig.after_initializeブロックの中にMultiDb::ConnectionProxy.setup! を追加。
config.after_initialize do
  MultiDb::ConnectionProxy.setup!
end
[environment]は、下記のいずれか
  • development
  • staging
  • production

passenger使用の場合

config/initializers/connection_proxy.rbに以下を追加
if defined?(PhusionPassenger)
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
    if forked
      # ... set MultiDb configuration options, if any ...
      MultiDb::ConnectionProxy.setup!
    end
  end
else # not using passenger (e.g. development/testing)
  # ... set MultiDb configuration options, if any ...
  MultiDb::ConnectionProxy.setup!
end
上記を追加した場合、config/environments/[environment].rbの設定は不要になります。

マスターのみを参照するモデルの指定

UserモデルとBookモデルはマスターしか参照しないという設定にする場合、下記のようにmaster_modelsにモデル名の配列を指定する。
MultiDb::ConnectionProxy.master_models = ['User', 'Book']
MultiDb::ConnectionProxy.setup!

動作確認

これまでの設定で、rails c で
 Blog.find(1)
とすると、下記のような結果が帰ってきます。
[MULTIDB] hijacking connection for Blog
Blog Load (6.6ms)  SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = $1 LIMIT 1  [["id", 1]]

スレーブの重み付け

複数スレーブ環境の場合、各スレーブに対して重み付けもできるようです。
production_slave_database_1:
  <<: *postgres
  host: my.slavedb_1
  weight: 1

production_slave_database_2:
  <<: *postgres
  host: my.slavedb_2
  weight: 10

production_slave_database_3:
  <<: *postgres
  host: my.slavedb_3
  weight: 5
上記のようにそれぞれに、weightを設定した場合、
リクエスト数が以下のようになる様です。
production_slave_database_1 6302クエリ
production_slave_database_2 62764クエリ
production_slave_database_3 30934クエリ
※上記実際に試していないので、公式ドキュメンとそのままです。
AWSでインスタンスのスペックの違いがあるときなんかは、こちらの設定が有効活用できるような気がします。

その他

テスト環境

テスト環境は、Octopasの時も、書きましたが、読み取り専用のユーザーを作成して簡易的にテストしても良いかと思います。
詳しくは、Octopusを使ってレプリケーションのDBに接続を振り分ける方法を参照してください。
本来であれば、ちゃんとレプリケーション環境で試すのが望ましいとおもいますが、、、、

Octopusを使ってレプリケーションのDBに接続を振り分ける方法

DBがレプリケーションの構成になっている場合、更新系のクエリーをマスターDBへ、参照系のクエリーをスレーブDBへクエリを投げるようして、DBの負荷分散をさせる方法です。
RailsのGemでそれを、やってくれるGem octopus (Git Hub - tchandy/octopus) があるので、これを使った実装は説明します。

インストール

gemのインストールは、Gemfileに以下を追加して、bundle install
Gemfile
# DB master slave振り分け
# https://github.com/tchandy/octopus
gem 'ar-octopus'

DBの接続先の設定

接続先の設定は、config/shards.ymlに行って行きます。
公式のConfigファイルの設定に関するWikiはこちら

全クエリの振り分け

更新のクエリは、マスターDB、参照のクエリはスレーブDBと全てのクエリに対して設定を行う場合は、下記。
octopus:
  replicated: true
  environments:
    - development
    - staging
    - production
  development:
   development_slave:
      database: development_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  staging:
    staging_slave1:
      database: staging_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  production:
    production_slave1:
      database: production_slave1
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000
    production_slave2:
      database: production_slave2
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

モデル毎の振り分け

モデル毎に、マスター、スレーブを使うかを決める場合は、こちら。
fully_replicated: false を指定する。
octopus:
  replicated: true
  fully_replicated: false
  environments:
    - development
    - staging
    - production
  development:
   development_slave1:
      database: development_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  staging:
    staging_slave1:
      database: staging_slave_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000

  production:
    production_slave1:
      database: production_slave1
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000
    production_slave2:
      database: production_slave2
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: postgres
      password: password
      port: 5432
      host: localhost
      timeout: 5000
config/shards.ymlの他に、modeleから、replicated_model() メソッドを呼び出します。
#This class is replicated, writes to master and reads to slave.
class Cat < ActiveRecord::Base
  replicated_model()
end

接続の確認

上記まで、設定が完了していれば、rails c で
Cat.find(1)
とすれば、 下記のような、ログがとれるはずです。
[Shard: development_slave1]  Cat Load (0.4ms)  SELECT "cats".* FROM "cats" WHERE "cats"."id" = $1 LIMIT 1  [["id", 1]]

直接DBを選択する場合

usingメソッドを呼び出します。

マスターに投げる場合

Cat.using(:master).find(1)

スレーブに投げる場合

Cat.using(:development_slave1).find(1)
または、ブロックを使って。
Octopus.using(:development_slave1) do 
 Cat.count
end

テスト環境の構築

テスト環境にもマスターDB、スレーブDBを作るのが環境的にはベストかもしれませんが、状況によって作れないこともあると思うので、
そんな時は、読み取り専用のユーザーを作るの楽かと思います。

読み取り専用ユーザーの作成

postgresでの読み取り専用ロールの作り方は以下
CREATE ROLE read_only_user LOGIN REPLICATION PASSWORD 'password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only_user;

shards.ymlの設定変更

下記のように、databaseをマスターDBに向けて、usernameをread_only_userに変更すれば、OKです。
octopus:
  replicated: true
  fully_replicated: false
  environments:
    - development
    - staging
    - production
  development:
   development_slave:
      database: development_master_db
      adapter: postgresql
      encoding: utf8
      pool: 5
      username: read_only_user
      password: password
      port: 5432
      host: localhost
      timeout: 5000

障害発生時の対応

もし、マスターDBに以上が発生した場合などに、取り合えず、振り分け処理を停止したい時は - production をコメントアウトするのが有効かと思います。
octopus:
  replicated: true
  fully_replicated: false
  environments:
#    - development
#    - staging
#    - production
  development:
   development_slave:
最低限、productionのみコメントにすれば、OKかと思います。
また、開発環境やステージング環境で、レプリケーション不要なんて時もコメントアウトしておけば、振り分け処理はおこなわれません。

2014年10月13日月曜日

bundle installのtiny_tdsのエラー対処

bundle installしたら、tiny_tdsのインストールで以下の様に怒られました
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /Users/name/.rvm/rubies/ruby-1.9.3-p327/bin/ruby extconf.rb
checking for iconv_open() in iconv.h... no
checking for iconv_open() in -liconv... yes
checking for sybfront.h... no
-----
freetds is missing.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/Users/name/.rvm/rubies/ruby-1.9.3-p327/bin/ruby
    --enable-lookup
    --disable-lookup
    --with-iconv-dir
    --without-iconv-dir
    --with-iconv-include
    --without-iconv-include=${iconv-dir}/include
    --with-iconv-lib
    --without-iconv-lib=${iconv-dir}/lib
    --with-freetds-dir
    --without-freetds-dir
    --with-freetds-include
    --without-freetds-include=${freetds-dir}/include
    --with-freetds-lib
    --without-freetds-lib=${freetds-dir}/lib
    --with-iconvlib
    --without-iconvlib

extconf failed, exit code 1

Gem files will remain installed in /Users/name/.rvm/gems/ruby-1.9.3-p327@restir/gems/tiny_tds-0.6.2 for inspection.
Results logged to /Users/name/.rvm/gems/ruby-1.9.3-p327@restir/extensions/x86_64-darwin-11/1.9.1/tiny_tds-0.6.2/gem_make.out
An error occurred while installing tiny_tds (0.6.2), and Bundler cannot continue.
Make sure that `gem install tiny_tds -v '0.6.2'` succeeds before bundling.
どうやら、freetdsのインストールが必要らしいです。
以下のようにインストールします。
sudo brew install freetds
それからもう一度、bundle install

公式サイト

git-hub rails-sqlserver/tiny_tdsはこちら

bundle installのrmagickでエラー対処

bundle installでrmagickのインストールでエラー発生。
brewでimagemagickを入れ直したりしたけど、全然改善せずで、
以下の方法でimagemagickをインストールし直してから
bundle installしたら成功しました。
cd /tmp
curl -OL ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
tar -xzf ImageMagick.tar.gz
cd ImageMagick-6.8.9-8/
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --disable-openmp --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install

2014年10月1日水曜日

Railsのcron管理 Gem wheneverの導入方法

Railsでcron管理に便利なgem wheneverの導入方法です。
公式のgitHubはこちら

gemのInstall

gemfileに以下を追加して、bundle installを実行
Gemfile
# cron
# https://github.com/javan/whenever
gem 'whenever', :require => false

設定ファイル(schedule.rb)の作成

projectのディレクトリに移動してから、設定ファイル生成コマンドを実行
$ cd /apps/[my-project]
$ wheneverize .
そうすると
./config/schedule.rb
が作成されます。

schedule.rbの作成の設定

呼び出し可能な job type

wheneverでは、下記の4つのjob typeをschedule.rbに設定することができます。
job type メソッド 使用例
bashコマンド command command “/usr/bin/some_great_command”
rakeタスク rake rake “some:great:rake:task”
Railsのメソッド runner runner “MyModel.some_method”
scriptファイル script script “script/my-script.sh”

時間の設定

3時間毎
every 3.hours do
  rake "my:rake:task"
end
毎日 AM4:30に実行
every 1.day, :at => '4:30 am' do
  rake "my:rake:task"
end
1時間毎に実行
every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
  runner "SomeModel.ladeeda"
end
日曜日pm12時に実行
every :sunday, :at => '12pm' do # Use any day of the week or :weekend, :weekday
  runner "Task.do_something_great"
end
27~31日の00:00に実行
every '0 0 27-31 * *' do
  command "echo 'you can use raw cron syntax too'"
end
このcron(分,時,日,月,曜日)の書き方が慣れているので普段はこれを使っています。

crontabへの反映,削除

上記で設定した内容をcrontabへ反映したり、削除させるための、下記コマンドが用意されています。

crontabへの追加

whenever -w

crontabからの削除

whenever -c

statistics