Skip to content

Commit c5f2f45

Browse files
committed
Refactor cdbconn_discardResults() (#1804)
Previosly, `cdbconn_discardResults()` has proved to be unrealiable in case of OOM, as it would still try to read the results, causing errors in recursion if called when aborting a query. This patch refactors the said function by transferring code from another similar procedure, to make sure we're discarding results in a more robust way, adressing all libpq pitfalls instead of mindlessly retrying, as opposed to the past behavior. Ticket: ADBDEV-7690
1 parent 3862dd0 commit c5f2f45

4 files changed

Lines changed: 44 additions & 90 deletions

File tree

src/backend/cdb/cdbutil.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ cleanupQE(SegmentDatabaseDescriptor *segdbDesc)
894894
return false;
895895

896896
/* Note, we cancel all "still running" queries */
897-
if (!cdbconn_discardResults(segdbDesc, 20))
897+
if (!cdbconn_discardResults(segdbDesc, -1))
898898
{
899899
elog(LOG, "cleaning up seg%d while it is still busy", segdbDesc->segindex);
900900
return false;

src/backend/cdb/dispatcher/cdbconn.c

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -337,57 +337,64 @@ cdbconn_disconnect(SegmentDatabaseDescriptor *segdbDesc)
337337
}
338338

339339
/*
340-
* Read result from connection and discard it.
340+
* Discard any results from a connection.
341341
*
342-
* Retry at most N times.
342+
* retryCount is deprecated and isn't used anymore. This function tries to avoid
343+
* extra allocations and is safe to be called even in case of OOM.
343344
*
344-
* Return false if there'er still leftovers.
345+
* Return false if connection is still busy or dead.
345346
*/
346347
bool
347348
cdbconn_discardResults(SegmentDatabaseDescriptor *segdbDesc,
348349
int retryCount)
349350
{
350-
PGresult *pRes = NULL;
351-
ExecStatusType stat;
352-
int i = 0;
353-
bool retval = true;
351+
PGresult *res;
352+
PGnotify *notify;
353+
PGconn *conn = segdbDesc->conn;
354354

355-
/* PQstatus() is smart enough to handle NULL */
356-
while (NULL != (pRes = PQgetResult(segdbDesc->conn)))
357-
{
358-
stat = PQresultStatus(pRes);
359-
PQclear(pRes);
355+
(void) retryCount;
360356

361-
elog(LOG, "(%s) Leftover result at freeGang time: %s %s", segdbDesc->whoami,
362-
PQresStatus(stat),
363-
PQerrorMessage(segdbDesc->conn));
357+
/* Free some memory and replace current result with a fatal error dummy. */
358+
pqSaveErrorResult(conn);
364359

365-
if (stat == PGRES_FATAL_ERROR || stat == PGRES_BAD_RESPONSE)
366-
{
367-
retval = true;
368-
break;
369-
}
360+
/* Make sure PQgetResult() calls are not blocking. */
361+
PQconsumeInput(conn);
370362

371-
if (i++ > retryCount)
363+
/*
364+
* Discard anything that is unread. Since our result contains a fatal
365+
* error, we'll just consume the entire message without actually parsing
366+
* it.
367+
*/
368+
while (!PQisBusy(conn) && (res = PQgetResult(conn)) != NULL)
369+
{
370+
switch (PQresultStatus(res))
372371
{
373-
retval = false;
374-
break;
372+
case PGRES_COPY_IN:
373+
case PGRES_COPY_OUT:
374+
case PGRES_COPY_BOTH:
375+
PQendcopy(conn);
376+
/* fallthrough */
377+
default:
378+
PQclear(res);
375379
}
380+
381+
pqSaveErrorResult(conn);
376382
}
377383

378-
/*
379-
* Clear of all the notify messages as well.
380-
*/
381-
PGnotify *notify = segdbDesc->conn->notifyHead;
382-
while (notify != NULL)
384+
/* Free notices. */
385+
while ((notify = PQnotifies(conn)) != NULL)
386+
PQfreemem(notify);
387+
388+
/* The result is not needed anymore. */
389+
pqClearAsyncResult(conn);
390+
391+
if (PQisBusy(conn) && PQstatus(conn) != CONNECTION_BAD)
383392
{
384-
PGnotify *prev = notify;
385-
notify = notify->next;
386-
PQfreemem(prev);
393+
/* Some work is still remaining until we can die. */
394+
return false;
387395
}
388-
segdbDesc->conn->notifyHead = segdbDesc->conn->notifyTail = NULL;
389396

390-
return retval;
397+
return true;
391398
}
392399

393400
/* Return if it's a bad connection */

src/backend/cdb/dispatcher/cdbdisp_async.c

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -825,60 +825,6 @@ handlePollError(CdbDispatchCmdAsync *pParms)
825825
return;
826826
}
827827

828-
/*
829-
* Try to completely discard results from a dispatchResult's connection without
830-
* extra memory allocations by abusing libpq state-machine hacks.
831-
*/
832-
static void
833-
tryToWipeResults(CdbDispatchResult *dispatchResult)
834-
{
835-
PGresult *res;
836-
PGnotify *notify;
837-
PGconn *conn = dispatchResult->segdbDesc->conn;
838-
839-
/* Free some memory and replace current result with a fatal error dummy. */
840-
pqSaveErrorResult(conn);
841-
842-
/* Make sure PQgetResult() calls are not blocking. */
843-
PQconsumeInput(conn);
844-
845-
/*
846-
* Discard anything that is unread. Since our result contains a fatal
847-
* error, we'll just consume the entire message without actually parsing
848-
* it.
849-
*/
850-
while (!PQisBusy(conn) && (res = PQgetResult(conn)) != NULL)
851-
{
852-
switch (PQresultStatus(res))
853-
{
854-
case PGRES_COPY_IN:
855-
case PGRES_COPY_OUT:
856-
case PGRES_COPY_BOTH:
857-
PQendcopy(conn);
858-
/* fallthrough */
859-
default:
860-
PQclear(res);
861-
}
862-
863-
pqSaveErrorResult(conn);
864-
}
865-
866-
/* Free notices. */
867-
while ((notify = PQnotifies(conn)) != NULL)
868-
PQfreemem(notify);
869-
870-
/* The result is not needed anymore. */
871-
pqClearAsyncResult(conn);
872-
873-
if (PQisBusy(conn) && PQstatus(conn) != CONNECTION_BAD)
874-
{
875-
/* Some work is still remaining until we can die. */
876-
return;
877-
}
878-
879-
dispatchResult->stillRunning = false;
880-
}
881-
882828
/*
883829
* Receive and process results from QEs.
884830
*/
@@ -936,7 +882,8 @@ handlePollSuccess(CdbDispatchCmdAsync *pParms,
936882
* might not be enough memory to discard the result properly.
937883
* Let's get the big guns out.
938884
*/
939-
tryToWipeResults(dispatchResult);
885+
dispatchResult->stillRunning =
886+
!cdbconn_discardResults(dispatchResult->segdbDesc, -1);
940887

941888
forwardQENotices();
942889

src/include/cdb/cdbconn.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ cdbconn_doConnectComplete(SegmentDatabaseDescriptor *segdbDesc);
7878
/*
7979
* Read result from connection and discard it.
8080
*
81-
* Retry at most N times.
81+
* retryCount is deprecated and isn't used anymore.
8282
*
8383
* Return false if there'er still leftovers.
8484
*/

0 commit comments

Comments
 (0)