Example #1
0
/*
 * 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;
}
Example #2
0
/*
 * 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);
}
Example #3
0
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;
}
Example #4
0
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);
			}
		}