From 5ca00774194dc179d02867d536b73eb85fffd227 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 23 Aug 2018 16:39:19 -0400 Subject: In libpq, don't look up all the hostnames at once. Historically, we looked up the target hostname in connectDBStart, so that PQconnectPoll did not need to do DNS name resolution. The patches that added multiple-target-host support to libpq preserved this division of labor; but it's really nonsensical now, because it means that if any one of the target hosts fails to resolve in DNS, the connection fails. That negates the no-single-point-of-failure goal of the feature. Additionally, DNS lookups aren't exactly cheap, but the code did them all even if the first connection attempt succeeds. Hence, rearrange so that PQconnectPoll does the lookups, and only looks up a hostname when it's time to try that host. This does mean that PQconnectPoll could block on a DNS lookup --- but if you wanted to avoid that, you should be using hostaddr, as the documentation has always specified. It seems fairly unlikely that any applications would really care whether the lookup occurs inside PQconnectStart or PQconnectPoll. In addition to calling out that fact explicitly, do some other minor wordsmithing in the docs around the multiple-target-host feature. Since this seems like a bug in the multiple-target-host feature, backpatch to v10 where that was introduced. In the back branches, avoid moving any existing fields of struct pg_conn, just in case any third-party code is looking into that struct. Tom Lane, reviewed by Fabien Coelho Discussion: https://postgr.es/m/4913.1533827102@sss.pgh.pa.us --- doc/src/sgml/libpq.sgml | 78 ++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 33 deletions(-) (limited to 'doc/src') diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 49de975e7f5..5e7931ba901 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -303,9 +303,9 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); - The hostaddr and host parameters are used appropriately to ensure that - name and reverse name queries are not made. See the documentation of - these parameters in for details. + The hostaddr parameter must be used appropriately + to prevent DNS queries from being made. See the documentation of + this parameter in for details. @@ -318,7 +318,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); - You ensure that the socket is in the appropriate state + You must ensure that the socket is in the appropriate state before calling PQconnectPoll, as described below. @@ -326,24 +326,27 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); - Note: use of PQconnectStartParams is analogous to - PQconnectStart shown below. + To begin a nonblocking connection request, + call PQconnectStart + or PQconnectStartParams. If the result is null, + then libpq has been unable to allocate a + new PGconn structure. Otherwise, a + valid PGconn pointer is returned (though not + yet representing a valid connection to the database). Next + call PQstatus(conn). If the result + is CONNECTION_BAD, the connection attempt has already + failed, typically because of invalid connection parameters. - To begin a nonblocking connection request, call conn = PQconnectStart("connection_info_string"). - If conn is null, then libpq has been unable to allocate a new PGconn - structure. Otherwise, a valid PGconn pointer is returned (though not yet - representing a valid connection to the database). On return from - PQconnectStart, call status = PQstatus(conn). If status equals - CONNECTION_BAD, PQconnectStart has failed. - - - - If PQconnectStart succeeds, the next stage is to poll - libpq so that it can proceed with the connection sequence. + If PQconnectStart + or PQconnectStartParams succeeds, the next stage + is to poll libpq so that it can proceed with + the connection sequence. Use PQsocket(conn) to obtain the descriptor of the socket underlying the database connection. + (Caution: do not assume that the socket remains the same + across PQconnectPoll calls.) Loop thus: If PQconnectPoll(conn) last returned PGRES_POLLING_READING, wait until the socket is ready to read (as indicated by select(), poll(), or @@ -352,9 +355,8 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); Conversely, if PQconnectPoll(conn) last returned PGRES_POLLING_WRITING, wait until the socket is ready to write, then call PQconnectPoll(conn) again. - If you have yet to call - PQconnectPoll, i.e., just after the call to - PQconnectStart, behave as if it last returned + On the first iteration, i.e. if you have yet to call + PQconnectPoll, behave as if it last returned PGRES_POLLING_WRITING. Continue this loop until PQconnectPoll(conn) returns PGRES_POLLING_FAILED, indicating the connection procedure @@ -479,10 +481,12 @@ switch(PQstatus(conn)) - Note that if PQconnectStart returns a non-null pointer, you must call - PQfinish when you are finished with it, in order to dispose of - the structure and any associated memory blocks. This must be done even if - the connection attempt fails or is abandoned. + Note that when PQconnectStart + or PQconnectStartParams returns a non-null + pointer, you must call PQfinish when you are + finished with it, in order to dispose of the structure and any + associated memory blocks. This must be done even if the connection + attempt fails or is abandoned. @@ -913,7 +917,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname It is possible to specify multiple hosts to connect to, so that they are tried in the given order. In the Keyword/Value format, the host, hostaddr, and port options accept a comma-separated - list of values. The same number of elements must be given in each option, such + list of values. The same number of elements must be given in each + option that is specified, such that e.g. the first hostaddr corresponds to the first host name, the second hostaddr corresponds to the second host name, and so forth. As an exception, if only one port is specified, it @@ -922,9 +927,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname In the connection URI format, you can list multiple host:port pairs - separated by commas, in the host component of the URI. In either - format, a single host name can also translate to multiple network addresses. A - common example of this is a host that has both an IPv4 and an IPv6 address. + separated by commas, in the host component of the URI. + + + + In either format, a single host name can translate to multiple network + addresses. A common example of this is a host that has both an IPv4 and + an IPv6 address. @@ -958,9 +967,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname Name of host to connect to.host name If a host name begins with a slash, 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. If - multiple host names are specified, each will be tried in turn in - the order given. The default behavior when host is + name of the directory in which the socket file is stored. + The default behavior when host is not specified, or is empty, is to connect to a Unix-domain socketUnix domain socket in /tmp (or whatever socket directory was specified @@ -997,8 +1005,12 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname - If host is specified without hostaddr, - a host name lookup occurs. + If host is specified + without hostaddr, a host name lookup occurs. + (When using PQconnectPoll, the lookup occurs + when PQconnectPoll first considers this host + name, and it may cause PQconnectPoll to block + for a significant amount of time.) -- cgit v1.2.3