はじめに
ruby用DBアクセスライブラリ Sequel を使用した場合、てっきりライブラリ側で使用後のコネクションをクローズしてくれるものだと思ってました。
でも正しい実装を行わないとクローズされないことが判明。
検証
まず以下のようなテーブルを用意
mysql> select * from table01;
+------+--------+--------+
| id | name | status |
+------+--------+--------+
| 1 | 田中 | 0 |
| 2 | 佐藤 | 1 |
+------+--------+--------+
2 rows in set (0.00 sec)
table01 のレコードを検索する @adapter.fetch
ブロック内で10秒、ブロックを抜けたところで10秒 sleep してみる。
sequel_mysql.rb
require 'sequel'
class SequelMysql
def initialize
@adapter = Sequel.mysql2(
:host=>'localhost',
:user=>'root',
:password=>'**',
:database=>'sample',
)
run
end
def run
sql = 'SELECT * FROM table01'
@adapter.fetch(sql) {|r|
# ここのブロックを抜けるとコネクションがクローズされる想定なので、20秒(10秒*2レコード)たったらMySQL実行プロセスから消えるはず
sleep 10
}
sleep 10
end
end
SequelMysql.new
しかし20秒たってもプロセスは消えず。
30秒(ブロック内20秒 + ブロック外10秒)たってようやく消えた。
mysql> show processlist;
+----+------+-----------+--------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+--------+---------+------+-------+------------------+
| 40 | root | localhost | sample | Query | 0 | NULL | show processlist |
| 61 | root | localhost | sample | Sleep | 29 | | NULL |
+----+------+-----------+--------+---------+------+-------+------------------+
2 rows in set (0.00 sec)
mysql> show processlist;
+----+------+-----------+--------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+--------+---------+------+-------+------------------+
| 40 | root | localhost | sample | Query | 0 | NULL | show processlist |
| 61 | root | localhost | sample | Sleep | 30 | | NULL |
+----+------+-----------+--------+---------+------+-------+------------------+
2 rows in set (0.00 sec)
mysql> show processlist;
+----+------+-----------+--------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+--------+---------+------+-------+------------------+
| 40 | root | localhost | sample | Query | 0 | NULL | show processlist |
+----+------+-----------+--------+---------+------+-------+------------------+
1 row in set (0.00 sec)
正しい実装は
コネクション接続処理
@adapter = Sequel.mysql2(
:host=>'localhost',
:user=>'root',
:password=>'**',
:database=>'sample',
)
にブロックを渡し、そこでの処理を抜けるときにクローズしてくれるらしい。1
@adapter = Sequel.mysql2(
:host=>'localhost',
:user=>'root',
:password=>'**',
:database=>'sample',
)) {|db|
# ここを抜けるとクローズ
}
再検証
@adapter.fetch
を廃止。
代わりにコネクション接続処理のブロック内で table01 のレコードを検索する db[:table01].all
を使用。
そしてブロック内で10秒、ブロックを抜けたところで10秒 sleep してみる。
sequel_mysql.rb
require 'sequel'
class SequelMysql
def initialize
@adapter = Sequel.mysql2(
:host=>'localhost',
:user=>'root',
:password=>'1qazxsw2',
:database=>'sample',
) {|db|
p db[:table01].all
# ここのブロックを抜けるとコネクションがクローズされる想定なので、10秒たったらMySQL実行プロセスから消えるはず
sleep 10
}
sleep 10
end
end
SequelMysql.new
結果は、、、10秒たったら正しくプロセスから消えました。
mysql> show processlist;
+----+------+-----------+--------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+--------+---------+------+-------+------------------+
| 40 | root | localhost | sample | Query | 0 | NULL | show processlist |
| 62 | root | localhost | sample | Sleep | 10 | | NULL |
+----+------+-----------+--------+---------+------+-------+------------------+
2 rows in set (0.00 sec)
mysql> show processlist;
+----+------+-----------+--------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+--------+---------+------+-------+------------------+
| 40 | root | localhost | sample | Query | 0 | NULL | show processlist |
+----+------+-----------+--------+---------+------+-------+------------------+
1 row in set (0.01 sec)
If a block is given to these methods, it is passed the opened Database object, which is closed (disconnected) when the block exits, just like a block passed to connect