database.ymlのhostで{localhost, 127.0.0.1}を指定したときの違い

MySQL4でレコードの大量削除の検証がしたくて、Railsでもにょもにょやっていたのですが、
MySQL4とmysql2 gem の組み合わせの問題でハマりました。
arproxyを使うと解決できるんですけど、それは後日に。

もうひとつハマったのが、database.ymlのhostの指定でlocalhost127.0.0.1を指定したときの違い。portを指定しているときは両方TCP/IPでの接続になるのかな、と思っていたのですが、おおはずれ。

こんなかんじでstraceをとってみると

sudo sh -c 'strace -e connect -t bundle exec rake db:migrate'

localhostの場合は

13:44:33 --- SIGCHLD (Child exited) @ 0 (0) ---
13:44:37 connect(8, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
13:44:37 connect(8, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
13:44:37 connect(8, {sa_family=AF_FILE, path="/var/lib/mysql/mysql.sock"}, 110) = 0

AF_FILE(≒AF_UNIX)なのでUnixドメインソケットを使っていて

127.0.0.1の場合は

13:44:08 connect(8, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
13:44:08 connect(8, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
13:44:08 connect(8, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("127.0.0.1")}, 16) = 0

AF_INETを使っているというオチでした。

どこでこういう処理をしているのかなぁと思って、せっかくなので上のレイヤーから下のレイヤーに降りていくことに

ActiveRecord

lib/active_record/connection_adapter/abstract_mysql_adapter.rbをみると、 ActiveRecord::ConnectionHandling#mysql2_connectionで、Mysql2::Client.new(config)とかしてdatabase.ymlの設定をそのまま渡しているので多分違いそう。

mysql2 gem

mysql2 gem もhost, port, socketの設定をそのままmysql_real_connectに渡している。

https://github.com/brianmario/mysql2/blob/master/ext/mysql2/client.c#L159

ドキュメント嫁

http://dev.mysql.com/doc/refman/4.1/ja/mysql-real-connect.html

host パラメータには、ホスト名または IP アドレスのどちらかを指定する。
host に NULL または文字列 "localhost" を指定した場合、
ローカルホストに接続するものとみなされる。
OS がソケット(Unix)または名前付きパイプ(Windows)をサポートしている場合、
TCP/IP の代わりにそれらを使用してサーバに接続する。

はい...