/* * Checks if RunawayCleaner_RunawayCleanupDoneForSession reactivates the runaway detector */ void test__RunawayCleaner_RunawayCleanupDoneForSession__ResetsRunawayFlagAndReactivateRunawayDetector(void **state) { InitFakeSessionState(2 /* activeProcessCount */, 2 /* cleanupCountdown */, RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */); static EventVersion fakeLatestRunawayVersion = 10; latestRunawayVersion = &fakeLatestRunawayVersion; /* * Satisfy asserts */ beginCleanupRunawayVersion = *latestRunawayVersion; endCleanupRunawayVersion = beginCleanupRunawayVersion; MySessionState->cleanupCountdown = CLEANUP_COUNTDOWN_BEFORE_RUNAWAY; *isRunawayDetector = 1; MySessionState->runawayStatus = RunawayStatus_PrimaryRunawaySession; RunawayCleaner_RunawayCleanupDoneForSession(); assert_true(MySessionState->runawayStatus == RunawayStatus_NotRunaway); /* Runaway detector should be re-enabled */ assert_true(*isRunawayDetector == 0); }
/* * Releases the pinCount of a SessionState entry. If the pinCount * drops to 0, it puts the entry back to the freeList for reuse. */ static void SessionState_Release(SessionState *acquired) { if (!sessionStateInited) { Assert(NULL == acquired); return; } Assert(NULL != acquired); Assert(0 < acquired->pinCount); Assert(acquired->sessionId == gp_session_id || acquired->isModifiedSessionId); LWLockAcquire(SessionStateLock, LW_EXCLUSIVE); Assert(!isProcessActive); Assert(acquired->activeProcessCount < acquired->pinCount); int pinCount = pg_atomic_sub_fetch_u32((pg_atomic_uint32 *) &acquired->pinCount, 1); ereport(gp_sessionstate_loglevel, (errmsg("SessionState_Release: pinCount: %d, activeProcessCount: %d", pinCount, acquired->activeProcessCount), errprintstack(true))); /* Before this point the process should have been deactivated */ Assert(acquired->activeProcessCount <= acquired->pinCount); Assert(0 <= acquired->pinCount); if (0 == acquired->pinCount) { RunawayCleaner_RunawayCleanupDoneForSession(); acquired->sessionId = INVALID_SESSION_ID; Assert(acquired->runawayStatus == RunawayStatus_NotRunaway); Assert(CLEANUP_COUNTDOWN_BEFORE_RUNAWAY == acquired->cleanupCountdown); Assert(0 == acquired->activeProcessCount); acquired->sessionVmem = 0; acquired->runawayStatus = RunawayStatus_NotRunaway; acquired->sessionVmemRunaway = 0; acquired->commandCountRunaway = 0; acquired->cleanupCountdown = CLEANUP_COUNTDOWN_BEFORE_RUNAWAY; acquired->activeProcessCount = 0; acquired->idle_start = 0; acquired->resGroupSlot = NULL; #ifdef USE_ASSERT_CHECKING acquired->isModifiedSessionId = false; #endif SessionState *cur = AllSessionStateEntries->usedList; SessionState *prev = NULL; while (cur != acquired && cur != NULL) { prev = cur; cur = cur->next; } Assert(cur == acquired); /* grabbed is at the head of used list */ if (NULL == prev) { Assert(AllSessionStateEntries->usedList == acquired); AllSessionStateEntries->usedList = acquired->next; } else { prev->next = cur->next; } acquired->next = AllSessionStateEntries->freeList; AllSessionStateEntries->freeList = acquired; AllSessionStateEntries->numSession--; Assert(AllSessionStateEntries->numSession >= 0); } LWLockRelease(SessionStateLock); }
/* * Marks the current process as clean. If all the processes are marked * as clean for this session (i.e., cleanupCountdown == 0 in the * MySessionState) then we reset session's runaway status as well as * the runaway detector flag (i.e., a new runaway detector can run). * * Parameters: * ignoredCleanup: whether the cleanup was ignored, i.e., no elog(ERROR, ...) * was thrown. In such case a deactivated process is not reactivated as the * deactivation didn't get interrupted. */ void RunawayCleaner_RunawayCleanupDoneForProcess(bool ignoredCleanup) { /* * We don't do anything if we don't have an ongoing cleanup, or we already finished * cleanup once for the current runaway event */ if (beginCleanupRunawayVersion != *latestRunawayVersion || endCleanupRunawayVersion == beginCleanupRunawayVersion) { /* Either we never started cleanup, or we already finished */ return; } /* Disable repeating call */ endCleanupRunawayVersion = beginCleanupRunawayVersion; Assert(NULL != MySessionState); /* * As the current cleanup holds leverage on the cleanupCountdown, * the session must stay as runaway at least until the current * process marks itself clean */ Assert(MySessionState->runawayStatus != RunawayStatus_NotRunaway); /* We only cleanup if we were active when the runaway event happened */ Assert((!isProcessActive && *latestRunawayVersion < deactivationVersion && *latestRunawayVersion > activationVersion) || (*latestRunawayVersion > activationVersion && (activationVersion >= deactivationVersion && isProcessActive))); /* * We don't reactivate if the process is already active or a deactivated * process never errored out during deactivation (i.e., failed to complete * deactivation) */ if (!isProcessActive && !ignoredCleanup) { Assert(1 == *isRunawayDetector); Assert(0 < MySessionState->cleanupCountdown); /* * As the process threw ERROR instead of going into ReadCommand() blocking * state, we have to reactivate the process from its current Deactivated * state */ IdleTracker_ActivateProcess(); } Assert(0 < MySessionState->cleanupCountdown); #if USE_ASSERT_CHECKING int cleanProgress = #endif gp_atomic_add_32(&MySessionState->cleanupCountdown, -1); Assert(0 <= cleanProgress); bool finalCleaner = compare_and_swap_32((uint32*) &MySessionState->cleanupCountdown, 0, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY); if (finalCleaner) { /* * The final cleaner is responsible to reset the runaway flag, * and enable the runaway detection process. */ RunawayCleaner_RunawayCleanupDoneForSession(); } /* * Finally we are done with all critical cleanup, which includes releasing all our memory and * releasing our cleanup counter so that another session can be marked as runaway, if needed. * Now, we have some head room to actually record our usage. */ write_stderr("Logging memory usage because of runaway cleanup. Note, this is a post-cleanup logging and may be incomplete."); MemoryAccounting_SaveToLog(); MemoryContextStats(TopMemoryContext); }