概要
Ruby の connection_pool を redis で使って挙動を確認してみました
connection_pool を使うことで意図しない大量の接続が来たときにもデータベース負荷をかけずにアプリ側でコントロールできるようになります
今回はプールのサイズを超えて接続した場合の挙動などを確認します
環境
- macOS 10.15.7
- Ruby 2.7.1p83
- connection_pool 2.2.3
プールサイズ確認用のスクリプト
特に redis に対してコマンドは発行していませんが state_check
のブロック内で cp.available
で残りのプール数を表示しています
(1..3)
の Range の部分でプールのサイズを調整できるのここを変更することで挙動を確認します
require 'connection_pool'
require 'redis'
def state_check(cp)
cp.with do |redis_cli|
puts cp.available
sleep 5
end
end
cp = ConnectionPool.new(size: 5, timeout: 3) {
Redis.new
}
threads = []
(1..3).each do
threads << Thread.new do
state_check(cp)
end
end
threads.each { |t| t.join }
ちなみに上記スクリプトを実行するとプール数が 4, 3, 2 と減って表示されるのが確認できます
なおコマンドが実行されていないので redis-cli の CLIENT LIST
で見ても表示はされません
プールサイズを超えてみる
では実際にプールサイズを超えて実行してみます
先程の Range の (1..3)
を (1..6)
にするだけです
すると Waited 3 sec (ConnectionPool::TimeoutError)
のエラーが発生するのが確認できると思います
これはプールサイズが 5 に対して同時に 6 接続した場合に 1 つは接続待ち状態になります
接続待ち状態になったプールは ConnectionPool.new(size: 5, timeout: 3)
で指定した timeout 秒待ってプールに空きが出なければ上記のエラーを raise しているのです
なので正常に終わらせたいのであれば timeout を 10 などに変更してみましょう
すると今度は raise せずに 4, 3, 2, 1, 0, 4 と表示されて正常終了するはずです
ここまで確認すればわかるのですがプールが開放されるのは with
ブロック内で実行しているコマンドなどが終了してブロックが終了した段階でプールが開放されるようです
with 内で raise された場合はどうなるのか
with 内で意図的に raise してブロックが最後まで行かなかった場合にどうなるのかも確認してみました
確認スクリプトを少し修正しています
require 'connection_pool'
require 'redis'
def state_check(cp, i)
cp.with do |redis_cli|
begin
puts cp.available
sleep 5
raise Exception
rescue Exception => e
puts "error on #{i}"
end
redis_cli.set("result#{i}", 0)
end
end
Thread.report_on_exception = false
cp = ConnectionPool.new(size: 5, timeout: 10) {
Redis.new
}
threads = []
(1..6).each do |i|
threads << Thread.new do
state_check(cp, i)
end
end
threads.each { |t| t.join }
puts "end"
スクリプトが悪い可能性もありますが一応これで 6 つのプールの処理がすべて正常に終了することが確認できました
with
のブロックが何かしらの処理で止まったりしてもブロックを抜けさえすればプールは開放されるようです
最後に
connection_pool を使って Redis に接続できるクライアント数の制御をしてみました
プールサイズを超えた場合はタイムアウト秒待ちそれでもプールに空きがない場合はエラーになることが確認できました
0 件のコメント:
コメントを投稿