PostgreSQL で新規接続をしても TCP 接続が発生しない場合

ローカルの Unix-domain socket を使ってたら TCP 接続が発生しないという話です。

問題設定

WSL2環境です。以下のようにローカルに PostgreSQL サーバーをインストールしたうえで立ち上げて、バンドルされている psql で繋いでみます。

$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start
pg_ctl: another server might be running; trying to start server anyway
waiting for server to start.... done
server started
$ /usr/local/pgsql/bin/psql test
psql (17.5)
Type "help" for help.

test=#

このタイミングで5432ポートのソケット使用状況を見てみると、以下のように5432で接続待ちしている様子だけがわかります。特に、新規TCP接続が発生していないことに注意しましょう。

$ ss -pantu | grep 5432
tcp   LISTEN 0      200         127.0.0.1:5432       0.0.0.0:*

(この記事を書き始めた時点での筆者のように)「psql は常にTCP 接続でサーバに接続する」と思っていると辻褄が合いません。これはどういうことか、という話を深掘るのがこの記事の目的です。

結論

psql のドキュメントに明確に書かれているのですが、Linux 環境で psql で接続する際に host を指定しなかったら Unix-domain socket を使おうとします。特に新規 TCP 接続はつくらないことがわかります。

 If you omit the host name, psql will connect via a Unix-domain socket to a server on the local host, or via TCP/IP to localhost on Windows.

また、-h でホスト名を指定する際に / からはじまる値を指定することでも Unix-domain socket を使うことがわかります。

-h hostname
--host=hostname 
Specifies the host name of the machine on which the server is running. If the value begins with a slash, it is used as the directory for the Unix-domain socket.

実機検証

実際に試してみたら本当にそのようにふるまいました。host に /tmp を指定したときは TCP 接続は発生しませんが、localhost を指定することで TCP 接続が発生していることがわかります。

$ /usr/local/pgsql/bin/psql test -h /tmp
psql (17.5)
Type "help" for help.

test=#

-----

$ ss -pantu | grep 5432
tcp   LISTEN 0      200         127.0.0.1:5432       0.0.0.0:*
$ /usr/local/pgsql/bin/psql test -h localhost
psql (17.5)
Type "help" for help.

test=#

-----

$ ss -pantu | grep 5432
tcp   LISTEN 0      200         127.0.0.1:5432       0.0.0.0:*
tcp   ESTAB  0      0           127.0.0.1:5432     127.0.0.1:52640
tcp   ESTAB  0      0           127.0.0.1:52640    127.0.0.1:5432

補足

host を指定しなかった場合に Unix-domain socket を使うのはここまでで述べたとおりなのですが、ではどのディレクトリにあるファイルを使うことにするのか?という疑問が考えられます。上の説明では /tmp を天下りに指定しましたが、どこでこれが指定されているのかは気になるところです。

上掲の筆者環境は PostgreSQL17 をソースコードからビルドしています。いったんはこの場合の振る舞いがどのように決定されるかを深堀りしてみます。

まずはサーバ側から。PostgreSQL のドキュメントには以下のように(Linux 環境なら)デフォルトは /tmp となる事が明記されていますが、しかしながらビルド時に変更可能であることも書かれています。

The default value is normally /tmp, but that can be changed at build time. On Windows, the default is empty, which means no Unix-domain socket is created by default. This parameter can only be set at server start.

In addition to the socket file itself, which is named .s.PGSQL.nnnn where nnnn is the server's port number, an ordinary file named .s.PGSQL.nnnn.lock will be created in each of the unix_socket_directories directories. Neither file should ever be removed manually. For sockets in the abstract namespace, no lock file is created.

続いて今回のクライアントである psql 側。psql のドキュメントを読む感じだと内部的には libpq を使っていることがわかります。では libpq のドキュメントまで辿ってみるとlibpq のデフォルト設定として /tmp にある Unix-domain socket を使うと明記がありました。

host 
Name of host to connect to. If a host name looks like an absolute path name, it specifies Unix-domain communication rather than TCP/IP communication; the value is the name of the directory in which the socket file is stored. (On Unix, an absolute path name begins with a slash. On Windows, paths starting with drive letters are also recognized.) If the host name starts with @, it is taken as a Unix-domain socket in the abstract namespace (currently supported on Linux and Windows). The default behavior when host is not specified, or is empty, is to connect to a Unix-domain socket in /tmp (or whatever socket directory was specified when PostgreSQL was built). On Windows, the default is to connect to localhost.

さて、筆者の WSL2 環境には apt 経由で入れてきた PostgreSQL も(なぜか)いたのですが、こいつで同様の接続をしようとすると以下の様にエラーになりました。

$ /usr/bin/psql test
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5433" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?

こうなる理由は、debian 版の PostgreSQL にパッチがあたっているからのようです。なぜこのようなパッチがあたっているかまでは(あまり興味がわかなくて)しらべていませんが……

-#define DEFAULT_PGSOCKET_DIR  "/tmp"
+#define DEFAULT_PGSOCKET_DIR  "/var/run/postgresql"

感想

おもしろかったです。