/* * CdbCheckDispatchResult: * * Waits for completion of threads launched by cdbdisp_dispatchToGang(). * * QEs that were dispatched with 'cancelOnError' true and are not yet idle * will be canceled/finished according to waitMode. */ void CdbCheckDispatchResult(struct CdbDispatcherState *ds, DispatchWaitMode waitMode) { PG_TRY(); { CdbCheckDispatchResult_internal(ds, waitMode); } PG_CATCH(); { cdbdisp_clearGangActiveFlag(ds); PG_RE_THROW(); } PG_END_TRY(); cdbdisp_clearGangActiveFlag(ds); if (log_dispatch_stats) ShowUsage("DISPATCH STATISTICS"); if (DEBUG1 >= log_min_messages) { char msec_str[32]; switch (check_log_duration(msec_str, false)) { case 1: case 2: ereport(LOG, (errmsg("duration to dispatch result received from all QEs: %s ms", msec_str))); break; } } }
static void thread_DispatchWait(DispatchCommandParms * pParms) { SegmentDatabaseDescriptor *segdbDesc; CdbDispatchResult *dispatchResult; int i, db_count = pParms->db_count; int timeoutCounter = 0; /* * OK, we are finished submitting the command to the segdbs. * Now, we have to wait for them to finish. */ for (;;) { int sock; int n; int nfds = 0; int cur_fds_num = 0; /* * Which QEs are still running and could send results to us? */ for (i = 0; i < db_count; i++) { dispatchResult = pParms->dispatchResultPtrArray[i]; segdbDesc = dispatchResult->segdbDesc; /* * Already finished with this QE? */ if (!dispatchResult->stillRunning) continue; /* * Add socket to fd_set if still connected. */ sock = PQsocket(segdbDesc->conn); if (sock >= 0 && PQstatus(segdbDesc->conn) != CONNECTION_BAD) { pParms->fds[nfds].fd = sock; pParms->fds[nfds].events = POLLIN; nfds++; Assert(nfds <= pParms->nfds); } /* * Lost the connection. */ else { char *msg = PQerrorMessage(segdbDesc->conn); /* * Save error info for later. */ cdbdisp_appendMessage(dispatchResult, DEBUG1, ERRCODE_GP_INTERCONNECTION_ERROR, "Lost connection to %s. %s", segdbDesc->whoami, msg ? msg : ""); /* * Free the PGconn object. */ PQfinish(segdbDesc->conn); segdbDesc->conn = NULL; dispatchResult->stillRunning = false; } } /* * Break out when no QEs still running. */ if (nfds <= 0) break; /* * bail-out if we are dying. We should not do much of cleanup * as the main thread is waiting on this thread to finish. Once * QD dies, QE will recognize it shortly anyway. */ if (proc_exit_inprogress) break; /* * Wait for results from QEs. Block here until input is available. */ n = poll(pParms->fds, nfds, DISPATCH_WAIT_TIMEOUT_SEC * 1000); if (n < 0) { int sock_errno = SOCK_ERRNO; if (sock_errno == EINTR) continue; handlePollError(pParms, db_count, sock_errno); continue; } if (n == 0) { handlePollTimeout(pParms, db_count, &timeoutCounter, true); continue; } cur_fds_num = 0; /* * We have data waiting on one or more of the connections. */ for (i = 0; i < db_count; i++) { bool finished; dispatchResult = pParms->dispatchResultPtrArray[i]; segdbDesc = dispatchResult->segdbDesc; /* * Skip if already finished or didn't dispatch. */ if (!dispatchResult->stillRunning) continue; if (DEBUG4 >= log_min_messages) write_log("looking for results from %d of %d", i + 1, db_count); /* * Skip this connection if it has no input available. */ sock = PQsocket(segdbDesc->conn); if (sock >= 0) { /* * The fds array is shorter than conn array, so the following * match method will use this assumtion. */ Assert(sock == pParms->fds[cur_fds_num].fd); } if (sock >= 0 && (sock == pParms->fds[cur_fds_num].fd)) { cur_fds_num++; if (!(pParms->fds[cur_fds_num - 1].revents & POLLIN)) continue; } if (DEBUG4 >= log_min_messages) write_log("PQsocket says there are results from %d", i + 1); /* * Receive and process results from this QE. */ finished = processResults(dispatchResult); /* * Are we through with this QE now? */ if (finished) { if (DEBUG4 >= log_min_messages) write_log ("processResults says we are finished with %d: %s", i + 1, segdbDesc->whoami); dispatchResult->stillRunning = false; if (DEBUG1 >= log_min_messages) { char msec_str[32]; switch (check_log_duration(msec_str, false)) { case 1: case 2: write_log ("duration to dispatch result received from thread %d (seg %d): %s ms", i + 1, dispatchResult->segdbDesc->segindex, msec_str); break; } } if (PQisBusy(dispatchResult->segdbDesc->conn)) write_log ("We thought we were done, because finished==true, but libpq says we are still busy"); } else if (DEBUG4 >= log_min_messages) write_log("processResults says we have more to do with %d: %s", i + 1, segdbDesc->whoami); } } }
static void thread_DispatchWaitSingle(DispatchCommandParms * pParms) { SegmentDatabaseDescriptor *segdbDesc; CdbDispatchResult *dispatchResult; char *msg = NULL; /* * Assert() cannot be used in threads */ if (pParms->db_count != 1) write_log("Bug... thread_dispatchWaitSingle called with db_count %d", pParms->db_count); dispatchResult = pParms->dispatchResultPtrArray[0]; segdbDesc = dispatchResult->segdbDesc; if (PQstatus(segdbDesc->conn) == CONNECTION_BAD) { msg = PQerrorMessage(segdbDesc->conn); /* * Save error info for later. */ cdbdisp_appendMessage(dispatchResult, DEBUG1, ERRCODE_GP_INTERCONNECTION_ERROR, "Lost connection to %s. %s", segdbDesc->whoami, msg ? msg : ""); /* * Free the PGconn object. */ PQfinish(segdbDesc->conn); segdbDesc->conn = NULL; dispatchResult->stillRunning = false; } else { PQsetnonblocking(segdbDesc->conn, FALSE); for (;;) { PGresult *pRes; ExecStatusType resultStatus; int resultIndex = cdbdisp_numPGresult(dispatchResult); if (DEBUG4 >= log_min_messages) write_log("PQgetResult, resultIndex = %d", resultIndex); /* * Get one message. */ pRes = PQgetResult(segdbDesc->conn); CollectQEWriterTransactionInformation(segdbDesc, dispatchResult); /* * Command is complete when PGgetResult() returns NULL. It is critical * that for any connection that had an asynchronous command sent thru * it, we call PQgetResult until it returns NULL. Otherwise, the next * time a command is sent to that connection, it will return an error * that there's a command pending. */ if (!pRes) { if (DEBUG4 >= log_min_messages) { /* * Don't use elog, it's not thread-safe */ write_log("%s -> idle", segdbDesc->whoami); } break; } /* * Attach the PGresult object to the CdbDispatchResult object. */ cdbdisp_appendResult(dispatchResult, pRes); /* * Did a command complete successfully? */ resultStatus = PQresultStatus(pRes); if (resultStatus == PGRES_COMMAND_OK || resultStatus == PGRES_TUPLES_OK || resultStatus == PGRES_COPY_IN || resultStatus == PGRES_COPY_OUT) { /* * Save the index of the last successful PGresult. Can be given to * cdbdisp_getPGresult() to get tuple count, etc. */ dispatchResult->okindex = resultIndex; if (DEBUG3 >= log_min_messages) { /* * Don't use elog, it's not thread-safe */ char *cmdStatus = PQcmdStatus(pRes); write_log("%s -> ok %s", segdbDesc->whoami, cmdStatus ? cmdStatus : "(no cmdStatus)"); } if (resultStatus == PGRES_COPY_IN || resultStatus == PGRES_COPY_OUT) return; } /* * Note QE error. Cancel the whole statement if requested. */ else { char *sqlstate = PQresultErrorField(pRes, PG_DIAG_SQLSTATE); int errcode = 0; msg = PQresultErrorMessage(pRes); if (DEBUG2 >= log_min_messages) { /* * Don't use elog, it's not thread-safe */ write_log("%s -> %s %s %s", segdbDesc->whoami, PQresStatus(resultStatus), sqlstate ? sqlstate : "(no SQLSTATE)", msg ? msg : ""); } /* * Convert SQLSTATE to an error code (ERRCODE_xxx). Use a generic * nonzero error code if no SQLSTATE. */ if (sqlstate && strlen(sqlstate) == 5) errcode = sqlstate_to_errcode(sqlstate); /* * Save first error code and the index of its PGresult buffer * entry. */ cdbdisp_seterrcode(errcode, resultIndex, dispatchResult); } } if (DEBUG4 >= log_min_messages) write_log("processResultsSingle says we are finished with : %s", segdbDesc->whoami); dispatchResult->stillRunning = false; if (DEBUG1 >= log_min_messages) { char msec_str[32]; switch (check_log_duration(msec_str, false)) { case 1: case 2: write_log ("duration to dispatch result received from thread (seg %d): %s ms", dispatchResult->segdbDesc->segindex, msec_str); break; } } if (PQisBusy(dispatchResult->segdbDesc->conn)) write_log ("We thought we were done, because finished==true, but libpq says we are still busy"); } }
/* * Receive and process results from QEs. */ static void handlePollSuccess(CdbDispatchCmdAsync* pParms, struct pollfd *fds) { int currentFdNumber = 0; int i = 0; /* * We have data waiting on one or more of the connections. */ for (i = 0; i < pParms->dispatchCount; i++) { bool finished; int sock; CdbDispatchResult *dispatchResult = pParms->dispatchResultPtrArray[i]; SegmentDatabaseDescriptor *segdbDesc = dispatchResult->segdbDesc; /* * Skip if already finished or didn't dispatch. */ if (!dispatchResult->stillRunning) continue; ELOG_DISPATCHER_DEBUG("looking for results from %d of %d (%s)", i + 1, pParms->dispatchCount, segdbDesc->whoami); sock = PQsocket(segdbDesc->conn); Assert(sock >= 0); Assert(sock == fds[currentFdNumber].fd); /* * Skip this connection if it has no input available. */ if (!(fds[currentFdNumber++].revents & POLLIN)) continue; ELOG_DISPATCHER_DEBUG("PQsocket says there are results from %d of %d (%s)", i + 1, pParms->dispatchCount, segdbDesc->whoami); /* * Receive and process results from this QE. */ finished = processResults(dispatchResult); /* * Are we through with this QE now? */ if (finished) { dispatchResult->stillRunning = false; ELOG_DISPATCHER_DEBUG("processResults says we are finished with %d of %d (%s)", i + 1, pParms->dispatchCount, segdbDesc->whoami); if (DEBUG1 >= log_min_messages) { char msec_str[32]; switch (check_log_duration(msec_str, false)) { case 1: case 2: elog(LOG, "duration to dispatch result received from %d (seg %d): %s ms", i + 1, dispatchResult->segdbDesc->segindex, msec_str); break; } } if (PQisBusy(dispatchResult->segdbDesc->conn)) elog(LOG, "We thought we were done, because finished==true, but libpq says we are still busy"); } else ELOG_DISPATCHER_DEBUG("processResults says we have more to do with %d of %d (%s)", i + 1, pParms->dispatchCount, segdbDesc->whoami); } }