読者です 読者をやめる 読者になる 読者になる

TCPSocket.newのDNSラウンドロビンの調査

mysqlへの接続時にホスト名に紐付くIPアドレスが帰ってきたらどうなるのかと思って少し調査中。


libcのレベルでは複数のgetaddrinfoが複数のIPアドレスをそのまま返している。
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/getaddrinfo.3.html


Ruby/MySQLの方を調べると、ホストへの接続にはTCPSocketにそのままホスト名を渡している模様。
https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/protocol.rb#L203


Rubyソースコードを読むと、リンクリストを舐めて最初に接続が成功したソケットをそのまま使っているっぽい。

    arg->remote.res = sock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM,
				    (type == INET_SERVER) ? AI_PASSIVE : 0);
    /*
     * Maybe also accept a local address
     */

    if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
	arg->local.res = sock_addrinfo(arg->local.host, arg->local.serv, SOCK_STREAM, 0);
    }

    arg->fd = fd = -1;
    for (res = arg->remote.res; res; res = res->ai_next) {
	status = ruby_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
	syscall = "socket(2)";
	fd = status;
	if (fd < 0) {
	    continue;
	}
	arg->fd = fd;
	if (type == INET_SERVER) {
#if !defined(_WIN32) && !defined(__CYGWIN__)
	    status = 1;
	    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
		       (char*)&status, sizeof(status));
#endif
	    status = bind(fd, res->ai_addr, res->ai_addrlen);
	    syscall = "bind(2)";
	}
	else {
	    if (arg->local.res) {
		status = bind(fd, arg->local.res->ai_addr, arg->local.res->ai_addrlen);
		syscall = "bind(2)";
	    }

	    if (status >= 0) {
		status = ruby_connect(fd, res->ai_addr, res->ai_addrlen,
				      (type == INET_SOCKS));
		syscall = "connect(2)";
	    }
	}

	if (status < 0) {
	    close(fd);
	    arg->fd = fd = -1;
	    continue;
	} else
	    break;
    }