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)