LoginSignup
3
4

More than 5 years have passed since last update.

Sequel + MySQL2でSQLの実行を終えてもコネクションがクローズされない

Last updated at Posted at 2015-10-03

はじめに

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


3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4