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