/* * ProcessCatchupInterrupt * * The portion of catchup interrupt handling that runs outside of the signal * handler, which allows it to actually process pending invalidations. */ void ProcessCatchupInterrupt(void) { while (catchupInterruptPending) { /* * What we need to do here is cause ReceiveSharedInvalidMessages() to * run, which will do the necessary work and also reset the * catchupInterruptPending flag. If we are inside a transaction we * can just call AcceptInvalidationMessages() to do this. If we * aren't, we start and immediately end a transaction; the call to * AcceptInvalidationMessages() happens down inside transaction start. * * It is awfully tempting to just call AcceptInvalidationMessages() * without the rest of the xact start/stop overhead, and I think that * would actually work in the normal case; but I am not sure that things * would clean up nicely if we got an error partway through. */ if (IsTransactionOrTransactionBlock()) { elog(DEBUG4, "ProcessCatchupEvent inside transaction"); AcceptInvalidationMessages(); } else { elog(DEBUG4, "ProcessCatchupEvent outside transaction"); StartTransactionCommand(); CommitTransactionCommand(); } } }
/* * -------------------------------------------------------------- * EnableNotifyInterrupt * * This is called by the PostgresMain main loop just before waiting * for a frontend command. If we are truly idle (ie, *not* inside * a transaction block), then process any pending inbound notifies, * and enable the signal handler to process future notifies directly. * * NOTE: the signal handler starts out disabled, and stays so until * PostgresMain calls this the first time. * -------------------------------------------------------------- */ void EnableNotifyInterrupt(void) { if (IsTransactionOrTransactionBlock()) return; /* not really idle */ /* * This code is tricky because we are communicating with a signal * handler that could interrupt us at any point. If we just checked * notifyInterruptOccurred and then set notifyInterruptEnabled, we * could fail to respond promptly to a signal that happens in between * those two steps. (A very small time window, perhaps, but Murphy's * Law says you can hit it...) Instead, we first set the enable flag, * then test the occurred flag. If we see an unserviced interrupt has * occurred, we re-clear the enable flag before going off to do the * service work. (That prevents re-entrant invocation of * ProcessIncomingNotify() if another interrupt occurs.) If an * interrupt comes in between the setting and clearing of * notifyInterruptEnabled, then it will have done the service work and * left notifyInterruptOccurred zero, so we have to check again after * clearing enable. The whole thing has to be in a loop in case * another interrupt occurs while we're servicing the first. Once we * get out of the loop, enable is set and we know there is no * unserviced interrupt. * * NB: an overenthusiastic optimizing compiler could easily break this * code. Hopefully, they all understand what "volatile" means these * days. */ for (;;) { notifyInterruptEnabled = 1; if (!notifyInterruptOccurred) break; notifyInterruptEnabled = 0; if (notifyInterruptOccurred) { if (Trace_notify) elog(DEBUG1, "EnableNotifyInterrupt: perform async notify"); ProcessIncomingNotify(); if (Trace_notify) elog(DEBUG1, "EnableNotifyInterrupt: done"); } } }
/* * ProcessCatchupEvent * * Respond to a catchup event (SIGUSR1) from another backend. * * This is called either directly from the SIGUSR1 signal handler, * or the next time control reaches the outer idle loop (assuming * there's still anything to do by then). */ static void ProcessCatchupEvent(void) { bool notify_enabled; /* Must prevent SIGUSR2 interrupt while I am running */ notify_enabled = DisableNotifyInterrupt(); /* * What we need to do here is cause ReceiveSharedInvalidMessages() to run, * which will do the necessary work and also reset the * catchupInterruptOccurred flag. If we are inside a transaction we can * just call AcceptInvalidationMessages() to do this. If we aren't, we * start and immediately end a transaction; the call to * AcceptInvalidationMessages() happens down inside transaction start. * * It is awfully tempting to just call AcceptInvalidationMessages() * without the rest of the xact start/stop overhead, and I think that * would actually work in the normal case; but I am not sure that things * would clean up nicely if we got an error partway through. */ if (IsTransactionOrTransactionBlock()) { elog(DEBUG4, "ProcessCatchupEvent inside transaction"); AcceptInvalidationMessages(); } else { elog(DEBUG4, "ProcessCatchupEvent outside transaction"); StartTransactionCommand(); CommitTransactionCommand(); } if (notify_enabled) EnableNotifyInterrupt(); }
/* * Create a writer gang. */ Gang * AllocateWriterGang() { Gang *writerGang = NULL; MemoryContext oldContext = NULL; int i = 0; ELOG_DISPATCHER_DEBUG("AllocateWriterGang begin."); if (Gp_role != GP_ROLE_DISPATCH) { elog(FATAL, "dispatch process called with role %d", Gp_role); } /* * First, we look for an unallocated but created gang of the right type * if it exists, we return it. * Else, we create a new gang */ if (primaryWriterGang == NULL) { int nsegdb = getgpsegmentCount(); insist_log(IsTransactionOrTransactionBlock(), "cannot allocate segworker group outside of transaction"); if (GangContext == NULL) { GangContext = AllocSetContextCreate(TopMemoryContext, "Gang Context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } Assert(GangContext != NULL); oldContext = MemoryContextSwitchTo(GangContext); writerGang = createGang(GANGTYPE_PRIMARY_WRITER, PRIMARY_WRITER_GANG_ID, nsegdb, -1); writerGang->allocated = true; /* * set "whoami" for utility statement. * non-utility statement will overwrite it in function getCdbProcessList. */ for(i = 0; i < writerGang->size; i++) setQEIdentifier(&writerGang->db_descriptors[i], -1, writerGang->perGangContext); MemoryContextSwitchTo(oldContext); } else { ELOG_DISPATCHER_DEBUG("Reusing an existing primary writer gang"); writerGang = primaryWriterGang; } /* sanity check the gang */ if (!GangOK(writerGang)) elog(ERROR, "could not connect to segment: initialization of segworker group failed"); ELOG_DISPATCHER_DEBUG("AllocateWriterGang end."); primaryWriterGang = writerGang; return writerGang; }
/* * Create a reader gang. * * @type can be GANGTYPE_ENTRYDB_READER, GANGTYPE_SINGLETON_READER or GANGTYPE_PRIMARY_READER. */ Gang * AllocateReaderGang(GangType type, char *portal_name) { MemoryContext oldContext = NULL; Gang *gp = NULL; int size = 0; int content = 0; ELOG_DISPATCHER_DEBUG("AllocateReaderGang for portal %s: allocatedReaderGangsN %d, availableReaderGangsN %d, " "allocatedReaderGangs1 %d, availableReaderGangs1 %d", (portal_name ? portal_name : "<unnamed>"), list_length(allocatedReaderGangsN), list_length(availableReaderGangsN), list_length(allocatedReaderGangs1), list_length(availableReaderGangs1)); if (Gp_role != GP_ROLE_DISPATCH) { elog(FATAL, "dispatch process called with role %d", Gp_role); } insist_log(IsTransactionOrTransactionBlock(), "cannot allocate segworker group outside of transaction"); if (GangContext == NULL) { GangContext = AllocSetContextCreate(TopMemoryContext, "Gang Context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } Assert(GangContext != NULL); oldContext = MemoryContextSwitchTo(GangContext); switch (type) { case GANGTYPE_ENTRYDB_READER: content = -1; size = 1; break; case GANGTYPE_SINGLETON_READER: content = gp_singleton_segindex; size = 1; break; case GANGTYPE_PRIMARY_READER: content = 0; size = getgpsegmentCount(); break; default: Assert(false); } /* * First, we look for an unallocated but created gang of the right type * if it exists, we return it. * Else, we create a new gang */ gp = getAvailableGang(type, size, content); if (gp == NULL) { ELOG_DISPATCHER_DEBUG("Creating a new reader size %d gang for %s", size, (portal_name ? portal_name : "unnamed portal")); gp = createGang(type, gang_id_counter++, size, content); gp->allocated = true; } /* * make sure no memory is still allocated for previous * portal name that this gang belonged to */ if (gp->portal_name) pfree(gp->portal_name); /* let the gang know which portal it is being assigned to */ gp->portal_name = (portal_name ? pstrdup(portal_name) : (char *) NULL); /* sanity check the gang */ insist_log(GangOK(gp), "could not connect to segment: initialization of segworker group failed"); addGangToAllocated(gp); MemoryContextSwitchTo(oldContext); ELOG_DISPATCHER_DEBUG("on return: allocatedReaderGangs %d, availableReaderGangsN %d, " "allocatedReaderGangs1 %d, availableReaderGangs1 %d", list_length(allocatedReaderGangsN), list_length(availableReaderGangsN), list_length(allocatedReaderGangs1), list_length(availableReaderGangs1)); return gp; }
void CheckForQDMirroringWork(void) { QDMIRRORUpdateMask updateMask; bool validFlag; QDMIRRORState state; QDMIRRORDisabledReason disabledReason; struct timeval lastLogTimeVal; char errorMessage[QDMIRRORErrorMessageSize]; if (ftsQDMirrorInfo == NULL) return; /* * Test without taking lock. */ if (ftsQDMirrorInfo->updateMask == QDMIRROR_UPDATEMASK_NONE) return; if (IsTransactionOrTransactionBlock()) return; /* * NOTE: We are trying to use the longer-term update config lock here. */ if (!LWLockConditionalAcquire(ftsQDMirrorUpdateConfigLock, LW_EXCLUSIVE)) return; LWLockAcquire(ftsQDMirrorLock, LW_EXCLUSIVE); if (ftsQDMirrorInfo->updateMask == QDMIRROR_UPDATEMASK_NONE) { LWLockRelease(ftsQDMirrorLock); LWLockRelease(ftsQDMirrorUpdateConfigLock); return; } updateMask = ftsQDMirrorInfo->updateMask; ftsQDMirrorInfo->updateMask = QDMIRROR_UPDATEMASK_NONE; validFlag = false; if ((updateMask & QDMIRROR_UPDATEMASK_VALIDFLAG) != 0) { validFlag = !ftsQDMirrorInfo->valid; /* * Assume we are successful... */ ftsQDMirrorInfo->valid = validFlag; } state = ftsQDMirrorInfo->state; disabledReason = ftsQDMirrorInfo->disabledReason; lastLogTimeVal = ftsQDMirrorInfo->lastLogTimeVal; strcpy(errorMessage, ftsQDMirrorInfo->errorMessage); LWLockRelease(ftsQDMirrorLock); QDMirroringUpdate(updateMask, validFlag, state, disabledReason, &lastLogTimeVal, errorMessage); LWLockRelease(ftsQDMirrorUpdateConfigLock); }
/* * ProcessCatchupEvent * * Respond to a catchup event (PROCSIG_CATCHUP_INTERRUPT) from another * backend. * * This is called either directly from the PROCSIG_CATCHUP_INTERRUPT * signal handler, or the next time control reaches the outer idle loop * (assuming there's still anything to do by then). */ static void ProcessCatchupEvent(void) { bool notify_enabled; bool client_wait_timeout_enabled; DtxContext saveDistributedTransactionContext; /* * Funny indentation to keep the code inside identical to upstream * while at the same time supporting CMockery which has problems with * multiple bracing on column 1. */ PG_TRY(); { in_process_catchup_event = 1; /* Must prevent SIGUSR2 and SIGALRM(for IdleSessionGangTimeout) interrupt while I am running */ notify_enabled = DisableNotifyInterrupt(); client_wait_timeout_enabled = DisableClientWaitTimeoutInterrupt(); /* * What we need to do here is cause ReceiveSharedInvalidMessages() to run, * which will do the necessary work and also reset the * catchupInterruptOccurred flag. If we are inside a transaction we can * just call AcceptInvalidationMessages() to do this. If we aren't, we * start and immediately end a transaction; the call to * AcceptInvalidationMessages() happens down inside transaction start. * * It is awfully tempting to just call AcceptInvalidationMessages() * without the rest of the xact start/stop overhead, and I think that * would actually work in the normal case; but I am not sure that things * would clean up nicely if we got an error partway through. */ if (IsTransactionOrTransactionBlock()) { elog(DEBUG1, "ProcessCatchupEvent inside transaction"); AcceptInvalidationMessages(); } else { elog(DEBUG1, "ProcessCatchupEvent outside transaction"); /* * Save distributed transaction context first. */ saveDistributedTransactionContext = DistributedTransactionContext; DistributedTransactionContext = DTX_CONTEXT_LOCAL_ONLY; StartTransactionCommand(); CommitTransactionCommand(); DistributedTransactionContext = saveDistributedTransactionContext; } if (notify_enabled) EnableNotifyInterrupt(); if (client_wait_timeout_enabled) EnableClientWaitTimeoutInterrupt(); in_process_catchup_event = 0; } PG_CATCH(); { in_process_catchup_event = 0; PG_RE_THROW(); } PG_END_TRY(); }