First pass at implementing concurrent index builds using multiple connections.
Adds a new --jobs command-line argument to specify how many worker connections you want. These worker connections should stick around while processing table(s) in a single database. For each table, parcel out the indexes to be built among these worker conns, submitting each CREATE INDEX ... request using PQsendQuery() i.e. in non-blocking fashion. Most of this is still rather crude, in particular the while (num_active_workers) ... loop in rebuild_indexes(), but it seems to be working, so I'm committing here.
This commit is contained in:
@ -26,9 +26,113 @@ YesNo prompt_password = DEFAULT;
|
||||
PGconn *connection = NULL;
|
||||
PGconn *conn2 = NULL;
|
||||
|
||||
worker_conns workers = {
|
||||
.num_workers = 0,
|
||||
.conns = NULL
|
||||
};
|
||||
|
||||
|
||||
static bool parse_pair(const char buffer[], char key[], char value[]);
|
||||
static char *get_username(void);
|
||||
|
||||
|
||||
/*
|
||||
* Set up worker conns which will be used for concurrent index rebuilds.
|
||||
* 'num_workers' is the desired number of worker connections, i.e. from
|
||||
* --jobs flag. Due to max_connections we might not actually be able to
|
||||
* set up that many workers, but don't treat that as a fatal error.
|
||||
*/
|
||||
void
|
||||
setup_workers(int num_workers)
|
||||
{
|
||||
StringInfoData buf;
|
||||
int i;
|
||||
PGconn *conn;
|
||||
|
||||
elog(DEBUG2, "In setup_workers(), target num_workers = %d", num_workers);
|
||||
|
||||
if (num_workers > 1 && num_workers > workers.num_workers)
|
||||
{
|
||||
initStringInfo(&buf);
|
||||
if (dbname && dbname[0])
|
||||
appendStringInfo(&buf, "dbname=%s ", dbname);
|
||||
if (host && host[0])
|
||||
appendStringInfo(&buf, "host=%s ", host);
|
||||
if (port && port[0])
|
||||
appendStringInfo(&buf, "port=%s ", port);
|
||||
if (username && username[0])
|
||||
appendStringInfo(&buf, "user=%s ", username);
|
||||
if (password && password[0])
|
||||
appendStringInfo(&buf, "password=%s ", password);
|
||||
|
||||
if (workers.conns == NULL)
|
||||
{
|
||||
elog(NOTICE, "Setting up workers.conns");
|
||||
workers.conns = (PGconn **) pgut_malloc(sizeof(PGconn *) * num_workers);
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "TODO: Implement pool resizing.");
|
||||
}
|
||||
|
||||
for (i = 0; i < num_workers; i++)
|
||||
{
|
||||
/* Don't prompt for password again; we should have gotten
|
||||
* it already from reconnect().
|
||||
*/
|
||||
elog(DEBUG2, "Setting up worker conn %d", i);
|
||||
|
||||
/* Don't confuse pgut_connections by using pgut_connect() */
|
||||
conn = PQconnectdb(buf.data);
|
||||
if (PQstatus(conn) == CONNECTION_OK)
|
||||
{
|
||||
workers.conns[i] = conn;
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(WARNING, "Unable to set up worker conn #%d: %s", i,
|
||||
PQerrorMessage(conn));
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* In case we bailed out of setting up all workers, record
|
||||
* how many successful worker conns we actually have.
|
||||
*/
|
||||
workers.num_workers = i;
|
||||
|
||||
termStringInfo(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disconnect all our worker conns. */
|
||||
void disconnect_workers(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(workers.num_workers))
|
||||
elog(DEBUG2, "No workers to disconnect.");
|
||||
else
|
||||
{
|
||||
for (i = 0; i < workers.num_workers; i++)
|
||||
{
|
||||
if (workers.conns[i])
|
||||
{
|
||||
elog(DEBUG2, "Disconnecting worker %d.", i);
|
||||
PQfinish(workers.conns[i]);
|
||||
workers.conns[i] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(NOTICE, "Worker %d already disconnected?", i);
|
||||
}
|
||||
}
|
||||
workers.num_workers = 0;
|
||||
free(workers.conns);
|
||||
workers.conns = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* the result is also available with the global variable 'connection'.
|
||||
*/
|
||||
@ -82,6 +186,7 @@ disconnect(void)
|
||||
pgut_disconnect(conn2);
|
||||
conn2 = NULL;
|
||||
}
|
||||
disconnect_workers();
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -48,6 +48,14 @@ typedef struct pgut_option
|
||||
|
||||
typedef void (*pgut_optfn) (pgut_option *opt, const char *arg);
|
||||
|
||||
typedef struct worker_conns
|
||||
{
|
||||
int max_num_workers;
|
||||
int num_workers;
|
||||
PGconn **conns;
|
||||
} worker_conns;
|
||||
|
||||
|
||||
|
||||
extern char *dbname;
|
||||
extern char *host;
|
||||
@ -58,12 +66,15 @@ extern YesNo prompt_password;
|
||||
|
||||
extern PGconn *connection;
|
||||
extern PGconn *conn2;
|
||||
extern worker_conns workers;
|
||||
|
||||
extern void pgut_help(bool details);
|
||||
extern void help(bool details);
|
||||
|
||||
extern void disconnect(void);
|
||||
extern void reconnect(int elevel);
|
||||
extern void setup_workers(int num_workers);
|
||||
extern void disconnect_workers(void);
|
||||
extern PGresult *execute(const char *query, int nParams, const char **params);
|
||||
extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel);
|
||||
extern ExecStatusType command(const char *query, int nParams, const char **params);
|
||||
|
Reference in New Issue
Block a user