bool Connection::ping() { log_debug("ping()"); if (PQsendQuery(conn, "select 1") == 0) { log_debug("failed to send statement \"select 1\" to database in Connection::ping()"); return false; } while (true) { struct pollfd fd; fd.fd = PQsocket(conn); fd.events = POLLIN; log_debug("wait for input on fd " << fd.fd); if (::poll(&fd, 1, 10000) != 1) { log_debug("no data received in Connection::ping()"); return false; } log_debug("consumeInput"); if (PQconsumeInput(conn) == 0) { log_debug("PQconsumeInput failed in Connection::ping()"); return false; } log_debug("check PQisBusy"); while (PQisBusy(conn) == 0) { log_debug("PQgetResult"); PGresult* result = PQgetResult(conn); log_debug("PQgetResult => " << static_cast<void*>(result)); if (result == 0) return true; log_debug("PQfree"); PQclear(result); } } }
PGconn * pgpool_getfreeconn(void) { assert(initialized); int id = last_used; while (1) { id = (id+1) % poolsize; PGconn *c = pool[id]; assert(c); PGresult *res; int rc; // can we get state AND is the server // not blocking and any results? while ((rc=PQconsumeInput(c)) && !PQisBusy(c) && (res=PQgetResult(c))) { // we have results. we need to clear those resultsets if (reshandler(c, res)) { return NULL; } } if (!rc) { log_fatal("pgpool", "Error in PQconsumeInput. %s", PQerrorMessage(c)); return NULL; } else if (!PQisBusy(c)) { last_used = id; return c; } if (id == last_used) { usleep(150000); } } }
/* Checks whether new data has arrived from the server (on either the snapshot * connection or the replication connection, as appropriate). If yes, it is * processed, and context->status is set to 1. If no data is available, this * function does not block, but returns immediately, and context->status is set * to 0. If the data stream has ended, context->status is set to -1. */ int db_client_poll(client_context_t context) { int err = 0; if (context->sql_conn) { /* To make PQgetResult() non-blocking, check PQisBusy() first */ if (PQisBusy(context->sql_conn)) { context->status = 0; return err; } check(err, snapshot_poll(context)); context->status = 1; /* If the snapshot is finished, switch over to the replication stream */ if (!context->sql_conn) { checkRepl(err, context, replication_stream_start(&context->repl)); } return err; } else { checkRepl(err, context, replication_stream_poll(&context->repl)); context->status = context->repl.status; return err; } }
static void pq_event(evutil_socket_t fd, short event, void *arg) { struct connection_struct* database = (struct connection_struct*) arg; if (database->queries) { if (database->queries->sent == 0) { PQsendQuery(database->conn, database->queries->query); database->queries->sent = 1; } if (PQconsumeInput(database->conn) && !PQisBusy(database->conn)) { PGresult* res = PQgetResult(database->conn); while (res) { if (database->queries->callback) database->queries->callback(res, database->queries->context, database->queries->query); if (database->report_errors && PQresultStatus(res) != PGRES_COMMAND_OK) fprintf(stderr, "Query: '%s' returned error\n\t%s\n", database->queries->query, PQresultErrorMessage(res)); PQclear(res); res = PQgetResult(database->conn); } database->query_count--; struct query_struct* old = database->queries; database->queries = database->queries->next; free(old->query); free(old); pq_event(fd, event, arg); } } }
static void evpg_query_finished(int sock, short which, void **data) { struct evpg_db_node *dbnode; struct evpg_cfg *config; const char *querystr; void (*cb)(PGresult *, void *); void *usrdata; struct event *event; config = data[0]; querystr = data[1]; cb = data[2]; usrdata = data[3]; dbnode = data[4]; event = data[5]; PQconsumeInput(dbnode->dbconn); if (PQisBusy(dbnode->dbconn) == 0) { PGresult *result; result = PQgetResult(dbnode->dbconn); cb(result, usrdata); PQclear(result); free(event); free(data); evpg_set_ready(config, dbnode); return; } /* this query has not finished */ event_set(event, sock, EV_READ, (void *)evpg_query_finished, data); event_add(event, 0); }
/* * Send a query and wait for the results by using the asynchronous libpq * functions and the backend version of select(). * * We must not use the regular blocking libpq functions like PQexec() * since they are uninterruptible by signals on some platforms, such as * Windows. * * We must also not use vanilla select() here since it cannot handle the * signal emulation layer on Windows. * * The function is modeled on PQexec() in libpq, but only implements * those parts that are in use in the walreceiver. * * Queries are always executed on the connection in streamConn. */ static PGresult * libpqrcv_PQexec(const char *query) { PGresult *result = NULL; PGresult *lastResult = NULL; /* * PQexec() silently discards any prior query results on the connection. * This is not required for walreceiver since it's expected that walsender * won't generate any such junk results. */ /* * Submit a query. Since we don't use non-blocking mode, this also can * block. But its risk is relatively small, so we ignore that for now. */ if (!PQsendQuery(streamConn, query)) return NULL; for (;;) { /* * Receive data until PQgetResult is ready to get the result without * blocking. */ while (PQisBusy(streamConn)) { /* * We don't need to break down the sleep into smaller increments, * and check for interrupts after each nap, since we can just * elog(FATAL) within SIGTERM signal handler if the signal arrives * in the middle of establishment of replication connection. */ if (!libpq_select(-1)) continue; /* interrupted */ if (PQconsumeInput(streamConn) == 0) return NULL; /* trouble */ } /* * Emulate the PQexec()'s behavior of returning the last result when * there are many. Since walsender will never generate multiple * results, we skip the concatenation of error messages. */ result = PQgetResult(streamConn); if (result == NULL) break; /* query is complete */ PQclear(lastResult); lastResult = result; if (PQresultStatus(lastResult) == PGRES_COPY_IN || PQresultStatus(lastResult) == PGRES_COPY_OUT || PQresultStatus(lastResult) == PGRES_COPY_BOTH || PQstatus(streamConn) == CONNECTION_BAD) break; } return lastResult; }
PGresult * do_postgres_cCommand_execute_async(VALUE self, VALUE connection, PGconn *db, VALUE query) { PGresult *response; char* str = StringValuePtr(query); while ((response = PQgetResult(db))) { PQclear(response); } struct timeval start; int retval; gettimeofday(&start, NULL); retval = PQsendQuery(db, str); if (!retval) { if (PQstatus(db) != CONNECTION_OK) { PQreset(db); if (PQstatus(db) == CONNECTION_OK) { retval = PQsendQuery(db, str); } else { do_postgres_full_connect(connection, db); retval = PQsendQuery(db, str); } } if (!retval) { rb_raise(eDO_ConnectionError, "%s", PQerrorMessage(db)); } } int socket_fd = PQsocket(db); fd_set rset; while (1) { FD_ZERO(&rset); FD_SET(socket_fd, &rset); retval = rb_thread_select(socket_fd + 1, &rset, NULL, NULL, NULL); if (retval < 0) { rb_sys_fail(0); } if (retval == 0) { continue; } if (PQconsumeInput(db) == 0) { rb_raise(eDO_ConnectionError, "%s", PQerrorMessage(db)); } if (PQisBusy(db) == 0) { break; } } data_objects_debug(connection, query, &start); return PQgetResult(db); }
static int esql_postgresql_io(Esql *e) { if (PQstatus(e->backend.db) == CONNECTION_BAD) { ERR("%s", esql_postgresql_error_get(e)); return ECORE_FD_ERROR; } if (e->current == ESQL_CONNECT_TYPE_INIT) { switch (PQconnectPoll(e->backend.db)) { case PGRES_POLLING_OK: return 0; case PGRES_POLLING_READING: return ECORE_FD_READ; case PGRES_POLLING_WRITING: return ECORE_FD_WRITE; default: ERR("%s", esql_postgresql_error_get(e)); return ECORE_FD_ERROR; } } if (!PQconsumeInput(e->backend.db)) { ERR("%s", esql_postgresql_error_get(e)); return ECORE_FD_ERROR; } if (!PQisBusy(e->backend.db)) return 0; return ECORE_FD_READ | ECORE_FD_WRITE; /* psql does not provide a method to get read/write mode :( */ }
/* * wait until current query finishes ignoring any results, this could be an async command * or a cancelation of a query * return 1 if Ok; 0 if any error ocurred; -1 if timeout reached */ int wait_connection_availability(PGconn *conn, int timeout) { PGresult *res; while(timeout-- >= 0) { if (PQconsumeInput(conn) == 0) { log_warning(_("PQconsumeInput: Query could not be sent to primary. %s\n"), PQerrorMessage(conn)); return 0; } if (PQisBusy(conn) == 0) { res = PQgetResult(conn); if (res == NULL) break; PQclear(res); } sleep(1); } if (timeout >= 0) return 1; else return -1; }
static void write_queue(ParallelWriter *self, const void *buffer, uint32 len) { struct iovec iov[2]; AssertArg(self->conn != NULL); AssertArg(self->queue != NULL); AssertArg(len == 0 || buffer != NULL); iov[0].iov_base = &len; iov[0].iov_len = sizeof(len); iov[1].iov_base = (void *) buffer; iov[1].iov_len = len; for (;;) { if (QueueWrite(self->queue, iov, 2, DEFAULT_TIMEOUT_MSEC, false)) return; PQconsumeInput(self->conn); if (!PQisBusy(self->conn)) { ereport(ERROR, (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), errmsg("unexpected reader termination"), errdetail("%s", finish_and_get_message(self)))); } /* retry */ } }
/* * Helper function that actually kicks off the command on the libpq connection. */ static void dispatchCommand(CdbDispatchResult * dispatchResult, const char *query_text, int query_text_len) { SegmentDatabaseDescriptor *segdbDesc = dispatchResult->segdbDesc; TimestampTz beforeSend = 0; long secs; int usecs; if (DEBUG1 >= log_min_messages) beforeSend = GetCurrentTimestamp(); if (PQisBusy(segdbDesc->conn)) elog(LOG, "Trying to send to busy connection %s: asyncStatus %d", segdbDesc->whoami, segdbDesc->conn->asyncStatus); if (cdbconn_isBadConnection(segdbDesc)) { char *msg = PQerrorMessage(dispatchResult->segdbDesc->conn); dispatchResult->stillRunning = false; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Connection lost before dispatch to segment %s: %s", dispatchResult->segdbDesc->whoami, msg ? msg : "unknown error"))); } /* * Submit the command asynchronously. */ if (PQsendGpQuery_shared(dispatchResult->segdbDesc->conn, (char *) query_text, query_text_len) == 0) { char *msg = PQerrorMessage(dispatchResult->segdbDesc->conn); dispatchResult->stillRunning = false; ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("Command could not be dispatch to segment %s: %s", dispatchResult->segdbDesc->whoami, msg ? msg : "unknown error"))); } if (DEBUG1 >= log_min_messages) { TimestampDifference(beforeSend, GetCurrentTimestamp(), &secs, &usecs); if (secs != 0 || usecs > 1000) /* Time > 1ms? */ elog(LOG, "time for PQsendGpQuery_shared %ld.%06d", secs, usecs); } /* * We'll keep monitoring this QE -- whether or not the command * was dispatched -- in order to check for a lost connection * or any other errors that libpq might have in store for us. */ dispatchResult->stillRunning = true; dispatchResult->hasDispatched = true; ELOG_DISPATCHER_DEBUG("Command dispatched to QE (%s)", dispatchResult->segdbDesc->whoami); }
int pgut_wait(int num, PGconn *connections[], struct timeval *timeout) { /* all connections are busy. wait for finish */ while (!interrupted) { int i; fd_set mask; int maxsock; FD_ZERO(&mask); maxsock = -1; for (i = 0; i < num; i++) { int sock; if (connections[i] == NULL) continue; sock = PQsocket(connections[i]); if (sock >= 0) { FD_SET(sock, &mask); if (maxsock < sock) maxsock = sock; } } if (maxsock == -1) { errno = ENOENT; return -1; } i = wait_for_sockets(maxsock + 1, &mask, timeout); if (i == 0) break; /* timeout */ for (i = 0; i < num; i++) { if (connections[i] && FD_ISSET(PQsocket(connections[i]), &mask)) { PQconsumeInput(connections[i]); if (PQisBusy(connections[i])) continue; return i; } } } errno = EINTR; return -1; }
int db_postgres_async_resume(db_con_t *_h, int fd, db_res_t **_r, void *_priv) { struct pool_con *con = (struct pool_con *)_priv; PGresult *res = NULL; #ifdef EXTRA_DEBUG if (!db_match_async_con(fd, _h)) { LM_BUG("no conn match for fd %d", fd); abort(); } #endif db_switch_to_async(_h, con); if( PQconsumeInput(CON_CONNECTION(_h)) == 0) { LM_ERR("Unable to consume input\n"); db_switch_to_sync(_h); db_store_async_con(_h, con); return -1; } if(PQisBusy(CON_CONNECTION(_h))) { async_status = ASYNC_CONTINUE; db_switch_to_sync(_h); return 1; } while (1) { if ((res = PQgetResult(CON_CONNECTION(_h)))) { CON_RESULT(_h) = res; } else { break; } } if (_r) { if (db_postgres_store_result(_h, _r) != 0) { LM_ERR("failed to store result\n"); db_switch_to_sync(_h); db_store_async_con(_h, con); return -2; } } db_switch_to_sync(_h); db_store_async_con(_h, con); return 0; }
static void close_connections() { if (primary_conn != NULL && PQisBusy(primary_conn) == 1) cancel_query(primary_conn, local_options.master_response_timeout); if (my_local_conn != NULL) PQfinish(my_local_conn); if (primary_conn != NULL && primary_conn != my_local_conn) PQfinish(primary_conn); primary_conn = NULL; my_local_conn = NULL; }
/* * call-seq: * conn.fetch() -> result or nil * conn.fetch() { |result| ... } -> obj * * Fetches the results of the previous Pg::Conn#send call. * See there for an example. * * The result will be +nil+ if there are no more results. */ VALUE pgconn_fetch( VALUE self) { struct pgconn_data *c; PGresult *result; VALUE res; Data_Get_Struct( self, struct pgconn_data, c); pg_check_conninvalid( c); if (PQconsumeInput( c->conn) == 0) pg_raise_connexec( c); if (PQisBusy( c->conn) > 0) return Qnil; result = PQgetResult( c->conn); return result == NULL ? Qnil : yield_or_return_result( pgresult_new( result, c, Qnil, Qnil)); }
ngx_int_t ngx_postgres_upstream_get_ack(ngx_http_request_t *r, ngx_connection_t *pgxc, ngx_postgres_upstream_peer_data_t *pgdt) { PGresult *res; dd("entering"); if (!PQconsumeInput(pgdt->pgconn)) { dd("returning NGX_ERROR"); return NGX_ERROR; } if (PQisBusy(pgdt->pgconn)) { dd("returning NGX_AGAIN"); return NGX_AGAIN; } /* remove result timeout */ if (pgxc->read->timer_set) { ngx_del_timer(pgxc->read); } dd("receiving ACK (ready for next query)"); res = PQgetResult(pgdt->pgconn); if (res != NULL) { dd("receiving ACK failed"); ngx_log_error(NGX_LOG_ERR, pgxc->log, 0, "receiving ACK failed: multiple queries(?)"); PQclear(res); dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } dd("ACK received successfully"); pgxc->log->action = "being idle on PostgreSQL database"; pgdt->state = state_db_idle; dd("returning"); return ngx_postgres_upstream_done(r, r->upstream, pgdt); }
void ngx_postgres_keepalive_close_handler(ngx_event_t *ev) { ngx_postgres_upstream_srv_conf_t *pgscf; ngx_postgres_keepalive_cache_t *item; ngx_connection_t *c; PGresult *res; dd("entering"); c = ev->data; item = c->data; if (c->close) { goto close; } if (PQconsumeInput(item->pgconn) && !PQisBusy(item->pgconn)) { res = PQgetResult(item->pgconn); if (res == NULL) { dd("returning"); return; } PQclear(res); dd("received result on idle keepalive connection"); ngx_log_error(NGX_LOG_ERR, c->log, 0, "postgres: received result on idle keepalive connection"); } close: pgscf = item->srv_conf; ngx_postgres_upstream_free_connection(ev->log, c, item->pgconn, pgscf); ngx_queue_remove(&item->queue); ngx_queue_insert_head(&pgscf->free, &item->queue); dd("returning"); }
/* MultiClientResultStatus checks result status for an asynchronous query. */ ResultStatus MultiClientResultStatus(int32 connectionId) { PGconn *connection = NULL; int consumed = 0; ConnStatusType connStatusType = CONNECTION_OK; ResultStatus resultStatus = CLIENT_INVALID_RESULT_STATUS; Assert(connectionId != INVALID_CONNECTION_ID); connection = ClientConnectionArray[connectionId]; Assert(connection != NULL); connStatusType = PQstatus(connection); if (connStatusType == CONNECTION_BAD) { ereport(WARNING, (errmsg("could not maintain connection to worker node"))); return CLIENT_RESULT_UNAVAILABLE; } /* consume input to allow status change */ consumed = PQconsumeInput(connection); if (consumed != 0) { int connectionBusy = PQisBusy(connection); if (connectionBusy == 0) { resultStatus = CLIENT_RESULT_READY; } else { resultStatus = CLIENT_RESULT_BUSY; } } else { ereport(WARNING, (errmsg("could not consume data from worker node"))); resultStatus = CLIENT_RESULT_UNAVAILABLE; } return resultStatus; }
/* * Wait for the result from a prior asynchronous execution function call. * * This function offers quick responsiveness by checking for any interruptions. * * This function emulates the PQexec()'s behavior of returning the last result * when there are many. * * Caller is responsible for the error handling on the result. */ PGresult * pgfdw_get_result(PGconn *conn, const char *query) { PGresult *last_res = NULL; for (;;) { PGresult *res; while (PQisBusy(conn)) { int wc; /* Sleep until there's something to do */ wc = WaitLatchOrSocket(MyLatch, WL_LATCH_SET | WL_SOCKET_READABLE, PQsocket(conn), -1L, PG_WAIT_EXTENSION); ResetLatch(MyLatch); CHECK_FOR_INTERRUPTS(); /* Data available in socket */ if (wc & WL_SOCKET_READABLE) { if (!PQconsumeInput(conn)) pgfdw_report_error(ERROR, NULL, conn, false, query); } } res = PQgetResult(conn); if (res == NULL) break; /* query is complete */ PQclear(last_res); last_res = res; } return last_res; }
static void ready_input(ErlDrvData drv_data, ErlDrvEvent event) { our_data_t* data = (our_data_t*)drv_data; PGconn* conn = data->conn; ei_x_buff x; PGresult* res; PQconsumeInput(conn); if (PQisBusy(conn)) return; ei_x_new_with_version(&x); res = PQgetResult(conn); encode_result(&x, res, conn); driver_output(data->port, x.buff, x.index); ei_x_free(&x); PQclear(res); for (;;) { res = PQgetResult(conn); if (res == NULL) break; PQclear(res); } }
ngx_int_t ngx_postgres_upstream_get_result(ngx_http_request_t *r, ngx_connection_t *pgxc, ngx_postgres_upstream_peer_data_t *pgdt) { ExecStatusType pgrc; PGresult *res; ngx_int_t rc; dd("entering"); /* remove connection timeout from re-used keepalive connection */ if (pgxc->write->timer_set) { ngx_del_timer(pgxc->write); } if (!PQconsumeInput(pgdt->pgconn)) { dd("returning NGX_ERROR"); return NGX_ERROR; } if (PQisBusy(pgdt->pgconn)) { dd("returning NGX_AGAIN"); return NGX_AGAIN; } dd("receiving result"); res = PQgetResult(pgdt->pgconn); if (res == NULL) { dd("receiving result failed"); ngx_log_error(NGX_LOG_ERR, pgxc->log, 0, "receiving result failed: %s", PQerrorMessage(pgdt->pgconn)); dd("returning NGX_ERROR"); return NGX_ERROR; } pgrc = PQresultStatus(res); if ((pgrc != PGRES_COMMAND_OK) && (pgrc != PGRES_TUPLES_OK)) { dd("receiving result failed"); ngx_log_error(NGX_LOG_ERR, pgxc->log, 0, "receiving result failed: %s: %s", PQresStatus(pgrc), PQerrorMessage(pgdt->pgconn)); PQclear(res); dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } dd("result received successfully, cols:%d rows:%d", PQnfields(res), PQntuples(res)); pgxc->log->action = "processing result from PostgreSQL database"; rc = ngx_postgres_process_response(r, res); PQclear(res); if (rc != NGX_DONE) { dd("returning rc:%d", (int) rc); return rc; } dd("result processed successfully"); pgxc->log->action = "waiting for ACK from PostgreSQL database"; pgdt->state = state_db_get_ack; dd("returning"); return ngx_postgres_upstream_get_ack(r, pgxc, pgdt); }
/* * Our caller already sent the query associated with this step. Wait for it * to either complete or (if given the STEP_NONBLOCK flag) to block while * waiting for a lock. We assume that any lock wait will persist until we * have executed additional steps in the permutation. * * When calling this function on behalf of a given step for a second or later * time, pass the STEP_RETRY flag. This only affects the messages printed. * * If the STEP_NONBLOCK flag was specified and the query is waiting to acquire * a lock, returns true. Otherwise, returns false. */ static bool try_complete_step(Step *step, int flags) { PGconn *conn = conns[1 + step->session]; fd_set read_set; struct timeval timeout; int sock = PQsocket(conn); int ret; PGresult *res; FD_ZERO(&read_set); while (flags & STEP_NONBLOCK && PQisBusy(conn)) { FD_SET(sock, &read_set); timeout.tv_sec = 0; timeout.tv_usec = 10000; /* Check for lock waits every 10ms. */ ret = select(sock + 1, &read_set, NULL, NULL, &timeout); if (ret < 0) /* error in select() */ { fprintf(stderr, "select failed: %s\n", strerror(errno)); exit_nicely(); } else if (ret == 0) /* select() timeout: check for lock wait */ { int ntuples; res = PQexecPrepared(conns[0], PREP_WAITING, 1, &backend_pids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "lock wait query failed: %s", PQerrorMessage(conn)); exit_nicely(); } ntuples = PQntuples(res); PQclear(res); if (ntuples >= 1) /* waiting to acquire a lock */ { if (!(flags & STEP_RETRY)) printf("step %s: %s <waiting ...>\n", step->name, step->sql); return true; } /* else, not waiting: give it more time */ } else if (!PQconsumeInput(conn)) /* select(): data available */ { fprintf(stderr, "PQconsumeInput failed: %s", PQerrorMessage(conn)); exit_nicely(); } } if (flags & STEP_RETRY) printf("step %s: <... completed>\n", step->name); else printf("step %s: %s\n", step->name, step->sql); while ((res = PQgetResult(conn))) { switch (PQresultStatus(res)) { case PGRES_COMMAND_OK: break; case PGRES_TUPLES_OK: printResultSet(res); break; case PGRES_FATAL_ERROR: /* Detail may contain xid values, so just show primary. */ printf("%s: %s\n", PQresultErrorField(res, PG_DIAG_SEVERITY), PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY)); break; default: printf("unexpected result status: %s\n", PQresStatus(PQresultStatus(res))); } PQclear(res); } return false; }
/* * GetIdleSlot * Return a connection slot that is ready to execute a command. * * We return the first slot we find that is marked isFree, if one is; * otherwise, we loop on select() until one socket becomes available. When * this happens, we read the whole set and mark as free all sockets that become * available. * * If an error occurs, NULL is returned. */ static ParallelSlot * GetIdleSlot(ParallelSlot slots[], int numslots, const char *progname) { int i; int firstFree = -1; /* Any connection already known free? */ for (i = 0; i < numslots; i++) { if (slots[i].isFree) return slots + i; } /* * No free slot found, so wait until one of the connections has finished * its task and return the available slot. */ while (firstFree < 0) { fd_set slotset; int maxFd = 0; bool aborting; /* We must reconstruct the fd_set for each call to select_loop */ FD_ZERO(&slotset); for (i = 0; i < numslots; i++) { int sock = PQsocket(slots[i].connection); /* * We don't really expect any connections to lose their sockets * after startup, but just in case, cope by ignoring them. */ if (sock < 0) continue; FD_SET(sock, &slotset); if (sock > maxFd) maxFd = sock; } SetCancelConn(slots->connection); i = select_loop(maxFd, &slotset, &aborting); ResetCancelConn(); if (aborting) { /* * We set the cancel-receiving connection to the one in the zeroth * slot above, so fetch the error from there. */ GetQueryResult(slots->connection, progname); return NULL; } Assert(i != 0); for (i = 0; i < numslots; i++) { int sock = PQsocket(slots[i].connection); if (sock >= 0 && FD_ISSET(sock, &slotset)) { /* select() says input is available, so consume it */ PQconsumeInput(slots[i].connection); } /* Collect result(s) as long as any are available */ while (!PQisBusy(slots[i].connection)) { PGresult *result = PQgetResult(slots[i].connection); if (result != NULL) { /* Check and discard the command result */ if (!ProcessQueryResult(slots[i].connection, result, progname)) return NULL; } else { /* This connection has become idle */ slots[i].isFree = true; if (firstFree < 0) firstFree = i; break; } } } } return slots + firstFree; }
/* * pqSetenvPoll * * Polls the process of passing the values of a standard set of environment * variables to the backend. */ PostgresPollingStatusType pqSetenvPoll(PGconn *conn) { PGresult *res; if (conn == NULL || conn->status == CONNECTION_BAD) return PGRES_POLLING_FAILED; /* Check whether there are any data for us */ switch (conn->setenv_state) { /* These are reading states */ case SETENV_STATE_CLIENT_ENCODING_WAIT: case SETENV_STATE_OPTION_WAIT: case SETENV_STATE_QUERY1_WAIT: case SETENV_STATE_QUERY2_WAIT: { /* Load waiting data */ int n = pqReadData(conn); if (n < 0) goto error_return; if (n == 0) return PGRES_POLLING_READING; break; } /* These are writing states, so we just proceed. */ case SETENV_STATE_CLIENT_ENCODING_SEND: case SETENV_STATE_OPTION_SEND: case SETENV_STATE_QUERY1_SEND: case SETENV_STATE_QUERY2_SEND: break; /* Should we raise an error if called when not active? */ case SETENV_STATE_IDLE: return PGRES_POLLING_OK; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "invalid setenv state %c, " "probably indicative of memory corruption\n" ), conn->setenv_state); goto error_return; } /* We will loop here until there is nothing left to do in this call. */ for (;;) { switch (conn->setenv_state) { /* * The _CLIENT_ENCODING_SEND code is slightly different from * _OPTION_SEND below (e.g., no getenv() call), which is why a * different state is used. */ case SETENV_STATE_CLIENT_ENCODING_SEND: { char setQuery[100]; /* note length limit in * sprintf below */ const char *val = conn->client_encoding_initial; if (val) { if (pg_strcasecmp(val, "default") == 0) sprintf(setQuery, "SET client_encoding = DEFAULT"); else sprintf(setQuery, "SET client_encoding = '%.60s'", val); #ifdef CONNECTDEBUG fprintf(stderr, "Sending client_encoding with %s\n", setQuery); #endif if (!PQsendQuery(conn, setQuery)) goto error_return; conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT; } else conn->setenv_state = SETENV_STATE_OPTION_SEND; break; } case SETENV_STATE_OPTION_SEND: { /* * Send SET commands for stuff directed by Environment * Options. Note: we assume that SET commands won't start * transaction blocks, even in a 7.3 server with * autocommit off. */ char setQuery[100]; /* note length limit in * sprintf below */ if (conn->next_eo->envName) { const char *val; if ((val = getenv(conn->next_eo->envName))) { if (pg_strcasecmp(val, "default") == 0) sprintf(setQuery, "SET %s = DEFAULT", conn->next_eo->pgName); else sprintf(setQuery, "SET %s = '%.60s'", conn->next_eo->pgName, val); #ifdef CONNECTDEBUG fprintf(stderr, "Use environment variable %s to send %s\n", conn->next_eo->envName, setQuery); #endif if (!PQsendQuery(conn, setQuery)) goto error_return; conn->setenv_state = SETENV_STATE_OPTION_WAIT; } else conn->next_eo++; } else { /* No more options to send, so move on to querying */ conn->setenv_state = SETENV_STATE_QUERY1_SEND; } break; } case SETENV_STATE_CLIENT_ENCODING_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { if (PQresultStatus(res) != PGRES_COMMAND_OK) { PQclear(res); goto error_return; } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, so send the next option */ conn->setenv_state = SETENV_STATE_OPTION_SEND; } break; } case SETENV_STATE_OPTION_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { if (PQresultStatus(res) != PGRES_COMMAND_OK) { PQclear(res); goto error_return; } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, so send the next option */ conn->next_eo++; conn->setenv_state = SETENV_STATE_OPTION_SEND; } break; } case SETENV_STATE_QUERY1_SEND: { /* * Issue query to get information we need. Here we must * use begin/commit in case autocommit is off by default * in a 7.3 server. * * Note: version() exists in all protocol-2.0-supporting * backends. In 7.3 it would be safer to write * pg_catalog.version(), but we can't do that without * causing problems on older versions. */ if (!PQsendQuery(conn, "begin; select version(); end")) goto error_return; conn->setenv_state = SETENV_STATE_QUERY1_WAIT; return PGRES_POLLING_READING; } case SETENV_STATE_QUERY1_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { char *val; if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* ignore begin/commit command results */ PQclear(res); continue; } if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) { PQclear(res); goto error_return; } /* * Extract server version and save as if * ParameterStatus */ val = PQgetvalue(res, 0, 0); if (val && strncmp(val, "PostgreSQL ", 11) == 0) { char *ptr; /* strip off PostgreSQL part */ val += 11; /* * strip off platform part (scribbles on result, * naughty naughty) */ ptr = strchr(val, ' '); if (ptr) *ptr = '\0'; pqSaveParameterStatus(conn, "server_version", val); } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, move to next */ conn->setenv_state = SETENV_STATE_QUERY2_SEND; } break; } case SETENV_STATE_QUERY2_SEND: { const char *query; /* * pg_client_encoding does not exist in pre-7.2 servers. * So we need to be prepared for an error here. Do *not* * start a transaction block, except in 7.3 servers where * we need to prevent autocommit-off from starting a * transaction anyway. */ if (conn->sversion >= 70300 && conn->sversion < 70400) query = "begin; select pg_catalog.pg_client_encoding(); end"; else query = "select pg_client_encoding()"; if (!PQsendQuery(conn, query)) goto error_return; conn->setenv_state = SETENV_STATE_QUERY2_WAIT; return PGRES_POLLING_READING; } case SETENV_STATE_QUERY2_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { const char *val; if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* ignore begin/commit command results */ PQclear(res); continue; } if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) { /* Extract client encoding and save it */ val = PQgetvalue(res, 0, 0); if (val && *val) /* null should not happen, but */ pqSaveParameterStatus(conn, "client_encoding", val); } else { /* * Error: presumably function not available, so * use PGCLIENTENCODING or SQL_ASCII as the * fallback. */ val = getenv("PGCLIENTENCODING"); if (val && *val) pqSaveParameterStatus(conn, "client_encoding", val); else pqSaveParameterStatus(conn, "client_encoding", "SQL_ASCII"); } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, so we're done */ conn->setenv_state = SETENV_STATE_IDLE; return PGRES_POLLING_OK; } break; } default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid state %c, " "probably indicative of memory corruption\n"), conn->setenv_state); goto error_return; } } /* Unreachable */ error_return: conn->setenv_state = SETENV_STATE_IDLE; return PGRES_POLLING_FAILED; }
/* * PQendcopy * * See fe-exec.c for documentation. */ int pqEndcopy2(PGconn *conn) { PGresult *result; if (conn->asyncStatus != PGASYNC_COPY_IN && conn->asyncStatus != PGASYNC_COPY_OUT) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("no COPY in progress\n")); return 1; } /* * make sure no data is waiting to be sent, abort if we are non-blocking * and the flush fails */ if (pqFlush(conn) && pqIsnonblocking(conn)) return 1; /* non blocking connections may have to abort at this point. */ if (pqIsnonblocking(conn) && PQisBusy(conn)) return 1; /* Return to active duty */ conn->asyncStatus = PGASYNC_BUSY; resetPQExpBuffer(&conn->errorMessage); /* Wait for the completion response */ result = PQgetResult(conn); /* Expecting a successful result */ if (result && result->resultStatus == PGRES_COMMAND_OK) { PQclear(result); return 0; } /* * Trouble. For backwards-compatibility reasons, we issue the error * message as if it were a notice (would be nice to get rid of this * silliness, but too many apps probably don't handle errors from * PQendcopy reasonably). Note that the app can still obtain the error * status from the PGconn object. */ if (conn->errorMessage.len > 0) { /* We have to strip the trailing newline ... pain in neck... */ char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; if (svLast == '\n') conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; } PQclear(result); /* * The worst case is that we've lost sync with the backend entirely due to * application screwup of the copy in/out protocol. To recover, reset the * connection (talk about using a sledgehammer...) */ pqInternalNotice(&conn->noticeHooks, "lost synchronization with server, resetting connection"); /* * Users doing non-blocking connections need to handle the reset * themselves, they'll need to check the connection status if we return an * error. */ if (pqIsnonblocking(conn)) PQresetStart(conn); else PQreset(conn); return 1; }
/* ---------- * remoteListen_receive_events * * Retrieve all new events that origin from nodes for which we listen on this * node as provider and add them to the node specific worker message queue. * ---------- */ static int remoteListen_receive_events(SlonNode * node, SlonConn * conn, struct listat * listat) { SlonNode *origin; SlonDString query; SlonDString q2; char *where_or_or; char seqno_buf[64]; PGresult *res; int ntuples; int tupno; time_t timeout; time_t now; dstring_init(&query); /* * In the runtime configuration info for the node, we remember the last * event sequence that we actually have received. If the remote worker * thread has processed it yet or it isn't important, we have it in the * message queue at least and don't need to select it again. * * So the query we construct contains a qualification (ev_origin = * <remote_node> and ev_seqno > <last_seqno>) per remote node we're listen * for here. */ monitor_state("remote listener", node->no_id, conn->conn_pid, "receiving events", 0, "n/a"); (void) slon_mkquery(&query, "select ev_origin, ev_seqno, ev_timestamp, " " ev_snapshot, " " \"pg_catalog\".txid_snapshot_xmin(ev_snapshot), " " \"pg_catalog\".txid_snapshot_xmax(ev_snapshot), " " ev_type, " " ev_data1, ev_data2, " " ev_data3, ev_data4, " " ev_data5, ev_data6, " " ev_data7, ev_data8 " "from %s.sl_event e", rtcfg_namespace); rtcfg_lock(); where_or_or = "where"; if (lag_interval) { dstring_init(&q2); (void) slon_mkquery(&q2, "where ev_timestamp < now() - '%s'::interval and (", lag_interval); where_or_or = dstring_data(&q2); } while (listat) { if ((origin = rtcfg_findNode(listat->li_origin)) == NULL) { rtcfg_unlock(); slon_log(SLON_ERROR, "remoteListenThread_%d: unknown node %d\n", node->no_id, listat->li_origin); dstring_free(&query); return -1; } sprintf(seqno_buf, INT64_FORMAT, origin->last_event); slon_appendquery(&query, " %s (e.ev_origin = '%d' and e.ev_seqno > '%s')", where_or_or, listat->li_origin, seqno_buf); where_or_or = "or"; listat = listat->next; } if (lag_interval) { slon_appendquery(&query, ")"); } /* * Limit the result set size to: sync_group_maxsize * 2, if it's set 100, * if sync_group_maxsize isn't set */ slon_appendquery(&query, " order by e.ev_origin, e.ev_seqno limit %d", (sync_group_maxsize > 0) ? sync_group_maxsize * 2 : 100); rtcfg_unlock(); if (PQsendQuery(conn->dbconn, dstring_data(&query)) == 0) { slon_log(SLON_ERROR, "remoteListenThread_%d: \"%s\" - %s", node->no_id, dstring_data(&query), PQerrorMessage(conn->dbconn)); dstring_free(&query); return -1; } (void) time(&timeout); timeout += remote_listen_timeout; while (PQisBusy(conn->dbconn) != 0) { (void) time(&now); if (now >= timeout) { slon_log(SLON_ERROR, "remoteListenThread_%d: timeout (%d s) for event selection\n", node->no_id, remote_listen_timeout); dstring_free(&query); return -1; } if (PQconsumeInput(conn->dbconn) == 0) { slon_log(SLON_ERROR, "remoteListenThread_%d: \"%s\" - %s", node->no_id, dstring_data(&query), PQerrorMessage(conn->dbconn)); dstring_free(&query); return -1; } if (PQisBusy(conn->dbconn) != 0) sched_wait_time(conn, SCHED_WAIT_SOCK_READ, 10000); } res = PQgetResult(conn->dbconn); if (PQresultStatus(res) != PGRES_TUPLES_OK) { slon_log(SLON_ERROR, "remoteListenThread_%d: \"%s\" - %s", node->no_id, dstring_data(&query), PQresultErrorMessage(res)); PQclear(res); dstring_free(&query); return -1; } dstring_free(&query); /* * Add all events found to the remote worker message queue. */ ntuples = PQntuples(res); /* If we drew in the maximum number of events */ if (ntuples == ((sync_group_maxsize > 0) ? sync_group_maxsize * 2 : 100)) sel_max_events++; /* Add to the count... */ else sel_max_events = 0; /* reset the count */ for (tupno = 0; tupno < ntuples; tupno++) { int ev_origin; int64 ev_seqno; ev_origin = (int) strtol(PQgetvalue(res, tupno, 0), NULL, 10); (void) slon_scanint64(PQgetvalue(res, tupno, 1), &ev_seqno); slon_log(SLON_DEBUG2, "remoteListenThread_%d: " "queue event %d,%s %s\n", node->no_id, ev_origin, PQgetvalue(res, tupno, 1), PQgetvalue(res, tupno, 6)); remoteWorker_event(node->no_id, ev_origin, ev_seqno, PQgetvalue(res, tupno, 2), /* ev_timestamp */ PQgetvalue(res, tupno, 3), /* ev_snapshot */ PQgetvalue(res, tupno, 4), /* mintxid */ PQgetvalue(res, tupno, 5), /* maxtxid */ PQgetvalue(res, tupno, 6), /* ev_type */ (PQgetisnull(res, tupno, 7)) ? NULL : PQgetvalue(res, tupno, 7), (PQgetisnull(res, tupno, 8)) ? NULL : PQgetvalue(res, tupno, 8), (PQgetisnull(res, tupno, 9)) ? NULL : PQgetvalue(res, tupno, 9), (PQgetisnull(res, tupno, 10)) ? NULL : PQgetvalue(res, tupno, 10), (PQgetisnull(res, tupno, 11)) ? NULL : PQgetvalue(res, tupno, 11), (PQgetisnull(res, tupno, 12)) ? NULL : PQgetvalue(res, tupno, 12), (PQgetisnull(res, tupno, 13)) ? NULL : PQgetvalue(res, tupno, 13), (PQgetisnull(res, tupno, 14)) ? NULL : PQgetvalue(res, tupno, 14)); } if (ntuples > 0) { if ((sel_max_events > 2) && (sync_group_maxsize > 100)) { slon_log(SLON_INFO, "remoteListenThread_%d: drew maximum # of events for %d iterations\n", node->no_id, sel_max_events); sched_msleep(node, 10000 + (1000 * sel_max_events)); } else { poll_sleep = 0; } } else { poll_sleep = poll_sleep * 2 + sync_interval; if (poll_sleep > sync_interval_timeout) { poll_sleep = sync_interval_timeout; } } PQclear(res); monitor_state("remote listener", node->no_id, conn->conn_pid, "thread main loop", 0, "n/a"); return 0; }
int pgQueryThread::execute() { rowsInserted = -1L; if (!conn->conn) return(raiseEvent(0)); wxCharBuffer queryBuf = query.mb_str(*conn->conv); if (!queryBuf && !query.IsEmpty()) { conn->SetLastResultError(NULL, _("The query could not be converted to the required encoding.")); return(raiseEvent(0)); } if (!PQsendQuery(conn->conn, queryBuf)) { conn->SetLastResultError(NULL); conn->IsAlive(); return(raiseEvent(0)); } int resultsRetrieved = 0; PGresult *lastResult = 0; while (true) { if (TestDestroy()) { if (rc != -3) { if (!PQrequestCancel(conn->conn)) // could not abort; abort failed. return(raiseEvent(-1)); rc = -3; } } if (!PQconsumeInput(conn->conn)) return(raiseEvent(0)); if (PQisBusy(conn->conn)) { Yield(); this->Sleep(10); continue; } // If resultToRetrieve is given, the nth result will be returned, // otherwise the last result set will be returned. // all others are discarded PGresult *res = PQgetResult(conn->conn); if (!res) break; resultsRetrieved++; if (resultsRetrieved == resultToRetrieve) { result = res; insertedOid = PQoidValue(res); if (insertedOid && insertedOid != (OID) - 1) appendMessage(wxString::Format(_("Query inserted one row with OID %d.\n"), insertedOid)); else appendMessage(wxString::Format(wxPLURAL("Query result with %d row will be returned.\n", "Query result with %d rows will be returned.\n", PQntuples(result)))); continue; } if (lastResult) { if (PQntuples(lastResult)) appendMessage(wxString::Format(wxPLURAL("Query result with %d row discarded.\n", "Query result with %d rows discarded.\n", PQntuples(lastResult)))); PQclear(lastResult); } lastResult = res; } if (!result) result = lastResult; conn->SetLastResultError(result); appendMessage(wxT("\n")); rc = PQresultStatus(result); insertedOid = PQoidValue(result); if (insertedOid == (OID) - 1) insertedOid = 0; if (rc == PGRES_TUPLES_OK) { dataSet = new pgSet(result, conn, *conn->conv, conn->needColQuoting); dataSet->MoveFirst(); } else if (rc == PGRES_COMMAND_OK) { char *s = PQcmdTuples(result); if (*s) rowsInserted = atol(s); } else if (rc == PGRES_FATAL_ERROR) { appendMessage(conn->GetLastError() + wxT("\n")); } return(raiseEvent(1)); }
/* * GetIdleSlot * Return a connection slot that is ready to execute a command. * * We return the first slot we find that is marked isFree, if one is; * otherwise, we loop on select() until one socket becomes available. When * this happens, we read the whole set and mark as free all sockets that become * available. * * Process the slot list, if any free slot is available then return the slotid * else perform the select on all the socket's and wait until at least one slot * becomes available. * * If an error occurs, NULL is returned. */ static ParallelSlot * GetIdleSlot(ParallelSlot slots[], int numslots, const char *dbname, const char *progname) { int i; int firstFree = -1; fd_set slotset; pgsocket maxFd; for (i = 0; i < numslots; i++) if ((slots + i)->isFree) return slots + i; FD_ZERO(&slotset); maxFd = slots->sock; for (i = 0; i < numslots; i++) { FD_SET((slots + i)->sock, &slotset); if ((slots + i)->sock > maxFd) maxFd = (slots + i)->sock; } /* * No free slot found, so wait until one of the connections has finished * its task and return the available slot. */ for (firstFree = -1; firstFree < 0;) { bool aborting; SetCancelConn(slots->connection); i = select_loop(maxFd, &slotset, &aborting); ResetCancelConn(); if (aborting) { /* * We set the cancel-receiving connection to the one in the zeroth * slot above, so fetch the error from there. */ GetQueryResult(slots->connection, dbname, progname); return NULL; } Assert(i != 0); for (i = 0; i < numslots; i++) { if (!FD_ISSET((slots + i)->sock, &slotset)) continue; PQconsumeInput((slots + i)->connection); if (PQisBusy((slots + i)->connection)) continue; (slots + i)->isFree = true; if (!GetQueryResult((slots + i)->connection, dbname, progname)) return NULL; if (firstFree < 0) firstFree = i; } } return slots + firstFree; }
/* * Our caller already sent the query associated with this step. Wait for it * to either complete or (if given the STEP_NONBLOCK flag) to block while * waiting for a lock. We assume that any lock wait will persist until we * have executed additional steps in the permutation. * * When calling this function on behalf of a given step for a second or later * time, pass the STEP_RETRY flag. This only affects the messages printed. * * If the connection returns an error, the message is saved in step->errormsg. * Caller should call report_error_message shortly after this, to have it * printed and cleared. * * If the STEP_NONBLOCK flag was specified and the query is waiting to acquire * a lock, returns true. Otherwise, returns false. */ static bool try_complete_step(Step * step, int flags) { PGconn *conn = conns[1 + step->session]; fd_set read_set; struct timeval timeout; int sock = PQsocket(conn); int ret; PGresult *res; FD_ZERO(&read_set); while ((flags & STEP_NONBLOCK) && PQisBusy(conn)) { FD_SET(sock, &read_set); timeout.tv_sec = 0; timeout.tv_usec = 10000; /* Check for lock waits every 10ms. */ ret = select(sock + 1, &read_set, NULL, NULL, &timeout); if (ret < 0) /* error in select() */ { if (errno == EINTR) continue; fprintf(stderr, "select failed: %s\n", strerror(errno)); exit_nicely(); } else if (ret == 0) /* select() timeout: check for lock wait */ { int ntuples; res = PQexecPrepared(conns[0], PREP_WAITING, 1, &backend_pids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "lock wait query failed: %s", PQerrorMessage(conn)); exit_nicely(); } ntuples = PQntuples(res); PQclear(res); if (ntuples >= 1) /* waiting to acquire a lock */ { if (!(flags & STEP_RETRY)) printf("step %s: %s <waiting ...>\n", step->name, step->sql); return true; } /* else, not waiting: give it more time */ } else if (!PQconsumeInput(conn)) /* select(): data available */ { fprintf(stderr, "PQconsumeInput failed: %s\n", PQerrorMessage(conn)); exit_nicely(); } } if (flags & STEP_RETRY) printf("step %s: <... completed>\n", step->name); else printf("step %s: %s\n", step->name, step->sql); while ((res = PQgetResult(conn))) { switch (PQresultStatus(res)) { case PGRES_COMMAND_OK: break; case PGRES_TUPLES_OK: printResultSet(res); break; case PGRES_FATAL_ERROR: if (step->errormsg != NULL) { printf("WARNING: this step had a leftover error message\n"); printf("%s\n", step->errormsg); } /* * Detail may contain XID values, so we want to just show * primary. Beware however that libpq-generated error results * may not contain subfields, only an old-style message. */ { const char *sev = PQresultErrorField(res, PG_DIAG_SEVERITY); const char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); if (sev && msg) { step->errormsg = malloc(5 + strlen(sev) + strlen(msg)); sprintf(step->errormsg, "%s: %s", sev, msg); } else step->errormsg = strdup(PQresultErrorMessage(res)); } break; default: printf("unexpected result status: %s\n", PQresStatus(PQresultStatus(res))); } PQclear(res); } return false; }
/* * Insert monitor info, this is basically the time and xlog replayed, * applied on standby and current xlog location in primary. * Also do the math to see how far are we in bytes for being uptodate */ static void MonitorExecute(void) { PGresult *res; char monitor_standby_timestamp[MAXLEN]; char last_wal_primary_location[MAXLEN]; char last_wal_standby_received[MAXLEN]; char last_wal_standby_applied[MAXLEN]; unsigned long long int lsn_primary; unsigned long long int lsn_standby_received; unsigned long long int lsn_standby_applied; int connection_retries; /* * Check if the master is still available, if after 5 minutes of retries * we cannot reconnect, try to get a new master. */ for (connection_retries = 0; connection_retries < 15; connection_retries++) { if (PQstatus(primaryConn) != CONNECTION_OK) { log_warning(_("Connection to master has been lost, trying to recover...\n")); /* wait 20 seconds between retries */ sleep(20); PQreset(primaryConn); } else { if (connection_retries > 0) { log_notice(_("Connection to master has been restored, continue monitoring.\n")); } break; } } if (PQstatus(primaryConn) != CONNECTION_OK) { log_err(_("We couldn't reconnect to master. Now checking if another node has been promoted.\n")); for (connection_retries = 0; connection_retries < 6; connection_retries++) { primaryConn = getMasterConnection(myLocalConn, local_options.node, local_options.cluster_name, &primary_options.node,NULL); if (PQstatus(primaryConn) == CONNECTION_OK) { /* Connected, we can continue the process so break the loop */ log_err(_("Connected to node %d, continue monitoring.\n"), primary_options.node); break; } else { log_err(_("We haven't found a new master, waiting before retry...\n")); /* wait 5 minutes before retries, after 6 failures (30 minutes) we stop trying */ sleep(300); } } } if (PQstatus(primaryConn) != CONNECTION_OK) { log_err(_("We couldn't reconnect for long enough, exiting...\n")); exit(ERR_DB_CON); } /* Check if we still are a standby, we could have been promoted */ if (!is_standby(myLocalConn)) { log_err(_("It seems like we have been promoted, so exit from monitoring...\n")); CloseConnections(); exit(ERR_PROMOTED); } /* * first check if there is a command being executed, * and if that is the case, cancel the query so i can * insert the current record */ if (PQisBusy(primaryConn) == 1) CancelQuery(); /* Get local xlog info */ sqlquery_snprintf( sqlquery, "SELECT CURRENT_TIMESTAMP, pg_last_xlog_receive_location(), " "pg_last_xlog_replay_location()"); res = PQexec(myLocalConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { log_err("PQexec failed: %s\n", PQerrorMessage(myLocalConn)); PQclear(res); /* if there is any error just let it be and retry in next loop */ return; } strncpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0), MAXLEN); strncpy(last_wal_standby_received , PQgetvalue(res, 0, 1), MAXLEN); strncpy(last_wal_standby_applied , PQgetvalue(res, 0, 2), MAXLEN); PQclear(res); /* Get primary xlog info */ sqlquery_snprintf(sqlquery, "SELECT pg_current_xlog_location() "); res = PQexec(primaryConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { log_err("PQexec failed: %s\n", PQerrorMessage(primaryConn)); PQclear(res); return; } strncpy(last_wal_primary_location, PQgetvalue(res, 0, 0), MAXLEN); PQclear(res); /* Calculate the lag */ lsn_primary = walLocationToBytes(last_wal_primary_location); lsn_standby_received = walLocationToBytes(last_wal_standby_received); lsn_standby_applied = walLocationToBytes(last_wal_standby_applied); if (only_one_entry && only_one_entry_desired) { sqlquery_snprintf(sqlquery, "UPDATE %s.repl_monitor " "VALUES(%d, %d, '%s'::timestamp with time zone, " " '%s', '%s', " " %lld, %lld)" "WHERE primary_node=%d AND secondary_node=%d", repmgr_schema, primary_options.node, local_options.node, monitor_standby_timestamp, last_wal_primary_location, last_wal_standby_received, (lsn_primary - lsn_standby_received), (lsn_standby_received - lsn_standby_applied)); res = PQexec(primaryConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { log_err("PQexec failed: %s\n", PQerrorMessage(conn)); PQclear(res); CloseConnections(); exit(ERR_DB_QUERY); } if (PQntuples(res) != 1) { only_one_entry = false; } PQclear(res); } else { /* * Build and send insert */ sqlquery_snprintf(sqlquery, "INSERT INTO %s.repl_monitor " "VALUES(%d, %d, '%s'::timestamp with time zone, " " '%s', '%s', " " %lld, %lld)", repmgr_schema, primary_options.node, local_options.node, monitor_standby_timestamp, last_wal_primary_location, last_wal_standby_received, (lsn_primary - lsn_standby_received), (lsn_standby_received - lsn_standby_applied)); res = PQexec(primaryConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { log_err("PQexec failed: %s\n", PQerrorMessage(conn)); PQclear(res); CloseConnections(); exit(ERR_DB_QUERY); } PQclear(res); if (only_one_entry_desired) { /* * Build the SQL to execute on primary */ sqlquery_snprintf(sqlquery, "DELETE FROM %s.repl_monitor " "WHERE primary_node=%d AND standby_node=%d AND last_monitor_time < '%s'::timestamp with time zone", repmgr_schema, primary_options.node, local_options.node, monitor_standby_timestamp); res = PQexec(primaryConn, sqlquery); if (PQresultStatus(res) != PGRES_TUPLES_OK) { log_err("PQexec failed: %s\n", PQerrorMessage(conn)); PQclear(res); CloseConnections(); exit(ERR_DB_QUERY); } PQclear(res); only_one_entry = true; } } }