Skip to content

Commit 5f374fe

Browse files
committed
libpq: Try next host if one of them times out.
If one host in a multi-host connection string times out, move on to the next specified host instead of giving up entirely. Takayuki Tsunakawa, reviewed by Michael Paquier. I added a minor adjustment to the documentation. Discussion: http://postgr.es/m/0A3221C70F24FB45833433255569204D1F6F42F5@G01JPEXMBYT05
1 parent aa41bc7 commit 5f374fe

File tree

3 files changed

+33
-13
lines changed

3 files changed

+33
-13
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,9 +1041,10 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
10411041
string). Zero or not specified means wait indefinitely. It is not
10421042
recommended to use a timeout of less than 2 seconds.
10431043
This timeout applies separately to each connection attempt.
1044-
For example, if you specify two hosts and both of them are unreachable,
1045-
and <literal>connect_timeout</> is 5, the total time spent waiting for a
1046-
connection might be up to 10 seconds.
1044+
For example, if you specify two hosts and <literal>connect_timeout</>
1045+
is 5, each host will time out if no connection is made within 5
1046+
seconds, so the total time spent waiting for a connection might be
1047+
up to 10 seconds.
10471048
</para>
10481049
</listitem>
10491050
</varlistentry>

src/interfaces/libpq/fe-connect.c

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,7 @@ connectDBComplete(PGconn *conn)
17201720
{
17211721
PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
17221722
time_t finish_time = ((time_t) -1);
1723+
int timeout = 0;
17231724

17241725
if (conn == NULL || conn->status == CONNECTION_BAD)
17251726
return 0;
@@ -1729,8 +1730,7 @@ connectDBComplete(PGconn *conn)
17291730
*/
17301731
if (conn->connect_timeout != NULL)
17311732
{
1732-
int timeout = atoi(conn->connect_timeout);
1733-
1733+
timeout = atoi(conn->connect_timeout);
17341734
if (timeout > 0)
17351735
{
17361736
/*
@@ -1745,6 +1745,8 @@ connectDBComplete(PGconn *conn)
17451745

17461746
for (;;)
17471747
{
1748+
int ret = 0;
1749+
17481750
/*
17491751
* Wait, if necessary. Note that the initial state (just after
17501752
* PQconnectStart) is to wait for the socket to select for writing.
@@ -1761,15 +1763,17 @@ connectDBComplete(PGconn *conn)
17611763
return 1; /* success! */
17621764

17631765
case PGRES_POLLING_READING:
1764-
if (pqWaitTimed(1, 0, conn, finish_time))
1766+
ret = pqWaitTimed(1, 0, conn, finish_time);
1767+
if (ret == -1)
17651768
{
17661769
conn->status = CONNECTION_BAD;
17671770
return 0;
17681771
}
17691772
break;
17701773

17711774
case PGRES_POLLING_WRITING:
1772-
if (pqWaitTimed(0, 1, conn, finish_time))
1775+
ret = pqWaitTimed(0, 1, conn, finish_time);
1776+
if (ret == -1)
17731777
{
17741778
conn->status = CONNECTION_BAD;
17751779
return 0;
@@ -1782,6 +1786,23 @@ connectDBComplete(PGconn *conn)
17821786
return 0;
17831787
}
17841788

1789+
if (ret == 1) /* connect_timeout elapsed */
1790+
{
1791+
/* If there are no more hosts, return (the error message is already set) */
1792+
if (++conn->whichhost >= conn->nconnhost)
1793+
{
1794+
conn->whichhost = 0;
1795+
conn->status = CONNECTION_BAD;
1796+
return 0;
1797+
}
1798+
/* Attempt connection to the next host, starting the connect_timeout timer */
1799+
pqDropConnection(conn, true);
1800+
conn->addr_cur = conn->connhost[conn->whichhost].addrlist;
1801+
conn->status = CONNECTION_NEEDED;
1802+
if (conn->connect_timeout != NULL)
1803+
finish_time = time(NULL) + timeout;
1804+
}
1805+
17851806
/*
17861807
* Now try to advance the state machine.
17871808
*/

src/interfaces/libpq/fe-misc.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -991,11 +991,9 @@ pqWait(int forRead, int forWrite, PGconn *conn)
991991
/*
992992
* pqWaitTimed: wait, but not past finish_time.
993993
*
994-
* If finish_time is exceeded then we return failure (EOF). This is like
995-
* the response for a kernel exception because we don't want the caller
996-
* to try to read/write in that case.
997-
*
998994
* finish_time = ((time_t) -1) disables the wait limit.
995+
*
996+
* Returns -1 on failure, 0 if the socket is readable/writable, 1 if it timed out.
999997
*/
1000998
int
1001999
pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
@@ -1005,13 +1003,13 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
10051003
result = pqSocketCheck(conn, forRead, forWrite, finish_time);
10061004

10071005
if (result < 0)
1008-
return EOF; /* errorMessage is already set */
1006+
return -1; /* errorMessage is already set */
10091007

10101008
if (result == 0)
10111009
{
10121010
printfPQExpBuffer(&conn->errorMessage,
10131011
libpq_gettext("timeout expired\n"));
1014-
return EOF;
1012+
return 1;
10151013
}
10161014

10171015
return 0;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy