Skip to content

Commit 44d85ba

Browse files
Copy and store addrinfo in libpq-owned private memory
This refactors libpq to copy addrinfos returned by getaddrinfo to memory owned by libpq such that future improvements can alter for example the order of entries. As a nice side effect of this refactor the mechanism for iteration over addresses in PQconnectPoll is now identical to its iteration over hosts. Author: Jelte Fennema <postgres@jeltef.nl> Reviewed-by: Aleksander Alekseev <aleksander@timescale.com> Reviewed-by: Michael Banck <mbanck@gmx.net> Reviewed-by: Andrey Borodin <amborodin86@gmail.com> Discussion: https://postgr.es/m/PR3PR83MB04768E2FF04818EEB2179949F7A69@PR3PR83MB0476.EURPRD83.prod.outlook.com
1 parent 8e5eef5 commit 44d85ba

File tree

4 files changed

+92
-34
lines changed

4 files changed

+92
-34
lines changed

src/include/libpq/pqcomm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ typedef struct
2727
socklen_t salen;
2828
} SockAddr;
2929

30+
typedef struct
31+
{
32+
int family;
33+
SockAddr addr;
34+
} AddrInfo;
35+
3036
/* Configure the UNIX socket location for the well known port. */
3137

3238
#define UNIXSOCK_PATH(path, port, sockdir) \

src/interfaces/libpq/fe-connect.c

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
389389
static void freePGconn(PGconn *conn);
390390
static void closePGconn(PGconn *conn);
391391
static void release_conn_addrinfo(PGconn *conn);
392+
static int store_conn_addrinfo(PGconn *conn, struct addrinfo *addrlist);
392393
static void sendTerminateConn(PGconn *conn);
393394
static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
394395
static PQconninfoOption *parse_connection_string(const char *connstr,
@@ -2295,7 +2296,7 @@ connectDBComplete(PGconn *conn)
22952296
time_t finish_time = ((time_t) -1);
22962297
int timeout = 0;
22972298
int last_whichhost = -2; /* certainly different from whichhost */
2298-
struct addrinfo *last_addr_cur = NULL;
2299+
int last_whichaddr = -2; /* certainly different from whichaddr */
22992300

23002301
if (conn == NULL || conn->status == CONNECTION_BAD)
23012302
return 0;
@@ -2339,11 +2340,11 @@ connectDBComplete(PGconn *conn)
23392340
if (flag != PGRES_POLLING_OK &&
23402341
timeout > 0 &&
23412342
(conn->whichhost != last_whichhost ||
2342-
conn->addr_cur != last_addr_cur))
2343+
conn->whichaddr != last_whichaddr))
23432344
{
23442345
finish_time = time(NULL) + timeout;
23452346
last_whichhost = conn->whichhost;
2346-
last_addr_cur = conn->addr_cur;
2347+
last_whichaddr = conn->whichaddr;
23472348
}
23482349

23492350
/*
@@ -2490,9 +2491,9 @@ PQconnectPoll(PGconn *conn)
24902491
/* Time to advance to next address, or next host if no more addresses? */
24912492
if (conn->try_next_addr)
24922493
{
2493-
if (conn->addr_cur && conn->addr_cur->ai_next)
2494+
if (conn->whichaddr < conn->naddr)
24942495
{
2495-
conn->addr_cur = conn->addr_cur->ai_next;
2496+
conn->whichaddr++;
24962497
reset_connection_state_machine = true;
24972498
}
24982499
else
@@ -2505,6 +2506,7 @@ PQconnectPoll(PGconn *conn)
25052506
{
25062507
pg_conn_host *ch;
25072508
struct addrinfo hint;
2509+
struct addrinfo *addrlist;
25082510
int thisport;
25092511
int ret;
25102512
char portstr[MAXPGPATH];
@@ -2545,7 +2547,7 @@ PQconnectPoll(PGconn *conn)
25452547
/* Initialize hint structure */
25462548
MemSet(&hint, 0, sizeof(hint));
25472549
hint.ai_socktype = SOCK_STREAM;
2548-
conn->addrlist_family = hint.ai_family = AF_UNSPEC;
2550+
hint.ai_family = AF_UNSPEC;
25492551

25502552
/* Figure out the port number we're going to use. */
25512553
if (ch->port == NULL || ch->port[0] == '\0')
@@ -2568,8 +2570,8 @@ PQconnectPoll(PGconn *conn)
25682570
{
25692571
case CHT_HOST_NAME:
25702572
ret = pg_getaddrinfo_all(ch->host, portstr, &hint,
2571-
&conn->addrlist);
2572-
if (ret || !conn->addrlist)
2573+
&addrlist);
2574+
if (ret || !addrlist)
25732575
{
25742576
libpq_append_conn_error(conn, "could not translate host name \"%s\" to address: %s",
25752577
ch->host, gai_strerror(ret));
@@ -2580,8 +2582,8 @@ PQconnectPoll(PGconn *conn)
25802582
case CHT_HOST_ADDRESS:
25812583
hint.ai_flags = AI_NUMERICHOST;
25822584
ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint,
2583-
&conn->addrlist);
2584-
if (ret || !conn->addrlist)
2585+
&addrlist);
2586+
if (ret || !addrlist)
25852587
{
25862588
libpq_append_conn_error(conn, "could not parse network address \"%s\": %s",
25872589
ch->hostaddr, gai_strerror(ret));
@@ -2590,7 +2592,7 @@ PQconnectPoll(PGconn *conn)
25902592
break;
25912593

25922594
case CHT_UNIX_SOCKET:
2593-
conn->addrlist_family = hint.ai_family = AF_UNIX;
2595+
hint.ai_family = AF_UNIX;
25942596
UNIXSOCK_PATH(portstr, thisport, ch->host);
25952597
if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
25962598
{
@@ -2605,8 +2607,8 @@ PQconnectPoll(PGconn *conn)
26052607
* name as a Unix-domain socket path.
26062608
*/
26072609
ret = pg_getaddrinfo_all(NULL, portstr, &hint,
2608-
&conn->addrlist);
2609-
if (ret || !conn->addrlist)
2610+
&addrlist);
2611+
if (ret || !addrlist)
26102612
{
26112613
libpq_append_conn_error(conn, "could not translate Unix-domain socket path \"%s\" to address: %s",
26122614
portstr, gai_strerror(ret));
@@ -2615,8 +2617,15 @@ PQconnectPoll(PGconn *conn)
26152617
break;
26162618
}
26172619

2618-
/* OK, scan this addrlist for a working server address */
2619-
conn->addr_cur = conn->addrlist;
2620+
/*
2621+
* Store a copy of the addrlist in private memory so we can perform
2622+
* randomization for load balancing.
2623+
*/
2624+
ret = store_conn_addrinfo(conn, addrlist);
2625+
pg_freeaddrinfo_all(hint.ai_family, addrlist);
2626+
if (ret)
2627+
goto error_return; /* message already logged */
2628+
26202629
reset_connection_state_machine = true;
26212630
conn->try_next_host = false;
26222631
}
@@ -2673,31 +2682,30 @@ PQconnectPoll(PGconn *conn)
26732682
{
26742683
/*
26752684
* Try to initiate a connection to one of the addresses
2676-
* returned by pg_getaddrinfo_all(). conn->addr_cur is the
2685+
* returned by pg_getaddrinfo_all(). conn->whichaddr is the
26772686
* next one to try.
26782687
*
26792688
* The extra level of braces here is historical. It's not
26802689
* worth reindenting this whole switch case to remove 'em.
26812690
*/
26822691
{
2683-
struct addrinfo *addr_cur = conn->addr_cur;
26842692
char host_addr[NI_MAXHOST];
26852693
int sock_type;
2694+
AddrInfo *addr_cur;
26862695

26872696
/*
26882697
* Advance to next possible host, if we've tried all of
26892698
* the addresses for the current host.
26902699
*/
2691-
if (addr_cur == NULL)
2700+
if (conn->whichaddr == conn->naddr)
26922701
{
26932702
conn->try_next_host = true;
26942703
goto keep_going;
26952704
}
2705+
addr_cur = &conn->addr[conn->whichaddr];
26962706

26972707
/* Remember current address for possible use later */
2698-
memcpy(&conn->raddr.addr, addr_cur->ai_addr,
2699-
addr_cur->ai_addrlen);
2700-
conn->raddr.salen = addr_cur->ai_addrlen;
2708+
memcpy(&conn->raddr, &addr_cur->addr, sizeof(SockAddr));
27012709

27022710
/*
27032711
* Set connip, too. Note we purposely ignore strdup
@@ -2732,7 +2740,7 @@ PQconnectPoll(PGconn *conn)
27322740
*/
27332741
sock_type |= SOCK_NONBLOCK;
27342742
#endif
2735-
conn->sock = socket(addr_cur->ai_family, sock_type, 0);
2743+
conn->sock = socket(addr_cur->family, sock_type, 0);
27362744
if (conn->sock == PGINVALID_SOCKET)
27372745
{
27382746
int errorno = SOCK_ERRNO;
@@ -2743,7 +2751,7 @@ PQconnectPoll(PGconn *conn)
27432751
* cases where the address list includes both IPv4 and
27442752
* IPv6 but kernel only accepts one family.
27452753
*/
2746-
if (addr_cur->ai_next != NULL ||
2754+
if (conn->whichaddr < conn->naddr ||
27472755
conn->whichhost + 1 < conn->nconnhost)
27482756
{
27492757
conn->try_next_addr = true;
@@ -2769,7 +2777,7 @@ PQconnectPoll(PGconn *conn)
27692777
* TCP sockets, nonblock mode, close-on-exec. Try the
27702778
* next address if any of this fails.
27712779
*/
2772-
if (addr_cur->ai_family != AF_UNIX)
2780+
if (addr_cur->family != AF_UNIX)
27732781
{
27742782
if (!connectNoDelay(conn))
27752783
{
@@ -2800,7 +2808,7 @@ PQconnectPoll(PGconn *conn)
28002808
#endif /* F_SETFD */
28012809
#endif
28022810

2803-
if (addr_cur->ai_family != AF_UNIX)
2811+
if (addr_cur->family != AF_UNIX)
28042812
{
28052813
#ifndef WIN32
28062814
int on = 1;
@@ -2892,8 +2900,8 @@ PQconnectPoll(PGconn *conn)
28922900
* Start/make connection. This should not block, since we
28932901
* are in nonblock mode. If it does, well, too bad.
28942902
*/
2895-
if (connect(conn->sock, addr_cur->ai_addr,
2896-
addr_cur->ai_addrlen) < 0)
2903+
if (connect(conn->sock, (struct sockaddr *) &addr_cur->addr.addr,
2904+
addr_cur->addr.salen) < 0)
28972905
{
28982906
if (SOCK_ERRNO == EINPROGRESS ||
28992907
#ifdef WIN32
@@ -4318,18 +4326,60 @@ freePGconn(PGconn *conn)
43184326
free(conn);
43194327
}
43204328

4329+
/*
4330+
* store_conn_addrinfo
4331+
* - copy addrinfo to PGconn object
4332+
*
4333+
* Copies the addrinfos from addrlist to the PGconn object such that the
4334+
* addrinfos can be manipulated by libpq. Returns a positive integer on
4335+
* failure, otherwise zero.
4336+
*/
4337+
static int
4338+
store_conn_addrinfo(PGconn *conn, struct addrinfo *addrlist)
4339+
{
4340+
struct addrinfo *ai = addrlist;
4341+
4342+
conn->whichaddr = 0;
4343+
4344+
conn->naddr = 0;
4345+
while (ai)
4346+
{
4347+
ai = ai->ai_next;
4348+
conn->naddr++;
4349+
}
4350+
4351+
conn->addr = calloc(conn->naddr, sizeof(AddrInfo));
4352+
if (conn->addr == NULL)
4353+
{
4354+
libpq_append_conn_error(conn, "out of memory");
4355+
return 1;
4356+
}
4357+
4358+
ai = addrlist;
4359+
for (int i = 0; i < conn->naddr; i++)
4360+
{
4361+
conn->addr[i].family = ai->ai_family;
4362+
4363+
memcpy(&conn->addr[i].addr.addr, ai->ai_addr,
4364+
ai->ai_addrlen);
4365+
conn->addr[i].addr.salen = ai->ai_addrlen;
4366+
ai = ai->ai_next;
4367+
}
4368+
4369+
return 0;
4370+
}
4371+
43214372
/*
43224373
* release_conn_addrinfo
43234374
* - Free any addrinfo list in the PGconn.
43244375
*/
43254376
static void
43264377
release_conn_addrinfo(PGconn *conn)
43274378
{
4328-
if (conn->addrlist)
4379+
if (conn->addr)
43294380
{
4330-
pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
4331-
conn->addrlist = NULL;
4332-
conn->addr_cur = NULL; /* for safety */
4381+
free(conn->addr);
4382+
conn->addr = NULL;
43334383
}
43344384
}
43354385

src/interfaces/libpq/libpq-int.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,10 @@ struct pg_conn
471471
PGTargetServerType target_server_type; /* desired session properties */
472472
bool try_next_addr; /* time to advance to next address/host? */
473473
bool try_next_host; /* time to advance to next connhost[]? */
474-
struct addrinfo *addrlist; /* list of addresses for current connhost */
475-
struct addrinfo *addr_cur; /* the one currently being tried */
476-
int addrlist_family; /* needed to know how to free addrlist */
474+
int naddr; /* number of addresses returned by getaddrinfo */
475+
int whichaddr; /* the address currently being tried */
476+
AddrInfo *addr; /* the array of addresses for the currently
477+
* tried host */
477478
bool send_appname; /* okay to send application_name? */
478479

479480
/* Miscellaneous stuff */

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ AcquireSampleRowsFunc
2626
ActionList
2727
ActiveSnapshotElt
2828
AddForeignUpdateTargets_function
29+
AddrInfo
2930
AffixNode
3031
AffixNodeData
3132
AfterTriggerEvent

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