diff options
Diffstat (limited to 'src/backend/postmaster/bgworker.c')
-rw-r--r-- | src/backend/postmaster/bgworker.c | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c index 92495850db0..13a6e23a142 100644 --- a/src/backend/postmaster/bgworker.c +++ b/src/backend/postmaster/bgworker.c @@ -80,9 +80,22 @@ typedef struct BackgroundWorkerSlot BackgroundWorker worker; } BackgroundWorkerSlot; +/* + * In order to limit the total number of parallel workers (according to + * max_parallel_workers GUC), we maintain the number of active parallel + * workers. Since the postmaster cannot take locks, two variables are used for + * this purpose: the number of registered parallel workers (modified by the + * backends, protected by BackgroundWorkerLock) and the number of terminated + * parallel workers (modified only by the postmaster, lockless). The active + * number of parallel workers is the number of registered workers minus the + * terminated ones. These counters can of course overflow, but it's not + * important here since the subtraction will still give the right number. + */ typedef struct BackgroundWorkerArray { int total_slots; + uint32 parallel_register_count; + uint32 parallel_terminate_count; BackgroundWorkerSlot slot[FLEXIBLE_ARRAY_MEMBER]; } BackgroundWorkerArray; @@ -127,6 +140,8 @@ BackgroundWorkerShmemInit(void) int slotno = 0; BackgroundWorkerData->total_slots = max_worker_processes; + BackgroundWorkerData->parallel_register_count = 0; + BackgroundWorkerData->parallel_terminate_count = 0; /* * Copy contents of worker list into shared memory. Record the shared @@ -267,9 +282,12 @@ BackgroundWorkerStateChange(void) /* * We need a memory barrier here to make sure that the load of - * bgw_notify_pid completes before the store to in_use. + * bgw_notify_pid and the update of parallel_terminate_count + * complete before the store to in_use. */ notify_pid = slot->worker.bgw_notify_pid; + if ((slot->worker.bgw_flags & BGWORKER_CLASS_PARALLEL) != 0) + BackgroundWorkerData->parallel_terminate_count++; pg_memory_barrier(); slot->pid = 0; slot->in_use = false; @@ -370,6 +388,9 @@ ForgetBackgroundWorker(slist_mutable_iter *cur) Assert(rw->rw_shmem_slot < max_worker_processes); slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot]; + if ((rw->rw_worker.bgw_flags & BGWORKER_CLASS_PARALLEL) != 0) + BackgroundWorkerData->parallel_terminate_count++; + slot->in_use = false; ereport(DEBUG1, @@ -824,6 +845,7 @@ RegisterDynamicBackgroundWorker(BackgroundWorker *worker, { int slotno; bool success = false; + bool parallel; uint64 generation = 0; /* @@ -840,9 +862,28 @@ RegisterDynamicBackgroundWorker(BackgroundWorker *worker, if (!SanityCheckBackgroundWorker(worker, ERROR)) return false; + parallel = (worker->bgw_flags & BGWORKER_CLASS_PARALLEL) != 0; + LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE); /* + * If this is a parallel worker, check whether there are already too many + * parallel workers; if so, don't register another one. Our view of + * parallel_terminate_count may be slightly stale, but that doesn't really + * matter: we would have gotten the same result if we'd arrived here + * slightly earlier anyway. There's no help for it, either, since the + * postmaster must not take locks; a memory barrier wouldn't guarantee + * anything useful. + */ + if (parallel && (BackgroundWorkerData->parallel_register_count - + BackgroundWorkerData->parallel_terminate_count) >= + max_parallel_workers) + { + LWLockRelease(BackgroundWorkerLock); + return false; + } + + /* * Look for an unused slot. If we find one, grab it. */ for (slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno) @@ -856,6 +897,8 @@ RegisterDynamicBackgroundWorker(BackgroundWorker *worker, slot->generation++; slot->terminate = false; generation = slot->generation; + if (parallel) + BackgroundWorkerData->parallel_register_count++; /* * Make sure postmaster doesn't see the slot as in use before it |