Re: Non-blocking synchronization in libpq using pipeline mode

From: Jan Behrens <jbe-mlist(at)magnetkern(dot)de>
To: pgsql-docs(at)lists(dot)postgresql(dot)org
Cc: Alvaro Herrera <alvherre(at)alvh(dot)no-ip(dot)org>
Subject: Re: Non-blocking synchronization in libpq using pipeline mode
Date: 2024-04-08 12:39:05
Message-ID: 20240408143905.aec070bfd2a0712552bd93ff@magnetkern.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-docs

On Mon, 25 Mar 2024 15:47:00 +0100
Jan Behrens <jbe-mlist(at)magnetkern(dot)de> wrote:

> On Tue, 19 Mar 2024 12:49:23 +0100
> Alvaro Herrera <alvherre(at)alvh(dot)no-ip(dot)org> wrote:
>
> > On 2024-Mar-06, PG Doc comments form wrote:
> >
> > > [...]
> >
> > [...]
>
> [...]
>
> [...] the documentation [of PQsendFlushRequest] specifically states
> that:
>
> "Note that the request is not itself flushed to the server
> automatically; use PQflush if necessary."
>
> However, that sentence only applies to "PQsendFlushRequest", not to
> "PQpipelineSync". On the contrary, section "34.5.1.1. Issuing Queries"
> reads:
>
> "[Flushing to the server] occurs when PQpipelineSync is used to
> establish a synchronization point in the pipeline, or when PQflush is
> called."
>
> As I understand it, this means that PQpipelineSync will flush the
> client-side's output, while PQsendFlushRequest won't flush the
> client-side. So both commands act differently with regard to flushing,
> is that right?
>
> If that is the case, then the question is what happens if you call
> "PQpipelineSync" in non-blocking mode. That isn't clear to me (and I
> believe it is not explicitly made clear in the documentation).

I had a look into the source code of PostgreSQL 16.2 to answer that
question, and I found:

int
PQpipelineSync(PGconn *conn)
{
/* ... */

/*
* Give the data a push (in pipeline mode, only if we're past the size
* threshold). In nonblock mode, don't complain if we're unable to send
* it all; PQgetResult() will do any additional flushing needed.
*/
if (pqPipelineFlush(conn) < 0)
return 0;

return 1;
}

Moreover:

/*
* pqPipelineFlush
*
* In pipeline mode, data will be flushed only when the out buffer reaches the
* threshold value. In non-pipeline mode, it behaves as stock pqFlush.
*
* Returns 0 on success.
*/
static int
pqPipelineFlush(PGconn *conn)
{
if ((conn->pipelineStatus != PQ_PIPELINE_ON) ||
(conn->outCount >= OUTBUFFER_THRESHOLD))
return pqFlush(conn);
return 0;
}

It is not entirely clear to me what this means for the user of libpq.

It seems like invoking PQpipelineSync with pipeline mode switched on,
will *NOT* always flush the synchronization request out to the server.

While the comment on PQpipelineSync suggests that "PQgetResult() will
do any additional flushing needed", I believe that an event loop that
only waits for incoming data (e.g. POLLIN on the file descriptor) may
block indefinitely because a synchronization request hasn't been
flushed out yet.

If that is true, the documentation should clarify that PQflush might
need to be invoked manually by the user of libpq. Do I understand this
right?

>
> In section "34.4. Asynchronous Command Processing", we find:
>
> "In the nonblocking state, successful calls to PQsendQuery, PQputline,
> PQputnbytes, PQputCopyData, and PQendcopy will not block; their changes
> are stored in the local output buffer until they are flushed.
> Unsuccessful calls will return an error and must be retried."
>
> There is no reference to "PQpipelineSync" there. Does that mean that
> "PQsetnonblocking" has no effect on "PQpipelineSync"'s blocking
> behavior? If so, is it correct that "PQpipelineSync" always blocks when
> the socket is not ready for writing (since, as stated in the
> documentation, it does flush)?

Following the source code, PQpipelineSync will not block (but also
sometimes not flush, which is not documented, except in the source
code).

>
> I think section 34.5.1.4 should clarifiy how exactly PQpipelineSync
> acts in regard to flushing and blocking in that matter. It doesn't seem
> very clear at the moment.
>
> >
> > > 2. the synchronization or flush request need to be flushed manually with
> > > successive PQflush calls
> >
> > Yes.
>
> Regarding "PQpipelineSync", the documentation implies otherwise. See
> cite above:
>
> "[Flushing to the server] occurs when PQpipelineSync is used to
> establish a synchronization point in the pipeline, or when PQflush is
> called."
>
> That sentence doesn't make sense if PQpipelineSync wouldn't flush.

See above: PQpipelineSync seems to "sometimes" flush.

> [...]

Regards
Jan Behrens

In response to

Browse pgsql-docs by date

  From Date Subject
Next Message PG Doc comments form 2024-04-09 09:31:40 8.14.5 jsonb subscripting
Previous Message David G. Johnston 2024-04-07 15:11:21 Re: A typo?