背景
Ruby on Rails 等で MySQLを利用している時、低レイヤーの根深い問題や、よくわからないエラーに遭遇したことはありますでしょうか?
RubyでMySQLを利用する際によく使われるライブラリである Mysql2
では、C拡張を利用して libmysqlclient
とリンクし、データベースサーバ上の mysqld
に対してアクセスしている都合上、複数の概念に対してのバージョン情報やオプションを扱う必要があります。
これらのバージョン情報は、問題の原因究明に重要であるため、取得方法を知っておくことで、効率的な作業が可能になります。自分が調べる時は勿論、他の人に手伝ってもらう時にも重要なヒントになります。
TL; DR
以下のようなデータを記録しておくと、調査が捗ります。
# Railsの場合の Mysql2::Client のオブジェクトの取得方法の例
client = ActiveRecord::Base.connection.raw_connection
# 自前の場合の Mysql2::Client のオブジェクトの作成方法の例
client = Mysql2::Client.new(host: 'localhost', username: 'root')
puts JSON.pretty_generate(
'RUBY_DESCRIPTION' => RUBY_DESCRIPTION, # Rubyの詳細
'Mysql2::VERSION' => Mysql2::VERSION, # gemのバージョン
'Mysql2::Client#info' => client.info, # libmysqlclient のバージョン
'Mysql2::Client#server_info' => client.server_info, # 接続先の MySQL Server のバージョン
)
# 接続に関するオプション (おまけ)
puts JSON.pretty_generate(client.query_options.reject { |k, _v| k == :password }) # 他にも重要な情報はあるが、最低限、パスワードは取り除く
出力例
{
"RUBY_DESCRIPTION": "ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]",
"Mysql2::VERSION": "0.4.10",
"Mysql2::Client#info": {
"id": 80016,
"version": "8.0.16",
"header_version": "8.0.16"
},
"Mysql2::Client#server_info": {
"id": 50637,
"version": "5.6.37"
}
}
各種バージョン
Rubyの詳細情報
ruby -v
の詳細が記述されます。
gem のバージョン
gem のバージョンは、以下の Mysql2 自体のバージョン Mysql2::VERSION
になります。
コードは以下のような形になっています。
module Mysql2
VERSION = "0.5.3".freeze
end
libmysqlclient のバージョン
Mysql2 は、独自に MySQLサーバ との通信プロトコルを実装しているわけではなく、 MySQLの提供している、Cで書かれたクライアントライブラリを利用してアクセスしています。
この時のライブラリのバージョンによって、微妙な挙動の差異が発生する場合があるので、この情報も重要です。
Mysql2 では、以下のようにC拡張で Mysql2::Client#info
が実装されています。
version = rb_str_new2(mysql_get_client_info());
これが呼び出している mysql_get_client_info()
が何を返すかは、MySQLのドキュメントに記述されています。
Returns a string that represents the MySQL client library version (for example, "5.6.48").
ということで、クライアントライブラリ libmysqlclient
のバージョンが返却されます。
接続先の MySQL Server のバージョン
問題が発生した時は、クライアントバージョンは勿論ですが、相手側のサーババージョンも気になるところです。
Mysql2 では、以下のようにC拡張で Mysql2::Client#server_info
が実装されています。
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
これが呼び出している mysql_get_server_info()
が何を返すかは、MySQLのドキュメントに記述されています。
Returns a string that represents the MySQL server version (for example, "5.6.48").
ということで、接続先の MySQL サーバのバージョンが返却されます。
接続に関するオプション
MySQLのサーバ側で設定されているオプションも勿論大切ですが、各クライアント(コネクション)毎に維持されているオプションも、細かい挙動を知る上では重要になってきます。
Mysql2 では、以下のようにインスタンス変数へのアクセッサーが生やされて、 Mysql2::Client#query_options
があるので、それを利用する形が良いと思われます。
※ このアクセッサーにはパスワードを含む情報が格納されているので、情報共有の際には、それをしっかり取り除くことを留意しておいてください。
参考
https://github.com/brianmario/mysql2
https://dev.mysql.com/doc/refman/5.6/en/c-api-server-client-versions.html