Skip to content

Commit

Permalink
Fix compat with Postgres 15devel again. (#842)
Browse files Browse the repository at this point in the history
A recent patch in Postgres source code replaces the random() API that is
embedded in libpq client-side (and also in the backend, but we're good on
that front).

See
https://git.postgresql.org/cgit/postgresql.git/commit/?id=3804539e48e794781c6145c7f988f5d507418fa8
for details.
  • Loading branch information
DimCitus committed Dec 6, 2021
1 parent e7edc49 commit 2d64147
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 17 deletions.
18 changes: 18 additions & 0 deletions src/bin/pg_autoctl/demoapp.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
#include <time.h>
#include <unistd.h>

#include "postgres.h"

#if PG_MAJORVERSION_NUM >= 15
#include "common/pg_prng.h"
#endif

#include "cli_do_demoapp.h"
#include "defaults.h"
#include "demoapp.h"
Expand Down Expand Up @@ -530,8 +536,13 @@ demoapp_update_client_failovers(const char *pguri, int clientId, int failovers)
/*
* http://c-faq.com/lib/randrange.html
*/
#if PG_MAJORVERSION_NUM < 15
#define random_between(M, N) \
((M) + pg_lrand48() / (RAND_MAX / ((N) -(M) +1) + 1))
#else
#define random_between(M, N) \
((M) + pg_prng_uint32(&prng_state) / (RAND_MAX / ((N) -(M) +1) + 1))
#endif

/*
* demo_start_client starts a sub-process that implements our demo application:
Expand All @@ -558,7 +569,14 @@ demoapp_start_client(const char *pguri, int clientId,
int retrySleepTime = 500; /* first retry happens after 500 ms */

/* initialize a seed for our random number generator */

#if PG_MAJORVERSION_NUM < 15
pg_srand48(((unsigned int) (getpid() ^ time(NULL))));
#else
pg_prng_state prng_state;

pg_prng_seed(&prng_state, (uint64) (getpid() ^ time(NULL)));
#endif

/* pick a random retry policy for this client */
retryCap = random_between(50, 500);
Expand Down
61 changes: 44 additions & 17 deletions src/bin/pg_autoctl/pgsql.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
#include <time.h>
#include <unistd.h>

#include "postgres.h"
#include "postgres_fe.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "portability/instr_time.h"

#if PG_MAJORVERSION_NUM >= 15
#include "common/pg_prng.h"
#endif

#include "cli_root.h"
#include "defaults.h"
#include "log.h"
Expand All @@ -24,13 +29,13 @@
#include "string_utils.h"


#define ERRCODE_DUPLICATE_OBJECT "42710"
#define ERRCODE_DUPLICATE_DATABASE "42P04"
#define STR_ERRCODE_DUPLICATE_OBJECT "42710"
#define STR_ERRCODE_DUPLICATE_DATABASE "42P04"

#define ERRCODE_INVALID_OBJECT_DEFINITION "42P17"
#define ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE "55000"
#define ERRCODE_OBJECT_IN_USE "55006"
#define ERRCODE_UNDEFINED_OBJECT "42704"
#define STR_ERRCODE_INVALID_OBJECT_DEFINITION "42P17"
#define STR_ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE "55000"
#define STR_ERRCODE_OBJECT_IN_USE "55006"
#define STR_ERRCODE_UNDEFINED_OBJECT "42704"

static char * ConnectionTypeToString(ConnectionType connectionType);
static void log_connection_error(PGconn *connection, int logLevel);
Expand Down Expand Up @@ -177,7 +182,11 @@ pgsql_set_retry_policy(ConnectionRetryPolicy *retryPolicy,
retryPolicy->baseSleepTime = baseSleepTime;

/* initialize a seed for our random number generator */
#if PG_MAJORVERSION_NUM < 15
pg_srand48(time(0));
#else
pg_prng_seed(&(retryPolicy->prng_state), (uint64) (getpid() ^ time(NULL)));
#endif
}


Expand Down Expand Up @@ -263,8 +272,28 @@ pgsql_set_monitor_interactive_retry_policy(ConnectionRetryPolicy *retryPolicy)
/*
* http://c-faq.com/lib/randrange.html
*/
#define random_between(M, N) \
((M) + pg_lrand48() / (RAND_MAX / ((N) -(M) +1) + 1))
#define random_between(R, M, N) ((M) + R / (RAND_MAX / ((N) -(M) +1) + 1))

/*
* pick_random_sleep_time picks a random sleep time between the given policy
* base sleep time and 3 times the previous sleep time. See below in
* pgsql_compute_connection_retry_sleep_time for a deep dive into why we are
* interested in this computation.
*/
static int
pick_random_sleep_time(ConnectionRetryPolicy *retryPolicy)
{
#if PG_MAJORVERSION_NUM < 15
long random = pg_lrand48();
#else
uint32_t random = pg_prng_uint32(&(retryPolicy->prng_state));
#endif

return random_between(random,
retryPolicy->baseSleepTime,
retryPolicy->sleepTime * 3);
}


/*
* pgsql_compute_connection_retry_sleep_time returns how much time to sleep
Expand Down Expand Up @@ -312,9 +341,7 @@ pgsql_compute_connection_retry_sleep_time(ConnectionRetryPolicy *retryPolicy)
* time spent, something we care to optimize for even when it means more
* work on the monitor side.
*/
int previousSleepTime = retryPolicy->sleepTime;
int sleepTime =
random_between(retryPolicy->baseSleepTime, previousSleepTime * 3);
int sleepTime = pick_random_sleep_time(retryPolicy);

retryPolicy->sleepTime = min(retryPolicy->maxSleepTime, sleepTime);

Expand Down Expand Up @@ -1070,10 +1097,10 @@ pgsql_execute_with_params(PGSQL *pgsql, const char *sql, int paramCount,
*/
if (pgsql->connectionType == PGSQL_CONN_MONITOR &&
sqlstate != NULL &&
!(strcmp(sqlstate, ERRCODE_INVALID_OBJECT_DEFINITION) == 0 ||
strcmp(sqlstate, ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE) == 0 ||
strcmp(sqlstate, ERRCODE_OBJECT_IN_USE) == 0 ||
strcmp(sqlstate, ERRCODE_UNDEFINED_OBJECT) == 0))
!(strcmp(sqlstate, STR_ERRCODE_INVALID_OBJECT_DEFINITION) == 0 ||
strcmp(sqlstate, STR_ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE) == 0 ||
strcmp(sqlstate, STR_ERRCODE_OBJECT_IN_USE) == 0 ||
strcmp(sqlstate, STR_ERRCODE_UNDEFINED_OBJECT) == 0))
{
log_error("SQL query: %s", sql);
log_error("SQL params: %s", debugParameters);
Expand Down Expand Up @@ -2073,7 +2100,7 @@ pgsql_create_database(PGSQL *pgsql, const char *dbname, const char *owner)
*/
char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);

if (strcmp(sqlstate, ERRCODE_DUPLICATE_DATABASE) == 0)
if (strcmp(sqlstate, STR_ERRCODE_DUPLICATE_DATABASE) == 0)
{
log_info("The database \"%s\" already exists, skipping.", dbname);
}
Expand Down Expand Up @@ -2270,7 +2297,7 @@ pgsql_create_user(PGSQL *pgsql, const char *userName, const char *password,
*/
char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);

if (strcmp(sqlstate, ERRCODE_DUPLICATE_OBJECT) == 0)
if (strcmp(sqlstate, STR_ERRCODE_DUPLICATE_OBJECT) == 0)
{
log_info("The user \"%s\" already exists, skipping.", userName);
}
Expand Down
9 changes: 9 additions & 0 deletions src/bin/pg_autoctl/pgsql.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
#include <limits.h>
#include <stdbool.h>

#include "postgres.h"
#include "libpq-fe.h"
#include "portability/instr_time.h"

#if PG_MAJORVERSION_NUM >= 15
#include "common/pg_prng.h"
#endif

#include "defaults.h"
#include "pgsetup.h"
#include "state.h"
Expand Down Expand Up @@ -92,6 +97,10 @@ typedef struct ConnectionRetryPolicy
instr_time startTime; /* time of the first attempt */
instr_time connectTime; /* time of successful connection */
int attempts; /* how many attempts have been made so far */

#if PG_MAJORVERSION_NUM >= 15
pg_prng_state prng_state;
#endif
} ConnectionRetryPolicy;

/*
Expand Down

0 comments on commit 2d64147

Please sign in to comment.