Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1e6e98f

Browse files
committed
Fix libpq's implementation of per-host connection timeouts.
Commit 5f374fe attempted to turn the connect_timeout from an overall maximum time limit into a per-host limit, but it didn't do a great job of that. The timer would only get restarted if we actually detected timeout within connectDBComplete(), not if we changed our attention to a new host for some other reason. In that case the old timeout continued to run, possibly causing a premature timeout failure for the new host. Fix that, and also tweak the logic so that if we do get a timeout, we advance to the next available IP address, not to the next host name. There doesn't seem to be a good reason to assume that all the IP addresses supplied for a given host name will necessarily fail the same way as the current one. Moreover, this conforms better to the admittedly-vague documentation statement that the timeout is "per connection attempt". I changed that to "per host name or IP address" to be clearer. (Note that reconnections to the same server, such as for switching protocol version or SSL status, don't get their own separate timeout; that was true before and remains so.) Also clarify documentation about the interpretation of connect_timeout values less than 2. This seems like a bug, so back-patch to v10 where this logic came in. Tom Lane, reviewed by Fabien Coelho Discussion: https://postgr.es/m/5735.1533828184@sss.pgh.pa.us
1 parent 246a6c8 commit 1e6e98f

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

doc/src/sgml/libpq.sgml

+5-4
Original file line numberDiff line numberDiff line change
@@ -1110,10 +1110,11 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
11101110
<term><literal>connect_timeout</literal></term>
11111111
<listitem>
11121112
<para>
1113-
Maximum wait for connection, in seconds (write as a decimal integer
1114-
string). Zero or not specified means wait indefinitely. It is not
1115-
recommended to use a timeout of less than 2 seconds.
1116-
This timeout applies separately to each connection attempt.
1113+
Maximum wait for connection, in seconds (write as a decimal integer,
1114+
e.g. <literal>10</literal>). Zero, negative, or not specified means
1115+
wait indefinitely. The minimum allowed timeout is 2 seconds, therefore
1116+
a value of <literal>1</literal> is interpreted as <literal>2</literal>.
1117+
This timeout applies separately to each host name or IP address.
11171118
For example, if you specify two hosts and <literal>connect_timeout</literal>
11181119
is 5, each host will time out if no connection is made within 5
11191120
seconds, so the total time spent waiting for a connection might be

src/interfaces/libpq/fe-connect.c

+22-13
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,8 @@ connectDBComplete(PGconn *conn)
19051905
PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
19061906
time_t finish_time = ((time_t) -1);
19071907
int timeout = 0;
1908+
int last_whichhost = -2; /* certainly different from whichhost */
1909+
struct addrinfo *last_addr_cur = NULL;
19081910

19091911
if (conn == NULL || conn->status == CONNECTION_BAD)
19101912
return 0;
@@ -1918,19 +1920,34 @@ connectDBComplete(PGconn *conn)
19181920
if (timeout > 0)
19191921
{
19201922
/*
1921-
* Rounding could cause connection to fail; need at least 2 secs
1923+
* Rounding could cause connection to fail unexpectedly quickly;
1924+
* to prevent possibly waiting hardly-at-all, insist on at least
1925+
* two seconds.
19221926
*/
19231927
if (timeout < 2)
19241928
timeout = 2;
1925-
/* calculate the finish time based on start + timeout */
1926-
finish_time = time(NULL) + timeout;
19271929
}
19281930
}
19291931

19301932
for (;;)
19311933
{
19321934
int ret = 0;
19331935

1936+
/*
1937+
* (Re)start the connect_timeout timer if it's active and we are
1938+
* considering a different host than we were last time through. If
1939+
* we've already succeeded, though, needn't recalculate.
1940+
*/
1941+
if (flag != PGRES_POLLING_OK &&
1942+
timeout > 0 &&
1943+
(conn->whichhost != last_whichhost ||
1944+
conn->addr_cur != last_addr_cur))
1945+
{
1946+
finish_time = time(NULL) + timeout;
1947+
last_whichhost = conn->whichhost;
1948+
last_addr_cur = conn->addr_cur;
1949+
}
1950+
19341951
/*
19351952
* Wait, if necessary. Note that the initial state (just after
19361953
* PQconnectStart) is to wait for the socket to select for writing.
@@ -1975,18 +1992,10 @@ connectDBComplete(PGconn *conn)
19751992
if (ret == 1) /* connect_timeout elapsed */
19761993
{
19771994
/*
1978-
* Attempt connection to the next host, ignoring any remaining
1979-
* addresses for the current host.
1995+
* Give up on current server/address, try the next one.
19801996
*/
1981-
conn->try_next_addr = false;
1982-
conn->try_next_host = true;
1997+
conn->try_next_addr = true;
19831998
conn->status = CONNECTION_NEEDED;
1984-
1985-
/*
1986-
* Restart the connect_timeout timer for the new host.
1987-
*/
1988-
if (timeout > 0)
1989-
finish_time = time(NULL) + timeout;
19901999
}
19912000

19922001
/*

0 commit comments

Comments
 (0)