2008年6月3日火曜日

Multiple Database Connection for Model in Rails(My solution)

In my project, I must talk with multiple database for Model(ex. Student). Unfortunately, Models in Rails can't connect with multiple database by default. After diving in to web, I found nice solution to solve this problem: Magic Multi-Connections plugin. But as written in top page in the project, Models with Magic Multi-Connections has problem with association(one-to-many, many-to-many). I was newbie for ruby, and couldn't understand source code of Magic Multi-Connections for all. So, I gave up using, fixing it and wrote some code for this problem. Here was my idea.

0. Model to use multiple connection

class Students
end

1. Define connection hosts in yaml.

config/students.yml

students:
 conn1: 192.168.1.101
 conn2: 192.168.1.102
 conn3: 192.168.1.103
 conn4: 192.168.1.104
 conn5: 192.168.1.105


2. Load yaml in environment.rb

$students_conn_hosts = YAML.load_file(File.join(RAILS_ROOT, "config/students.yml"))


3. Define Model classes by using "eval".

class StudentAlias

 @@students_conns = []

 1.upto(5) do |i|
  @@students_conns << eval("""
   class StudentModel#{i} <>
    establish_connection( :adapter => 'mysql',
     :host => '#{$students_conn_hosts["students"]["conn" + i.to_s]}',
     :database => 'hoge',
     :encoding => 'utf8',
     :username => 'db_user_name',
     :password => '',
     :timeout => '5000',
     :socket => 'your_socket_name
    )
    set_table_name 'students'
   end
   StudentModel#{i}
  """)
 end

 # select one connection by rand, for example
 def StudentAlias.get_connection
  @@student_conns[rand(@@student_conns.size)]
 end

end


4. Call class method in StudentAlias for Student Model to connect one of databases

StudentAlias.get_connection # => StudentAlias::StudentX(... attribute definitions of student table ...)


Not so bad?

2008年5月31日土曜日

Google favicon has changed!

I noticed the favicon on google.com(and google.co.jp) has changed today. It seems to be 'web2.0ly' ?

Anyway I was fully confused when I searched google search result pages in many tabs on Firefox :-(

Don't use backgroundDRb in product!

Recently I used backgrounDRb for batch-like jobs in Rails application.

It worked fine before I noticed the used memory on process 'ruby script/backgroundrb start' increased day by day. I googled 'backgroundrb memory leak', and found some pages like this. Unfortunately backgrounDRb has some problems I met with, and these are not solved in latest version.

So, use ruby script and crond instead of backgrounDRb for long time use while those problems are solved, or fix it on your self and please commit your great code! :p

2008年5月5日月曜日

backgrounDRbのworkerメソッドはThread Safeでした

【状況】
apache + mod_proxy_balancer + 複数のmongrelインスタンスが待機しており、mod_proxy_balancerによってリクエストが各mongrelインスタンスに振り分けられる。

【疑問】
あるControllerのaction(worker_test)内でMiddleMan.worker(:hoge_worker).hugaを呼び出す記述を行った場合、同時にworker_testへリクエストが投げられた際にhugaは排他的に呼ばれるのか?

【実験準備】
以下のようなController, Worker, tmpファイルを用意した。

- HogeController

def worker_test
 MiddleMan.worker(:hoge_worker).huga
 render :layout => false
end


- HogeWorker

def huga
 count = 0
 open('/path-to-tmpfile', 'r') do {|f| count = f.read.to_i + 1 }
 open('/path-to-tmpfile', 'w') do {|f| f.write(count) }
end

- /path-to-tmpfile
0


以下のコマンドをターミナルから実行した。

$ ab -n 100 -c 100 http://path-to-worker-test

【結果】
Server Software: Mongrel
Server Hostname: localhost
Server Port: 80

Document Path: /path-to-worker_test
Document Length: 639 bytes

Concurrency Level: 100
Time taken for tests: 10.275617 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Non-2xx responses: 100
Total transferred: 107900 bytes
HTML transferred: 63900 bytes
Requests per second: 9.73 [#/sec] (mean)
Time per request: 10275.617 [ms] (mean)
Time per request: 102.756 [ms] (mean, across all concurrent requests)
Transfer rate: 10.22 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 2 3 0.9 3 5
Processing: 584 5098 3022.2 4912 10268
Waiting: 583 5058 3002.2 4718 10268
Total: 588 5101 3021.3 4915 10270

Percentage of the requests served within a certain time (ms)
50% 4915
66% 6754
75% 7984
80% 8561
90% 9608
95% 10030
98% 10183
99% 10270
100% 10270 (longest request)


$ less /path-to-tmpfile
100

ちゃんと100になってた。ここまできてvendor/plugin/backgroundrb以下のファイルを漁ってみると、
  1. ruby script/backgroundrb startでBackgrounDRb::MasterProxy.new()してdaemonを作成
  2. vendor/plugins/backgroundrb/server/lib/master_worker.rbのMasterProxyにload_and_invokeなる関数ハケーン。「#method will load the worker and invoke worker method」と書いてあるし、workerに定義した関数を呼び出している箇所はここで間違いなさそう。
  3. worker.send_requestで関数を呼び出しているようなので、vendor/plugins/backgroundrb/lib/backgroundrb.rbを見てみる
  4. send_request内で@mutex.synchronizeしてた。
【結論】
backgrounDRbのworkerメソッドはThread Safeでした。つまり、例えばworker内でファイルを開いてどうのこうのして、という処理を行ったとしても同時に同じファイルへアクセスすることはないのでファイルが壊れる心配はないと。

【追記】
何度もテストを重ねてみると、稀にnオプションで指定した以上の数値がtmpfileに現れる現象が確認されたが、この現象が起こった際には必ずapache_access_logの行数もその分だけ増えていたので、apacheに対してnオプションで指定した以上のリクエストが投げられていたことになる。恐らくサーバーが高負荷状態にあったので何度かabコマンドによるリクエストが途中でやり直された?可能性が高い。これはRailsとかbackgrounDRbの責任ではないよね。

2008年4月25日金曜日

backgroundDRbはmission criticalな箇所では使えないかも

あるサービスをテストサーバーにデプロイしていたのだけれども、backgroundDRbのタスクが停止していたようなのでログを見てみると以下のエラーが吐かれていた。
/usr/lib/ruby/1.8/timeout.rb:54:in `rbuf_fill': execution expired (Timeout::Error)
from /usr/lib/ruby/1.8/timeout.rb:56:in `timeout'
from /usr/lib/ruby/1.8/timeout.rb:76:in `timeout'
from /usr/lib/ruby/1.8/net/protocol.rb:132:in `rbuf_fill'
from /usr/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
from /usr/lib/ruby/1.8/net/protocol.rb:126:in `readline'
from /usr/lib/ruby/1.8/net/http.rb:2029:in `read_status_line'
from /usr/lib/ruby/1.8/net/http.rb:2018:in `read_new'
from /usr/lib/ruby/1.8/net/http.rb:1059:in `request'
... 27 levels...
from /usr/lib/ruby/gems/1.8/gems/packet-0.1.5/lib/packet/packet_master.rb:20:in `run'
from /deploy/seo/current/vendor/plugins/backgroundrb/server/lib/master_worker.rb:166:in `initialize'
from /deploy/seo/current/script/backgroundrb:42:in `new'
from /deploy/seo/current/script/backgroundrb:42

まぁよくあるNet::HTTPがタイムアウトした際のエラーでした。このエラーが起こった以降のログが取られていないことを考えると、daemonプロセスが落ちたのか?と思ったけどpsで見てみるとちゃんと動いてる。ということは、一度エラーが起こった場合、backgroundDRbに登録されているworkerは二度と動かないということですか。そうですか。

2008年4月21日月曜日

Ruby on Railsのサイトドメインが切れてるんだけど


エエエェェェ(´Д`)ェェェエエエ

追記:4/21 12:12現在、回復した模様。

2008年4月13日日曜日

どうやら私は裁判員を辞退できるみたいです

裁判員制度:「辞退」に配慮するケースまとめる 最高裁

> ▽情報処理SE=システムトラブル発生時に対応が求められる
すごく当てはまってます。本当にありがとうございました。

でも、万が一自分が宇宙から飛来した隕石に当たって即死しても何事もなく稼働+メンテナンスができるシステムを作っていく必要があるんだろうなぁ。Capistranoとpuppet、それからnagiosでも使えばある程度は実現できるか。