2020年10月14日水曜日

Ruby の connection_pool の挙動を確認してみる

概要

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 件のコメント:

コメントを投稿