From 11d892dfcff303b409c66e8acfc400cb6b8da426 Mon Sep 17 00:00:00 2001 From: Evan Jones Date: Wed, 17 May 2023 12:22:30 -0400 Subject: [PATCH] pgconn.CancelRequest: Fix unix sockets: don't use RemoteAddr() The tests for cancelling requests were failing when using unix sockets. The reason is that net.Conn.RemoteAddr() calls getpeername() to get the address. For Unix sockets, this returns the address that was passed to bind() by the *server* process, not the address that was passed to connect() by the *client*. For postgres, this is always relative to the server's directory, so is a path like: ./.s.PGSQL.5432 Since it does not return the full absolute path, this function cannot connect, so it cannot cancel requests. To fix it, use the connection's config for Unix sockets. I think this should be okay, since a system using unix sockets should not have "fallbacks". If that is incorrect, we will need to save the address on PgConn. Fixes the following failed tests when using Unix sockets: --- FAIL: TestConnCancelRequest (2.00s) pgconn_test.go:2056: Error Trace: /Users/evan.jones/pgx/pgconn/pgconn_test.go:2056 /Users/evan.jones/pgx/pgconn/asm_arm64.s:1172 Error: Received unexpected error: dial unix ./.s.PGSQL.5432: connect: no such file or directory Test: TestConnCancelRequest pgconn_test.go:2063: Error Trace: /Users/evan.jones/pgx/pgconn/pgconn_test.go:2063 Error: Object expected to be of type *pgconn.PgError, but was Test: TestConnCancelRequest --- FAIL: TestConnContextCanceledCancelsRunningQueryOnServer (5.10s) pgconn_test.go:2109: Error Trace: /Users/evan.jones/pgx/pgconn/pgconn_test.go:2109 Error: Received unexpected error: timeout: context already done: context deadline exceeded Test: TestConnContextCanceledCancelsRunningQueryOnServer --- pgconn/pgconn.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pgconn/pgconn.go b/pgconn/pgconn.go index 8656ea518..ca4e88edc 100644 --- a/pgconn/pgconn.go +++ b/pgconn/pgconn.go @@ -857,9 +857,28 @@ func (pgConn *PgConn) CancelRequest(ctx context.Context) error { // the connection config. This is important in high availability configurations where fallback connections may be // specified or DNS may be used to load balance. serverAddr := pgConn.conn.RemoteAddr() - cancelConn, err := pgConn.config.DialFunc(ctx, serverAddr.Network(), serverAddr.String()) + var serverNetwork string + var serverAddress string + if serverAddr.Network() == "unix" { + // for unix sockets, RemoteAddr() calls getpeername() which returns the name the + // server passed to bind(). For Postgres, this is always a relative path "./.s.PGSQL.5432" + // so connecting to it will fail. Fall back to the config's value + serverNetwork, serverAddress = NetworkAddress(pgConn.config.Host, pgConn.config.Port) + } else { + serverNetwork, serverAddress = serverAddr.Network(), serverAddr.String() + } + cancelConn, err := pgConn.config.DialFunc(ctx, serverNetwork, serverAddress) if err != nil { - return err + // In case of unix sockets, RemoteAddr() returns only the file part of the path. If the + // first connect failed, try the config. + if serverAddr.Network() != "unix" { + return err + } + serverNetwork, serverAddr := NetworkAddress(pgConn.config.Host, pgConn.config.Port) + cancelConn, err = pgConn.config.DialFunc(ctx, serverNetwork, serverAddr) + if err != nil { + return err + } } defer cancelConn.Close()