/* * Cleanup work if something breaks while executing the query */ void Persistent_ExecuteQuery_Cleanup(void) { Assert(oldMemoryContext && savedResourceOwner); if (connected) { SPI_finish(); connected=false; } AbortCurrentTransaction(); MemoryContextSwitchTo(oldMemoryContext); CurrentResourceOwner = savedResourceOwner; oldMemoryContext = NULL; savedResourceOwner = NULL; }
/* * kafka_consume_main * * Main function for Kafka consumers running as background workers */ void kafka_consume_main(Datum arg) { char err_msg[512]; rd_kafka_topic_conf_t *topic_conf; rd_kafka_t *kafka; rd_kafka_topic_t *topic; rd_kafka_message_t **messages; const struct rd_kafka_metadata *meta; struct rd_kafka_metadata_topic topic_meta; rd_kafka_resp_err_t err; bool found; Oid id = (Oid) arg; ListCell *lc; KafkaConsumerProc *proc = hash_search(consumer_procs, &id, HASH_FIND, &found); KafkaConsumer consumer; CopyStmt *copy; int valid_brokers = 0; int i; int my_partitions = 0; if (!found) elog(ERROR, "kafka consumer %d not found", id); pqsignal(SIGTERM, kafka_consume_main_sigterm); #define BACKTRACE_SEGFAULTS #ifdef BACKTRACE_SEGFAULTS pqsignal(SIGSEGV, debug_segfault); #endif /* we're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* give this proc access to the database */ BackgroundWorkerInitializeConnection(NameStr(proc->dbname), NULL); /* load saved consumer state */ StartTransactionCommand(); load_consumer_state(proc->consumer_id, &consumer); copy = get_copy_statement(&consumer); topic_conf = rd_kafka_topic_conf_new(); kafka = rd_kafka_new(RD_KAFKA_CONSUMER, NULL, err_msg, sizeof(err_msg)); rd_kafka_set_logger(kafka, logger); /* * Add all brokers currently in pipeline_kafka_brokers */ if (consumer.brokers == NIL) elog(ERROR, "no valid brokers were found"); foreach(lc, consumer.brokers) valid_brokers += rd_kafka_brokers_add(kafka, lfirst(lc)); if (!valid_brokers) elog(ERROR, "no valid brokers were found"); /* * Set up our topic to read from */ topic = rd_kafka_topic_new(kafka, consumer.topic, topic_conf); err = rd_kafka_metadata(kafka, false, topic, &meta, CONSUMER_TIMEOUT); if (err != RD_KAFKA_RESP_ERR_NO_ERROR) elog(ERROR, "failed to acquire metadata: %s", rd_kafka_err2str(err)); Assert(meta->topic_cnt == 1); topic_meta = meta->topics[0]; load_consumer_offsets(&consumer, &topic_meta, proc->offset); CommitTransactionCommand(); /* * Begin consuming all partitions that this process is responsible for */ for (i = 0; i < topic_meta.partition_cnt; i++) { int partition = topic_meta.partitions[i].id; Assert(partition <= consumer.num_partitions); if (partition % consumer.parallelism != proc->partition_group) continue; elog(LOG, "[kafka consumer] %s <- %s consuming partition %d from offset %ld", consumer.rel->relname, consumer.topic, partition, consumer.offsets[partition]); if (rd_kafka_consume_start(topic, partition, consumer.offsets[partition]) == -1) elog(ERROR, "failed to start consuming: %s", rd_kafka_err2str(rd_kafka_errno2err(errno))); my_partitions++; } /* * No point doing anything if we don't have any partitions assigned to us */ if (my_partitions == 0) { elog(LOG, "[kafka consumer] %s <- %s consumer %d doesn't have any partitions to read from", consumer.rel->relname, consumer.topic, MyProcPid); goto done; } messages = palloc0(sizeof(rd_kafka_message_t) * consumer.batch_size); /* * Consume messages until we are terminated */ while (!got_sigterm) { ssize_t num_consumed; int i; int messages_buffered = 0; int partition; StringInfoData buf; bool xact = false; for (partition = 0; partition < consumer.num_partitions; partition++) { if (partition % consumer.parallelism != proc->partition_group) continue; num_consumed = rd_kafka_consume_batch(topic, partition, CONSUMER_TIMEOUT, messages, consumer.batch_size); if (num_consumed <= 0) continue; if (!xact) { StartTransactionCommand(); xact = true; } initStringInfo(&buf); for (i = 0; i < num_consumed; i++) { if (messages[i]->payload != NULL) { appendBinaryStringInfo(&buf, messages[i]->payload, messages[i]->len); if (buf.len > 0 && buf.data[buf.len - 1] != '\n') appendStringInfoChar(&buf, '\n'); messages_buffered++; } consumer.offsets[partition] = messages[i]->offset; rd_kafka_message_destroy(messages[i]); } } if (!xact) { pg_usleep(1 * 1000); continue; } /* we don't want to die in the event of any errors */ PG_TRY(); { if (messages_buffered) execute_copy(copy, &buf); } PG_CATCH(); { elog(LOG, "[kafka consumer] %s <- %s failed to process batch, dropped %d message%s:", consumer.rel->relname, consumer.topic, (int) num_consumed, (num_consumed == 1 ? "" : "s")); EmitErrorReport(); FlushErrorState(); AbortCurrentTransaction(); xact = false; } PG_END_TRY(); if (!xact) StartTransactionCommand(); if (messages_buffered) save_consumer_state(&consumer, proc->partition_group); CommitTransactionCommand(); } done: hash_search(consumer_procs, &id, HASH_REMOVE, NULL); rd_kafka_topic_destroy(topic); rd_kafka_destroy(kafka); rd_kafka_wait_destroyed(CONSUMER_TIMEOUT); }
static void QDMirroringUpdate( QDMIRRORUpdateMask updateMask, bool validFlag, QDMIRRORState state, QDMIRRORDisabledReason disabledReason, struct timeval *lastLogTimeVal, char *errorMessage) { #define UPDATE_VALIDFLAG_CMD "update gp_configuration set valid='%c' where dbid = CAST(%d AS SMALLINT)" #define UPDATE_MASTER_MIRRORING_CMD "update gp_master_mirroring set (summary_state, detail_state, log_time, error_message) = ('%s', %s, '%s'::timestamptz, %s);" int count = 0; char cmd[200 + QDMIRRORErrorMessageSize * 2 + 3]; char detailValue[100]; char logTimeStr[128]; char *summaryStateString; char *detailStateString; char errorMessageQuoted[QDMIRRORErrorMessageSize * 2 + 3]; char *user; MemoryContext mcxt = CurrentMemoryContext; Segment *master = NULL; volatile PQExpBuffer entryBuffer = NULL; volatile PGconn *entryConn = NULL; volatile PGresult *rs = NULL; PG_TRY(); { StartTransactionCommand(); user = getDBSuperuserName("QDMirroringUpdate"); Assert(user != NULL); master = GetMasterSegment(); entryBuffer = createPQExpBuffer(); if (PQExpBufferBroken(entryBuffer)) { destroyPQExpBuffer(entryBuffer); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("QDMirroringUpdate: out of memory"))); /* not reached. */ } /* * initialize libpq connection buffer, we only need to initialize it * once. */ initPQConnectionBuffer(master, user, NULL, entryBuffer, true); FreeSegment(master); free(user); /* * Call libpq to connect */ entryConn = PQconnectdb(entryBuffer->data); if (PQstatus((PGconn *)entryConn) == CONNECTION_BAD) { /* * When we get an error, we strdup it here. When the main thread * checks for errors, it makes a palloc copy of this, and frees * this. */ char *error_message = strdup(PQerrorMessage((PGconn *)entryConn)); if (!error_message) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("QDMirroringUpdate: out of memory"))); } destroyPQExpBuffer(entryBuffer); PQfinish((PGconn *)entryConn); entryConn = NULL; elog(FATAL, "QDMirroringUpdate: setting segDB state failed, error connecting to entry db, error: %s", error_message); } /* finally, we're ready to actually get some stuff done. */ do { rs = PQexec((PGconn *)entryConn, "BEGIN"); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) break; if ((updateMask & QDMIRROR_UPDATEMASK_VALIDFLAG) != 0) { count = snprintf(cmd, sizeof(cmd), UPDATE_VALIDFLAG_CMD, (validFlag ? 't' : 'f'), ftsQDMirrorInfo->dbid); if (count >= sizeof(cmd)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: format command string failure"))); } rs = PQexec((PGconn *)entryConn, cmd); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: could not execute command '%s'", cmd))); break; } } if ((updateMask & QDMIRROR_UPDATEMASK_MASTERMIRRORING)!= 0) { switch (state) { case QDMIRROR_STATE_NONE: summaryStateString = "None"; break; case QDMIRROR_STATE_NOTCONFIGURED: summaryStateString = "Not Configured"; break; case QDMIRROR_STATE_CONNECTINGWALSENDSERVER: case QDMIRROR_STATE_POSITIONINGTOEND: case QDMIRROR_STATE_CATCHUPPENDING: case QDMIRROR_STATE_CATCHINGUP: summaryStateString = "Synchronizing"; break; case QDMIRROR_STATE_SYNCHRONIZED: summaryStateString = "Synchronized"; break; case QDMIRROR_STATE_DISABLED: summaryStateString = "Not Synchronized"; break; default: summaryStateString = "Unknown"; break; } if (state == QDMIRROR_STATE_DISABLED) { detailStateString = QDMirroringDisabledReasonToString(disabledReason); } else { detailStateString = NULL; } if (detailStateString == NULL) { strcpy(detailValue, "null"); } else { count = snprintf(detailValue, sizeof(detailValue), "'%s'", detailStateString); if (count >= sizeof(detailValue)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: format command string failure"))); } } QDMirroringFormatTime(logTimeStr, sizeof(logTimeStr), lastLogTimeVal); /* * Escape quote the error string before putting in DML statement... */ if (errorMessage != NULL) { int errorMessageLen = strlen(errorMessage); if (errorMessageLen == 0) { strcpy(errorMessageQuoted, "null"); } else { size_t escapedLen; errorMessageQuoted[0] = '\''; escapedLen = PQescapeString(&errorMessageQuoted[1], errorMessage, errorMessageLen); errorMessageQuoted[escapedLen + 1] = '\''; errorMessageQuoted[escapedLen + 2] = '\0'; elog((Debug_print_qd_mirroring ? LOG : DEBUG5), "Error message quoted: \"%s\"", errorMessageQuoted); } } else { strcpy(errorMessageQuoted, "null"); } count = snprintf(cmd, sizeof(cmd), UPDATE_MASTER_MIRRORING_CMD, summaryStateString, detailValue, logTimeStr, errorMessageQuoted); if (count >= sizeof(cmd)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: format command string failure"))); } rs = PQexec((PGconn *)entryConn, cmd); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: could not execute command '%s'", cmd))); break; } elog((Debug_print_qd_mirroring ? LOG : DEBUG5), "Successfully executed command \"%s\"", cmd); rs = PQexec((PGconn *)entryConn, "COMMIT"); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) break; } } while (0); PQclear((PGresult *)rs); PQfinish((PGconn *)entryConn); destroyPQExpBuffer(entryBuffer); CommitTransactionCommand(); } PG_CATCH(); { PQclear((PGresult *)rs); PQfinish((PGconn *)entryConn); destroyPQExpBuffer(entryBuffer); AbortCurrentTransaction(); } PG_END_TRY(); MemoryContextSwitchTo(mcxt); /* Just incase we hit an error */ return; }
void ContQuerySchedulerMain(int argc, char *argv[]) { sigjmp_buf local_sigjmp_buf; List *dbs = NIL; /* we are a postmaster subprocess now */ IsUnderPostmaster = true; am_cont_scheduler = true; /* reset MyProcPid */ MyProcPid = getpid(); MyPMChildSlot = AssignPostmasterChildSlot(); /* record Start Time for logging */ MyStartTime = time(NULL); /* Identify myself via ps */ init_ps_display("continuous query scheduler process", "", "", ""); ereport(LOG, (errmsg("continuous query scheduler started"))); if (PostAuthDelay) pg_usleep(PostAuthDelay * 1000000L); SetProcessingMode(InitProcessing); /* * If possible, make this process a group leader, so that the postmaster * can signal any child processes too. This is only for consistency sake, we * never fork the scheduler process. Instead dynamic bgworkers are used. */ #ifdef HAVE_SETSID if (setsid() < 0) elog(FATAL, "setsid() failed: %m"); #endif /* * Set up signal handlers. We operate on databases much like a regular * backend, so we use the same signal handling. See equivalent code in * tcop/postgres.c. */ pqsignal(SIGHUP, sighup_handler); pqsignal(SIGINT, sigint_handler); pqsignal(SIGTERM, sigterm_handler); pqsignal(SIGQUIT, quickdie); InitializeTimeouts(); /* establishes SIGALRM handler */ pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, procsignal_sigusr1_handler); pqsignal(SIGUSR2, sigusr2_handler); pqsignal(SIGFPE, FloatExceptionHandler); pqsignal(SIGCHLD, SIG_DFL); #define BACKTRACE_SEGFAULTS #ifdef BACKTRACE_SEGFAULTS pqsignal(SIGSEGV, debug_segfault); #endif /* Early initialization */ BaseInit(); /* * Create a per-backend PGPROC struct in shared memory, except in the * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do * this before we can use LWLocks (and in the EXEC_BACKEND case we already * had to do some stuff with LWLocks). */ #ifndef EXEC_BACKEND InitProcess(); #endif InitPostgres(NULL, InvalidOid, NULL, NULL); SetProcessingMode(NormalProcessing); /* * Create a memory context that we will do all our work in. We do this so * that we can reset the context during error recovery and thereby avoid * possible memory leaks. */ ContQuerySchedulerMemCxt = AllocSetContextCreate(TopMemoryContext, "ContQuerySchedulerCtx", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(ContQuerySchedulerMemCxt); /* * If an exception is encountered, processing resumes here. * * This code is a stripped down version of PostgresMain error recovery. */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; /* Prevents interrupts while cleaning up */ HOLD_INTERRUPTS(); /* Forget any pending QueryCancel or timeout request */ disable_all_timeouts(false); QueryCancelPending = false; /* second to avoid race condition */ /* Report the error to the server log */ EmitErrorReport(); /* Abort the current transaction in order to recover */ AbortCurrentTransaction(); /* * Now return to normal top-level context and clear ErrorContext for * next time. */ MemoryContextSwitchTo(ContQuerySchedulerMemCxt); FlushErrorState(); /* Flush any leaked data in the top-level context */ MemoryContextResetAndDeleteChildren(ContQuerySchedulerMemCxt); /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); /* * Sleep at least 1 second after any error. We don't want to be * filling the error logs as fast as we can. */ pg_usleep(1000000L); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; /* must unblock signals before calling rebuild_database_list */ PG_SETMASK(&UnBlockSig); ContQuerySchedulerShmem->scheduler_pid = MyProcPid; dbs = get_database_list(); /* Loop forever */ for (;;) { ListCell *lc; int rc; foreach(lc, dbs) { DatabaseEntry *db_entry = lfirst(lc); bool found; ContQueryProcGroup *grp = hash_search(ContQuerySchedulerShmem->proc_table, &db_entry->oid, HASH_ENTER, &found); /* If we don't have an entry for this dboid, initialize a new one and fire off bg procs */ if (!found) { grp->db_oid = db_entry->oid; namestrcpy(&grp->db_name, NameStr(db_entry->name)); start_group(grp); } } /* Allow sinval catchup interrupts while sleeping */ EnableCatchupInterrupt(); /* * Wait until naptime expires or we get some type of signal (all the * signal handlers will wake us by calling SetLatch). */ rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0); ResetLatch(&MyProc->procLatch); DisableCatchupInterrupt(); /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. */ if (rc & WL_POSTMASTER_DEATH) proc_exit(1); /* the normal shutdown case */ if (got_SIGTERM) break; /* update config? */ if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); /* update tuning parameters, so that they can be read downstream by background processes */ update_tuning_params(); } /* terminate a proc group? */ if (got_SIGUSR2) { HASH_SEQ_STATUS status; ContQueryProcGroup *grp; got_SIGUSR2 = false; hash_seq_init(&status, ContQuerySchedulerShmem->proc_table); while ((grp = (ContQueryProcGroup *) hash_seq_search(&status)) != NULL) { ListCell *lc; if (!grp->terminate) continue; foreach(lc, dbs) { DatabaseEntry *entry = lfirst(lc); if (entry->oid == grp->db_oid) { dbs = list_delete(dbs, entry); break; } } terminate_group(grp); } }