/* * Dynamically launch an SPI worker. */ Datum worker_spi_launch(PG_FUNCTION_ARGS) { int32 i = PG_GETARG_INT32(0); BackgroundWorker worker; BackgroundWorkerHandle *handle; BgwHandleStatus status; pid_t pid; memset(&worker, 0, sizeof(worker)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_restart_time = BGW_NEVER_RESTART; sprintf(worker.bgw_library_name, "worker_spi"); sprintf(worker.bgw_function_name, "worker_spi_main"); snprintf(worker.bgw_name, BGW_MAXLEN, "worker_spi worker %d", i); snprintf(worker.bgw_type, BGW_MAXLEN, "worker_spi"); worker.bgw_main_arg = Int32GetDatum(i); /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */ worker.bgw_notify_pid = MyProcPid; if (!RegisterDynamicBackgroundWorker(&worker, &handle)) PG_RETURN_NULL(); status = WaitForBackgroundWorkerStartup(handle, &pid); if (status == BGWH_STOPPED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not start background process"), errhint("More details may be available in the server log."))); if (status == BGWH_POSTMASTER_DIED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("cannot start background processes without postmaster"), errhint("Kill all remaining database processes and restart the database."))); Assert(status == BGWH_STARTED); PG_RETURN_INT32(pid); }
void cfs_start_background_gc() { int i; cfs_state->max_iterations = INT_MAX; cfs_state->n_workers = cfs_gc_workers; for (i = 0; i < cfs_gc_workers; i++) { BackgroundWorker worker; BackgroundWorkerHandle* handle; MemSet(&worker, 0, sizeof(worker)); sprintf(worker.bgw_name, "cfs-worker-%d", i); worker.bgw_flags = BGWORKER_SHMEM_ACCESS; worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_restart_time = 1; worker.bgw_main = cfs_bgworker_main; worker.bgw_main_arg = Int32GetDatum(i); if (!RegisterDynamicBackgroundWorker(&worker, &handle)) { break; } } elog(LOG, "Start %d background CFS background workers", i); }
Datum cfs_start_gc(PG_FUNCTION_ARGS) { int i = 0; if (cfs_gc_workers == 0 && pg_atomic_test_set_flag(&cfs_state->gc_started)) { int j; BackgroundWorkerHandle** handles; cfs_stop = true; /* do just one iteration */ cfs_state->max_iterations = 1; cfs_state->n_workers = PG_GETARG_INT32(0); handles = (BackgroundWorkerHandle**)palloc(cfs_state->n_workers*sizeof(BackgroundWorkerHandle*)); for (i = 0; i < cfs_state->n_workers; i++) { BackgroundWorker worker; MemSet(&worker, 0, sizeof(worker)); sprintf(worker.bgw_name, "cfs-worker-%d", i); worker.bgw_flags = BGWORKER_SHMEM_ACCESS; worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_restart_time = 1; worker.bgw_main = cfs_bgworker_main; worker.bgw_main_arg = Int32GetDatum(i); if (!RegisterDynamicBackgroundWorker(&worker, &handles[i])) { break; } } for (j = 0; j < i; j++) { WaitForBackgroundWorkerShutdown(handles[j]); } pfree(handles); pg_atomic_clear_flag(&cfs_state->gc_started); } PG_RETURN_INT32(i); }
static bool run_background_proc(ContQueryProc *proc) { BackgroundWorker worker; BackgroundWorkerHandle *handle; bool success; strcpy(worker.bgw_name, GetContQueryProcName(proc)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_main = cq_bgproc_main; worker.bgw_notify_pid = MyProcPid; worker.bgw_restart_time = 1; /* recover in 1s */ worker.bgw_let_crash = false; worker.bgw_main_arg = PointerGetDatum(proc); success = RegisterDynamicBackgroundWorker(&worker, &handle); if (success) proc->handle = *handle; return success; }
/* * launch_consumer_group * * Launch a group of background worker process that will consume from the given topic * into the given relation */ static bool launch_consumer_group(Relation consumers, KafkaConsumer *consumer, int64 offset) { BackgroundWorker worker; BackgroundWorkerHandle *handle; KafkaConsumerGroup *group; bool found; int i; group = (KafkaConsumerGroup *) hash_search(consumer_groups, &consumer->id, HASH_ENTER, &found); if (found) { KafkaConsumerProc *proc; HASH_SEQ_STATUS iter; bool running = false; hash_seq_init(&iter, consumer_procs); while ((proc = (KafkaConsumerProc *) hash_seq_search(&iter)) != NULL) { if (proc->consumer_id == consumer->id) { running = true; break; } } hash_seq_term(&iter); /* if there are already procs running, it's a noop */ if (running) return true; /* no procs actually running, so it's ok to launch new ones */ } group->parallelism = consumer->parallelism; for (i = 0; i < group->parallelism; i++) { /* we just need any unique OID here */ Oid id = GetNewOid(consumers); KafkaConsumerProc *proc; proc = (KafkaConsumerProc *) hash_search(consumer_procs, &id, HASH_ENTER, &found); if (found) continue; worker.bgw_main_arg = DatumGetObjectId(id); worker.bgw_flags = BGWORKER_BACKEND_DATABASE_CONNECTION | BGWORKER_SHMEM_ACCESS; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = NULL; worker.bgw_notify_pid = 0; /* this module is loaded dynamically, so we can't use bgw_main */ sprintf(worker.bgw_library_name, PIPELINE_KAFKA_LIB); sprintf(worker.bgw_function_name, KAFKA_CONSUME_MAIN); snprintf(worker.bgw_name, BGW_MAXLEN, "[kafka consumer] %s <- %s", consumer->rel->relname, consumer->topic); proc->consumer_id = consumer->id; proc->partition_group = i; proc->offset = offset; namestrcpy(&proc->dbname, get_database_name(MyDatabaseId)); if (!RegisterDynamicBackgroundWorker(&worker, &handle)) return false; proc->worker = *handle; } return true; }
/* * Start new apply background worker, if possible. */ void logicalrep_worker_launch(Oid dbid, Oid subid, const char *subname, Oid userid, Oid relid) { BackgroundWorker bgw; BackgroundWorkerHandle *bgw_handle; uint16 generation; int i; int slot = 0; LogicalRepWorker *worker = NULL; int nsyncworkers; TimestampTz now; ereport(DEBUG1, (errmsg("starting logical replication worker for subscription \"%s\"", subname))); /* Report this after the initial starting message for consistency. */ if (max_replication_slots == 0) ereport(ERROR, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("cannot start logical replication workers when max_replication_slots = 0"))); /* * We need to do the modification of the shared memory under lock so that * we have consistent view. */ LWLockAcquire(LogicalRepWorkerLock, LW_EXCLUSIVE); retry: /* Find unused worker slot. */ for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; if (!w->in_use) { worker = w; slot = i; break; } } nsyncworkers = logicalrep_sync_worker_count(subid); now = GetCurrentTimestamp(); /* * If we didn't find a free slot, try to do garbage collection. The * reason we do this is because if some worker failed to start up and its * parent has crashed while waiting, the in_use state was never cleared. */ if (worker == NULL || nsyncworkers >= max_sync_workers_per_subscription) { bool did_cleanup = false; for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; /* * If the worker was marked in use but didn't manage to attach in * time, clean it up. */ if (w->in_use && !w->proc && TimestampDifferenceExceeds(w->launch_time, now, wal_receiver_timeout)) { elog(WARNING, "logical replication worker for subscription %u took too long to start; canceled", w->subid); logicalrep_worker_cleanup(w); did_cleanup = true; } } if (did_cleanup) goto retry; } /* * If we reached the sync worker limit per subscription, just exit * silently as we might get here because of an otherwise harmless race * condition. */ if (nsyncworkers >= max_sync_workers_per_subscription) { LWLockRelease(LogicalRepWorkerLock); return; } /* * However if there are no more free worker slots, inform user about it * before exiting. */ if (worker == NULL) { LWLockRelease(LogicalRepWorkerLock); ereport(WARNING, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("out of logical replication worker slots"), errhint("You might need to increase max_logical_replication_workers."))); return; } /* Prepare the worker slot. */ worker->launch_time = now; worker->in_use = true; worker->generation++; worker->proc = NULL; worker->dbid = dbid; worker->userid = userid; worker->subid = subid; worker->relid = relid; worker->relstate = SUBREL_STATE_UNKNOWN; worker->relstate_lsn = InvalidXLogRecPtr; worker->last_lsn = InvalidXLogRecPtr; TIMESTAMP_NOBEGIN(worker->last_send_time); TIMESTAMP_NOBEGIN(worker->last_recv_time); worker->reply_lsn = InvalidXLogRecPtr; TIMESTAMP_NOBEGIN(worker->reply_time); /* Before releasing lock, remember generation for future identification. */ generation = worker->generation; LWLockRelease(LogicalRepWorkerLock); /* Register the new dynamic worker. */ memset(&bgw, 0, sizeof(bgw)); bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; bgw.bgw_start_time = BgWorkerStart_RecoveryFinished; snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres"); snprintf(bgw.bgw_function_name, BGW_MAXLEN, "ApplyWorkerMain"); if (OidIsValid(relid)) snprintf(bgw.bgw_name, BGW_MAXLEN, "logical replication worker for subscription %u sync %u", subid, relid); else snprintf(bgw.bgw_name, BGW_MAXLEN, "logical replication worker for subscription %u", subid); snprintf(bgw.bgw_type, BGW_MAXLEN, "logical replication worker"); bgw.bgw_restart_time = BGW_NEVER_RESTART; bgw.bgw_notify_pid = MyProcPid; bgw.bgw_main_arg = Int32GetDatum(slot); if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle)) { /* Failed to start worker, so clean up the worker slot. */ LWLockAcquire(LogicalRepWorkerLock, LW_EXCLUSIVE); Assert(generation == worker->generation); logicalrep_worker_cleanup(worker); LWLockRelease(LogicalRepWorkerLock); ereport(WARNING, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("out of background worker slots"), errhint("You might need to increase max_worker_processes."))); return; } /* Now wait until it attaches. */ WaitForReplicationWorkerAttach(worker, generation, bgw_handle); }
/* * Register background workers. */ static worker_state * setup_background_workers(int nworkers, dsm_segment *seg) { MemoryContext oldcontext; BackgroundWorker worker; worker_state *wstate; int i; /* * We need the worker_state object and the background worker handles to * which it points to be allocated in CurTransactionContext rather than * ExprContext; otherwise, they'll be destroyed before the on_dsm_detach * hooks run. */ oldcontext = MemoryContextSwitchTo(CurTransactionContext); /* Create worker state object. */ wstate = MemoryContextAlloc(TopTransactionContext, offsetof(worker_state, handle) + sizeof(BackgroundWorkerHandle *) * nworkers); wstate->nworkers = 0; /* * Arrange to kill all the workers if we abort before all workers are * finished hooking themselves up to the dynamic shared memory segment. * * If we die after all the workers have finished hooking themselves up to * the dynamic shared memory segment, we'll mark the two queues to which * we're directly connected as detached, and the worker(s) connected to * those queues will exit, marking any other queues to which they are * connected as detached. This will cause any as-yet-unaware workers * connected to those queues to exit in their turn, and so on, until * everybody exits. * * But suppose the workers which are supposed to connect to the queues to * which we're directly attached exit due to some error before they * actually attach the queues. The remaining workers will have no way of * knowing this. From their perspective, they're still waiting for those * workers to start, when in fact they've already died. */ on_dsm_detach(seg, cleanup_background_workers, PointerGetDatum(wstate)); /* Configure a worker. */ worker.bgw_flags = BGWORKER_SHMEM_ACCESS; worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = NULL; /* new worker might not have library loaded */ sprintf(worker.bgw_library_name, "test_shm_mq"); sprintf(worker.bgw_function_name, "test_shm_mq_main"); snprintf(worker.bgw_name, BGW_MAXLEN, "test_shm_mq"); worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg)); /* set bgw_notify_pid, so we can detect if the worker stops */ worker.bgw_notify_pid = MyProcPid; /* Register the workers. */ for (i = 0; i < nworkers; ++i) { if (!RegisterDynamicBackgroundWorker(&worker, &wstate->handle[i])) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not register background process"), errhint("You may need to increase max_worker_processes."))); ++wstate->nworkers; } /* All done. */ MemoryContextSwitchTo(oldcontext); return wstate; }
Datum worker_test(PG_FUNCTION_ARGS) { int i, nworkers; dsm_segment *seg; test_shm_mq_header *hdr; MemoryContext oldcontext; worker_state *wstate; nworkers = PG_GETARG_INT32(0); #if PG_VERSION_NUM >= 90500 seg = dsm_create(sizeof(test_shm_mq_header), 0); #else seg = dsm_create(sizeof(test_shm_mq_header) #endif hdr = dsm_segment_address(seg); printf("begin worker_test: %d, %p\n", dsm_segment_handle(seg), hdr); MemSet(hdr, 0, sizeof(test_shm_mq_header)); SpinLockInit(&hdr->mutex); strncpy(hdr->dbname, get_database_name(MyDatabaseId), sizeof(hdr->dbname)); oldcontext = MemoryContextSwitchTo(CurTransactionContext); wstate = MemoryContextAlloc(TopTransactionContext, offsetof(worker_state, handle) + sizeof(BackgroundWorkerHandle *) * nworkers); MemSet(wstate, 0, offsetof(worker_state, handle) + sizeof(BackgroundWorkerHandle *) * nworkers); on_dsm_detach(seg, cleanup_background_workers, PointerGetDatum(wstate)); for (i = 0; i < nworkers; i++) { BackgroundWorker worker; MemSet(&worker, 0, sizeof(worker)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = NULL; /* new worker might not have library loaded */ sprintf(worker.bgw_library_name, "worker_test"); sprintf(worker.bgw_function_name, "worker_test_main"); snprintf(worker.bgw_name, BGW_MAXLEN, "worker_test %d", i); worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg)); worker.bgw_notify_pid = MyProcPid; if (!RegisterDynamicBackgroundWorker(&worker, &wstate->handle[i])) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not register background process"), errhint("You may need to increase max_worker_processes."))); ++wstate->nworkers; } for (i = 0; i < nworkers; i++) { BgwHandleStatus status; pid_t pid; status = WaitForBackgroundWorkerStartup(wstate->handle[i], &pid); if (status == BGWH_STOPPED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not start background process"), errhint("More details may be available in the server log."))); if (status == BGWH_POSTMASTER_DIED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("cannot start background processes without postmaster"), errhint("Kill all remaining database processes and restart the database."))); Assert(status == BGWH_STARTED); } wait_for_workers_to_become_ready(wstate, hdr); cancel_on_dsm_detach(seg, cleanup_background_workers, PointerGetDatum(wstate)); dsm_detach(seg); pfree(wstate); MemoryContextSwitchTo(oldcontext); PG_RETURN_VOID(); }
/* * Starts background worker that will create new partitions, * waits till it finishes the job and returns the result (new partition oid) */ Oid create_partitions_bg_worker(Oid relid, Datum value, Oid value_type, bool *crashed) { BackgroundWorker worker; BackgroundWorkerHandle *worker_handle; BgwHandleStatus status; dsm_segment *segment; dsm_handle segment_handle; pid_t pid; PartitionArgs *args; Oid child_oid; TypeCacheEntry *tce; /* Create a dsm segment for the worker to pass arguments */ segment = dsm_create(sizeof(PartitionArgs), 0); segment_handle = dsm_segment_handle(segment); tce = lookup_type_cache(value_type, 0); /* Fill arguments structure */ args = (PartitionArgs *) dsm_segment_address(segment); args->dbid = MyDatabaseId; args->relid = relid; if (tce->typbyval) args->value = value; else memcpy(&args->value, DatumGetPointer(value), sizeof(args->value)); args->by_val = tce->typbyval; args->value_type = value_type; args->result = 0; /* Initialize worker struct */ worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = bg_worker_main; worker.bgw_main_arg = Int32GetDatum(segment_handle); worker.bgw_notify_pid = MyProcPid; /* Start dynamic worker */ if (!RegisterDynamicBackgroundWorker(&worker, &worker_handle)) { elog(WARNING, "Unable to create background worker for pg_pathman"); } status = WaitForBackgroundWorkerStartup(worker_handle, &pid); if (status == BGWH_POSTMASTER_DIED) { ereport(WARNING, (errmsg("Postmaster died during the pg_pathman background worker process"), errhint("More details may be available in the server log."))); } /* Wait till the worker finishes its job */ status = WaitForBackgroundWorkerShutdown(worker_handle); *crashed = args->crashed; child_oid = args->result; /* Free dsm segment */ dsm_detach(segment); return child_oid; }