mirror of
https://github.com/postgres/postgres.git
synced 2025-05-06 19:59:18 +03:00
Generalize parallel slot result handling.
Instead of having a hard-coded behavior that we ignore missing tables and report all other errors, let the caller decide what to do by setting a callback. Mark Dilger, reviewed and somewhat revised by me. The larger patch series of which this is a part has also had review from Peter Geoghegan, Andres Freund, Álvaro Herrera, Michael Paquier, and Amul Sul, but I don't know whether any of them have reviewed this bit specifically. Discussion: http://postgr.es/m/12ED3DA8-25F0-4B68-937D-D907CFBF08E7@enterprisedb.com Discussion: http://postgr.es/m/5F743835-3399-419C-8324-2D424237E999@enterprisedb.com Discussion: http://postgr.es/m/70655DF3-33CE-4527-9A4D-DDEB582B6BA0@enterprisedb.com
This commit is contained in:
parent
e955bd4b6c
commit
418611c84d
@ -466,6 +466,7 @@ reindex_one_database(const ConnParams *cparams, ReindexType type,
|
|||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
|
||||||
run_reindex_command(free_slot->connection, process_type, objname,
|
run_reindex_command(free_slot->connection, process_type, objname,
|
||||||
echo, verbose, concurrently, true);
|
echo, verbose, concurrently, true);
|
||||||
|
|
||||||
|
@ -713,6 +713,7 @@ vacuum_one_database(const ConnParams *cparams,
|
|||||||
* Execute the vacuum. All errors are handled in processQueryResult
|
* Execute the vacuum. All errors are handled in processQueryResult
|
||||||
* through ParallelSlotsGetIdle.
|
* through ParallelSlotsGetIdle.
|
||||||
*/
|
*/
|
||||||
|
ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
|
||||||
run_vacuum_command(free_slot->connection, sql.data,
|
run_vacuum_command(free_slot->connection, sql.data,
|
||||||
echo, tabname);
|
echo, tabname);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
static void init_slot(ParallelSlot *slot, PGconn *conn);
|
static void init_slot(ParallelSlot *slot, PGconn *conn);
|
||||||
static int select_loop(int maxFd, fd_set *workerset);
|
static int select_loop(int maxFd, fd_set *workerset);
|
||||||
static bool processQueryResult(PGconn *conn, PGresult *result);
|
static bool processQueryResult(ParallelSlot *slot, PGresult *result);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_slot(ParallelSlot *slot, PGconn *conn)
|
init_slot(ParallelSlot *slot, PGconn *conn)
|
||||||
@ -38,34 +38,24 @@ init_slot(ParallelSlot *slot, PGconn *conn)
|
|||||||
slot->connection = conn;
|
slot->connection = conn;
|
||||||
/* Initially assume connection is idle */
|
/* Initially assume connection is idle */
|
||||||
slot->isFree = true;
|
slot->isFree = true;
|
||||||
|
ParallelSlotClearHandler(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process (and delete) a query result. Returns true if there's no error,
|
* Process (and delete) a query result. Returns true if there's no problem,
|
||||||
* false otherwise -- but errors about trying to work on a missing relation
|
* false otherwise. It's up to the handler to decide what cosntitutes a
|
||||||
* are reported and subsequently ignored.
|
* problem.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
processQueryResult(PGconn *conn, PGresult *result)
|
processQueryResult(ParallelSlot *slot, PGresult *result)
|
||||||
{
|
{
|
||||||
/*
|
Assert(slot->handler != NULL);
|
||||||
* If it's an error, report it. Errors about a missing table are harmless
|
|
||||||
* so we continue processing; but die for other errors.
|
|
||||||
*/
|
|
||||||
if (PQresultStatus(result) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
char *sqlState = PQresultErrorField(result, PG_DIAG_SQLSTATE);
|
|
||||||
|
|
||||||
pg_log_error("processing of database \"%s\" failed: %s",
|
/* On failure, the handler should return NULL after freeing the result */
|
||||||
PQdb(conn), PQerrorMessage(conn));
|
if (!slot->handler(result, slot->connection, slot->handler_context))
|
||||||
|
|
||||||
if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0)
|
|
||||||
{
|
|
||||||
PQclear(result);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Ok, we have to free it ourself */
|
||||||
PQclear(result);
|
PQclear(result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -76,15 +66,15 @@ processQueryResult(PGconn *conn, PGresult *result)
|
|||||||
* Note that this will block if the connection is busy.
|
* Note that this will block if the connection is busy.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
consumeQueryResult(PGconn *conn)
|
consumeQueryResult(ParallelSlot *slot)
|
||||||
{
|
{
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
PGresult *result;
|
PGresult *result;
|
||||||
|
|
||||||
SetCancelConn(conn);
|
SetCancelConn(slot->connection);
|
||||||
while ((result = PQgetResult(conn)) != NULL)
|
while ((result = PQgetResult(slot->connection)) != NULL)
|
||||||
{
|
{
|
||||||
if (!processQueryResult(conn, result))
|
if (!processQueryResult(slot, result))
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
ResetCancelConn();
|
ResetCancelConn();
|
||||||
@ -227,14 +217,15 @@ ParallelSlotsGetIdle(ParallelSlot *slots, int numslots)
|
|||||||
|
|
||||||
if (result != NULL)
|
if (result != NULL)
|
||||||
{
|
{
|
||||||
/* Check and discard the command result */
|
/* Handle and discard the command result */
|
||||||
if (!processQueryResult(slots[i].connection, result))
|
if (!processQueryResult(slots + i, result))
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* This connection has become idle */
|
/* This connection has become idle */
|
||||||
slots[i].isFree = true;
|
slots[i].isFree = true;
|
||||||
|
ParallelSlotClearHandler(slots + i);
|
||||||
if (firstFree < 0)
|
if (firstFree < 0)
|
||||||
firstFree = i;
|
firstFree = i;
|
||||||
break;
|
break;
|
||||||
@ -329,9 +320,53 @@ ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots)
|
|||||||
|
|
||||||
for (i = 0; i < numslots; i++)
|
for (i = 0; i < numslots; i++)
|
||||||
{
|
{
|
||||||
if (!consumeQueryResult((slots + i)->connection))
|
if (!consumeQueryResult(slots + i))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TableCommandResultHandler
|
||||||
|
*
|
||||||
|
* ParallelSlotResultHandler for results of commands (not queries) against
|
||||||
|
* tables.
|
||||||
|
*
|
||||||
|
* Requires that the result status is either PGRES_COMMAND_OK or an error about
|
||||||
|
* a missing table. This is useful for utilities that compile a list of tables
|
||||||
|
* to process and then run commands (vacuum, reindex, or whatever) against
|
||||||
|
* those tables, as there is a race condition between the time the list is
|
||||||
|
* compiled and the time the command attempts to open the table.
|
||||||
|
*
|
||||||
|
* For missing tables, logs an error but allows processing to continue.
|
||||||
|
*
|
||||||
|
* For all other errors, logs an error and terminates further processing.
|
||||||
|
*
|
||||||
|
* res: PGresult from the query executed on the slot's connection
|
||||||
|
* conn: connection belonging to the slot
|
||||||
|
* context: unused
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
TableCommandResultHandler(PGresult *res, PGconn *conn, void *context)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If it's an error, report it. Errors about a missing table are harmless
|
||||||
|
* so we continue processing; but die for other errors.
|
||||||
|
*/
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
char *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
||||||
|
|
||||||
|
pg_log_error("processing of database \"%s\" failed: %s",
|
||||||
|
PQdb(conn), PQerrorMessage(conn));
|
||||||
|
|
||||||
|
if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) != 0)
|
||||||
|
{
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -15,12 +15,39 @@
|
|||||||
#include "fe_utils/connect_utils.h"
|
#include "fe_utils/connect_utils.h"
|
||||||
#include "libpq-fe.h"
|
#include "libpq-fe.h"
|
||||||
|
|
||||||
|
typedef bool (*ParallelSlotResultHandler) (PGresult *res, PGconn *conn,
|
||||||
|
void *context);
|
||||||
|
|
||||||
typedef struct ParallelSlot
|
typedef struct ParallelSlot
|
||||||
{
|
{
|
||||||
PGconn *connection; /* One connection */
|
PGconn *connection; /* One connection */
|
||||||
bool isFree; /* Is it known to be idle? */
|
bool isFree; /* Is it known to be idle? */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prior to issuing a command or query on 'connection', a handler callback
|
||||||
|
* function may optionally be registered to be invoked to process the
|
||||||
|
* results, and context information may optionally be registered for use
|
||||||
|
* by the handler. If unset, these fields should be NULL.
|
||||||
|
*/
|
||||||
|
ParallelSlotResultHandler handler;
|
||||||
|
void *handler_context;
|
||||||
} ParallelSlot;
|
} ParallelSlot;
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ParallelSlotSetHandler(ParallelSlot *slot, ParallelSlotResultHandler handler,
|
||||||
|
void *context)
|
||||||
|
{
|
||||||
|
slot->handler = handler;
|
||||||
|
slot->handler_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ParallelSlotClearHandler(ParallelSlot *slot)
|
||||||
|
{
|
||||||
|
slot->handler = NULL;
|
||||||
|
slot->handler_context = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
extern ParallelSlot *ParallelSlotsGetIdle(ParallelSlot *slots, int numslots);
|
extern ParallelSlot *ParallelSlotsGetIdle(ParallelSlot *slots, int numslots);
|
||||||
|
|
||||||
extern ParallelSlot *ParallelSlotsSetup(const ConnParams *cparams,
|
extern ParallelSlot *ParallelSlotsSetup(const ConnParams *cparams,
|
||||||
@ -31,5 +58,7 @@ extern void ParallelSlotsTerminate(ParallelSlot *slots, int numslots);
|
|||||||
|
|
||||||
extern bool ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots);
|
extern bool ParallelSlotsWaitCompletion(ParallelSlot *slots, int numslots);
|
||||||
|
|
||||||
|
extern bool TableCommandResultHandler(PGresult *res, PGconn *conn,
|
||||||
|
void *context);
|
||||||
|
|
||||||
#endif /* PARALLEL_SLOT_H */
|
#endif /* PARALLEL_SLOT_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user