Re: dynamic background workers, round two

From: Andres Freund <andres(at)2ndquadrant(dot)com>
To: Robert Haas <robertmhaas(at)gmail(dot)com>
Cc: "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: dynamic background workers, round two
Date: 2013-08-27 13:50:25
Message-ID: 20130827135025.GB24807@alap2.anarazel.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Robert,

On 2013-08-17 12:08:17 -0400, Robert Haas wrote:
> On Sun, Aug 11, 2013 at 1:31 AM, Andres Freund <andres(at)anarazel(dot)de> wrote:
> > So, I'd suggest something like:
> >
> > typedef enum BgwHandleStatus {
> > BGWH_SUCCESS, /* sucessfully got status */
> > BGWH_NOT_YET, /* worker hasn't started yet */
> > BGWH_GONE, /* worker had been started, but shut down already */
> > BGWH_POSTMASTER_DIED /* well, there you go */
> > } BgwHandleStatus;
> >
> >
> > BgwHandleStatus GetBackgroundWorkerPid(BackgroundWorkerHandle *handle, pid_t *pid);
> > BgwHandleStatus WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pid);
>
> OK, here's a patch that API. I renamed the constants a bit, because a
> process that has stopped is not necessarily gone; it could be
> configured for restart. But we can say that it is stopped, at the
> moment.
>
> I'm not sure that this API is an improvement. But I think it's OK, if
> you prefer it.

Thanks, looks more readable to me. I like code that tells me what it
does without having to look in other places ;)

> + <function>RegisterDynamicBackgroundWorker(<type>BackgroundWorker
> + *worker, BackgroundWorkerHandle **handle</type>)</function>. Unlike
> + <function>RegisterBackgroundWorker</>, which can only be called from within
> + the postmaster, <function>RegisterDynamicBackgroundWorker</function> must be
> + called from a regular backend.
> </para>

s/which can only be called from within the posmaster/during initial
startup, via shared_preload_libraries/?

> <para>
> + When a background worker is registered using the
> + <function>RegisterDynamicBackgroundWorker</function> function, it is
> + possible for the backend performing the registration to obtain information
> + the status of the worker. Backends wishing to do this should pass the
> + address of a <type>BackgroundWorkerHandle *</type> as the second argument
> + to <function>RegisterDynamicBackgroundWorker</function>. If the worker
> + is successfully registered, this pointer will be initialized with an
> + opaque handle that can subsequently be passed to
> + <function>GetBackgroundWorkerPid(<parameter>BackgroundWorkerHandle *</parameter>, <parameter>pid_t *</parameter>)</function>.
> + This function can be used to poll the status of the worker: a return
> + value of <literal>BGWH_NOT_YET_STARTED</> indicates that the worker has not
> + yet been started by the postmaster; <literal>BGWH_STOPPED</literal>
> + indicates that it has been started but is no longer running; and
> + <literal>BGWH_STATED</literal> indicates that it is currently running.
> + In this last case, the PID will also be returned via the second argument.
> + </para>

s/STATED/STARTED/

> diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
> index f7ebd1a..6414291 100644
> --- a/src/backend/commands/async.c
> +++ b/src/backend/commands/async.c

> -#define InvalidPid (-1)
> -

Hrmpf ;)

> bool
> -RegisterDynamicBackgroundWorker(BackgroundWorker *worker)
> +RegisterDynamicBackgroundWorker(BackgroundWorker *worker,
> + BackgroundWorkerHandle **handle)
> {

Hm. Not this patches fault, but We seem to allow bgw_start_time ==
BgWorkerStart_PostmasterStart here which doesn't make sense...

> +/*
> + * Get the PID of a dynamically-registered background worker.
> + *
> + * If the return value is InvalidPid, it indicates that the worker has not
> + * yet been started by the postmaster.
> + *
> + * If the return value is 0, it indicates that the worker was started by
> + * the postmaster but is no longer running.
> + *
> + * Any other return value is the PID of the running worker.
> + */
> +BgwHandleStatus
> +GetBackgroundWorkerPid(BackgroundWorkerHandle *handle, pid_t *pidp)
> +{
> + BackgroundWorkerSlot *slot;
> + pid_t pid;
> +
> + slot = &BackgroundWorkerData->slot[handle->slot];

Maybe add something like Assert(hanlde->slot < max_worker_processes);?

> +/*
> + * Wait for a background worker to start up.
> + *
> + * The return value is the same as for GetBackgroundWorkerPID, except that
> + * we only return InvalidPid if the postmaster has died (and therefore we
> + * know that the worker will never be started).
> + */
> +BgwHandleStatus
> +WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
> +{
> + BgwHandleStatus status;
> + pid_t pid;
> + int rc;
> +
> + set_latch_on_sigusr1 = true;
> +
> + PG_TRY();
> + {
> + for (;;)
> + {
> + status = GetBackgroundWorkerPid(handle, &pid);
> + if (status != BGWH_NOT_YET_STARTED)
> + break;
> + rc = WaitLatch(&MyProc->procLatch,
> + WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
> + if (rc & WL_POSTMASTER_DEATH)
> + {
> + status = BGWH_POSTMASTER_DIED;
> + break;
> + }
> + ResetLatch(&MyProc->procLatch);
> + }
> + }
> + PG_CATCH();
> + {
> + set_latch_on_sigusr1 = false;
> + PG_RE_THROW();
> + }
> + PG_END_TRY();
> +
> + set_latch_on_sigusr1 = false;
> + *pidp = pid;
> + return status;
> +}

Shouldn't that loop have a CHECK_FOR_INTERRUPTS()?

Theoretically this would unset set_latch_on_sigusr1 if it was previously
already set to 'true'. If you feel defensive you could safe the previous
value...

So, besides those I don't see much left to be done. I haven't tested
EXEC_BACKEND, but I don't anything specific to that here.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Andres Freund 2013-08-27 13:56:36 Re: dynamic background workers, round two
Previous Message Tom Lane 2013-08-27 13:44:18 Re: UNNEST with multiple args, and TABLE with multiple funcs