1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-30 05:39:12 +02:00

Support for dual stack connections.

The prior code would only connect to the first address provided by getaddrinfo().

Instead try each address in the list. If all connections fail then wait and try them all again until timeout.

Currently a round robin approach is used where each connection attempt must fail before the next connection is attempted. This works fine, for example, when an ipv6 address has no route to the host, but will work less well when a host answers but doesn't respond in a timely fashion.

We may consider a Happy Eyeballs approach in the future, but since pgBackRest is primarily a background process, it is not clear that slightly improved response time (in the case of failed connections) is worth the extra complexity.
This commit is contained in:
David Steele 2023-09-12 18:09:58 -04:00 committed by GitHub
parent 9d3a605900
commit 39bb8a0d3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 15 deletions

View File

@ -1,6 +1,25 @@
<release date="XXXX-XX-XX" version="2.48dev" title="Under Development">
<release-core-list>
<release-feature-list>
<release-item>
<commit subject="Adjust Wait object to be more accurate when nested."/>
<commit subject="Aggregate error retries in ErrorRetry output."/>
<commit subject="Refactor address list lookup to include all returned addresses.">
<github-pull-request id="2174"/>
</commit>
<commit subject="Support for dual stack connections.">
<github-issue id="2154"/>
<github-pull-request id="2175"/>
</commit>
<release-item-contributor-list>
<release-item-contributor id="david.steele"/>
<release-item-reviewer id="stephen.frost"/>
</release-item-contributor-list>
<p>Support for dual stack connections.</p>
</release-item>
<release-item>
<github-issue id="2007"/>
<github-pull-request id="2141"/>

View File

@ -9,6 +9,7 @@ Socket Client
#include <unistd.h>
#include "common/debug.h"
#include "common/error/retry.h"
#include "common/io/client.h"
#include "common/io/socket/address.h"
#include "common/io/socket/client.h"
@ -72,8 +73,13 @@ sckClientOpen(THIS_VOID)
MEM_CONTEXT_TEMP_BEGIN()
{
Wait *const wait = waitNew(this->timeoutConnect);
ErrorRetry *const errRetry = errRetryNew();
bool retry;
Wait *wait = waitNew(this->timeoutConnect);
// Get an address list for the host
const AddressInfo *const addrInfo = addrInfoNew(this->host, this->port);
unsigned int addrInfoIdx = 0;
do
{
@ -83,8 +89,8 @@ sckClientOpen(THIS_VOID)
TRY_BEGIN()
{
// Get an address for the host. We are only going to try the first address returned.
const struct addrinfo *const addressFound = addrInfoGet(addrInfoNew(this->host, this->port), 0);
// Get next address for the host
const struct addrinfo *const addressFound = addrInfoGet(addrInfo, addrInfoIdx);
// Connect to the host
fd = socket(addressFound->ai_family, addressFound->ai_socktype, addressFound->ai_protocol);
@ -106,19 +112,32 @@ sckClientOpen(THIS_VOID)
}
CATCH_ANY()
{
if (fd != -1)
close(fd);
// Close socket
close(fd);
// Retry if wait time has not expired
if (waitMore(wait))
// Add the error retry info
errRetryAdd(errRetry);
// Increment address info index and reset if the end has been reached. When the end has been reached sleep for a bit
// to hopefully have better chance at succeeding, otherwise continue right to the next address as long as there is
// some time left.
addrInfoIdx++;
if (addrInfoIdx >= addrInfoSize(addrInfo))
{
LOG_DEBUG_FMT("retry %s: %s", errorTypeName(errorType()), errorMessage());
retry = true;
statInc(SOCKET_STAT_RETRY_STR);
addrInfoIdx = 0;
retry = waitMore(wait);
}
else
RETHROW();
retry = waitRemaining(wait) > 0;
// Error when no retries remain
if (!retry)
THROWP(errRetryType(errRetry), strZ(errRetryMessage(errRetry)));
// Log retry
LOG_DEBUG_FMT("retry %s: %s", errorTypeName(errorType()), errorMessage());
statInc(SOCKET_STAT_RETRY_STR);
}
TRY_END();
}

View File

@ -306,7 +306,8 @@ testRun(void)
TEST_ERROR_FMT(
httpRequestResponse(httpRequestNewP(client, STRDEF("GET"), STRDEF("/")), false), HostConnectError,
"unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused",
"unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused\n"
"[RETRY DETAIL OMITTED]",
hrnServerPort(0));
HRN_FORK_BEGIN()

View File

@ -368,7 +368,9 @@ testRun(void)
TEST_ASSIGN(client, sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100, 100), "new client");
TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused",
ioClientOpen(client), HostConnectError,
"unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused\n"
"[RETRY DETAIL OMITTED]",
hrnServerPort(0));
// This address should not be in use in a test environment -- if it is the test will fail
@ -451,7 +453,9 @@ testRun(void)
client, tlsClientNewP(sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100, 100), STRDEF("X"), 100, 100, true),
"new client");
TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused",
ioClientOpen(client), HostConnectError,
"unable to connect to 'localhost:%u (127.0.0.1)': [111] Connection refused\n"
"[RETRY DETAIL OMITTED]",
hrnServerPort(0));
// -------------------------------------------------------------------------------------------------------------------------