ADMIN PINGとは
前提
Railsのバージョン
https://github.com/rails/rails/tree/v4.2.6
ADMIN PINGの正体
ADMIN PINGというコマンドはありません。
実態はMySQLのCOM_PINGというコマンドになります。
https://dev.mysql.com/doc/refman/5.6/ja/mysql-ping.html
pt-query-digestがCOM_PINGのコマンドを ADMIN PINGと見せています。
クエリではないので、単純にtcpdumpしていてもADMIN PINGのコマンドを確認することはできません
(パケットの内容を16進表示して理解できる人には見えるかもしれません)
RubyでADMIN PINGを送っている場所
mysql2にpingを送るMysql2::Client#pingメソッドが用意されています。
pingがbool値を返します。(pingが通ればtrue)
RailsでMysql2::Client#pingを呼び出しているのはActiveRecord::ConnectionAdapters::Mysql2Adapter#active?のみになります。
ここまでで実態はつかめたかと
実際にActiveRecord::ConnectionAdapters::Mysql2Adapter#active?が呼ばれるまでの経由
これはRailsがMySQLとのコネクションを確立するまでの流れを追ってみます。
http://blog.livedoor.jp/sonots/archives/38797925.html sonotsさんのblogがわかりやすいです。
Railsの立ち上げ時
ActiveRecord::Base#establish_connectionが呼ばれるが、この時点ではMySQLサーバーにコネクションは貼られない。
モデルクラスがロードされて、評価されると、pk_cache_adapterがPKを取得するためにMySQLサーバーに通信します。
findとかをMySQLサーバーからデータを取得する
AR::Base.find AR.find(:id)は下記が呼ばれる
find https://github.com/rails/rails/blob/v4.2.6/activerecord/lib/active_record/core.rb#L126
AR::ConnectionAdapters::ConnectionPool#connection
findの中でconnectionが呼ばれると下記メソッドが呼ばれます。
AR::ConnectionAdapters::ConnectionPool#checkout
ここでMySQLサーバーにコネクションを貼ったobjectがなければ、checkoutにいきます。
checkoutでは、 acquire_connectionでpoolからMySQLサーバーにコネクションを貼ったmysql2_adapterのobjectを取ってくる or 新しくMySQLサーバーにコネクションを貼ったmysql2_adapterのobjectを取ってきます。
acquire_connection https://github.com/rails/rails/blob/v4.2.6/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L418 その後に、checkout_and_verifyがよばれます。
checkout_and_verify https://github.com/rails/rails/blob/v4.2.6/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L454
checkout_and_verifyの中で、AR::ConnectionAdapters::AbstractAdapter#verify!を呼び出して、取ってきたobejctの中のMySQLサーバーのコネクションが生きているかを確認し、 もし死んでたら、MySQLサーバーへのコネクションを貼り直します。
この中で、active?が呼ばれて最終的にpingが飛びます。
ADMIN PINGを抑制するために
AR::ConnectionAdapters::AbstractAdapter#verify!で何もしないようにする
def verify!(*ignored) reconnect! unless active? # ここを削除する end
このveryfy!はconnection_pool経由でobjectを取って来た際に、objectは存在するがMySQLとの接続が切れている可能性があるためにその確認をするためのメソッドかと思います。 (思わぬところでconnectionが切れている、idle状態でwait_timeout超える等々)。
APIはリクエスト毎にMySQLのコネクションを貼り直していることを前提にします。
1リクエスト内では、一度貼ったコネクションをもつobjectは AR::ConnectionAdapters::ConnectionPool#connectionの中で、@reserved_connections
インスタンス変数に現在のスレッドのobject_idをキーにキャッシュされ、ここから利用されます。
(余談ですが、WEBrickはmulti threadなWEBサーバーです)
そうすると1リクエスト内で一度AR::ConnectionAdapters::ConnectionPool#connection経由でコネクションを貼ると、AR::ConnectionAdapters::ConnectionPool#checkoutまで到達しません。
なので、AR::ConnectionAdapters::ConnectionPool#checkoutに到達するのは、
最初の一回目でコネクションをはるとき or 1リクエストの中で@reserved_connectionsが開放され、かつARのコネクションプールが残っている場合
です。
最初の一回目はコネクションを貼った直後に切れていることはないと思うので、verify!を向こうにしてADMIN PINGを飛ばないようにしても問題なさそうです。
def checkout synchronize do conn = acquire_connection <- ここで貼ったコネクションが conn.lease checkout_and_verify(conn) <- この間で切れる場合にveryfy!の結果が効くがそんな刹那なタイミングで切れることはないかと end end
@reserved_connectionsが開放されARのコネクションプールが残っている場合において、 生存確認がないとMySQL側でコネクションが切られているときにエラーになりますが、MySQLサーバーがコネクションを切る場合はreconnectしてもつながらない場合のほうが多いかなと。
このMySQLサーバーとのコネクションが切れているconnection objectを使って通信すると Lost connection to MySQL
のエラーが即座に返ってくるので、詰まることはないと思います。