Обсуждение: Non-blocking synchronization in libpq using pipeline mode

Поиск
Список
Период
Сортировка

Non-blocking synchronization in libpq using pipeline mode

От
PG Doc comments form
Дата:
The following documentation comment has been logged on the website:

Page: https://www.postgresql.org/docs/16/libpq-pipeline-mode.html
Description:

The calls to PQpipelineSync and PQsendFlushRequest may either report failure
or success, but not that data could not be written as of yet because the
request would block.

Does this mean that

1. these functions will always block when invoked and the socket is not
ready to accept the number of bytes that need to be written (assuming the
number of bytes required to be written is greater than one byte)

or

2. the synchronization or flush request need to be flushed manually with
successive PQflush calls

or

3. the functions will return an error condition when the connection is
non-blocking and no data could be written.

Any clarification in the documentation would be appreciated.

Re: Non-blocking synchronization in libpq using pipeline mode

От
Alvaro Herrera
Дата:
On 2024-Mar-06, PG Doc comments form wrote:

> The following documentation comment has been logged on the website:
> 
> Page: https://www.postgresql.org/docs/16/libpq-pipeline-mode.html
> Description:
> 
> The calls to PQpipelineSync and PQsendFlushRequest may either report failure
> or success, but not that data could not be written as of yet because the
> request would block.

Is this a literal quote?  If so, where do you see it?

> Does this mean that
> 
> 1. these functions will always block when invoked and the socket is not
> ready to accept the number of bytes that need to be written (assuming the
> number of bytes required to be written is greater than one byte)

No.

> 2. the synchronization or flush request need to be flushed manually with
> successive PQflush calls

Yes.

> or
> 
> 3. the functions will return an error condition when the connection is
> non-blocking and no data could be written.

No.

> Any clarification in the documentation would be appreciated.

So I checked https://www.postgresql.org/docs/16/libpq-pipeline-mode.html
and it has this, under PQsendFlushRequest:

   The server flushes its output buffer automatically as a result of
   PQpipelineSync being called, or on any request when not in pipeline
   mode; this function is useful to cause the server to flush its output
   buffer in pipeline mode without establishing a synchronization point.
   Note that the request is not itself flushed to the server
   automatically; use PQflush if necessary.

which I think answers what you are asking.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Just treat us the way you want to be treated + some extra allowance
 for ignorance."                                    (Michael Brusser)



Re: Non-blocking synchronization in libpq using pipeline mode

От
Jan Behrens
Дата:
On Tue, 19 Mar 2024 12:49:23 +0100
Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

> On 2024-Mar-06, PG Doc comments form wrote:
>
> > The following documentation comment has been logged on the website:
> >
> > Page: https://www.postgresql.org/docs/16/libpq-pipeline-mode.html
> > Description:
> >
> > The calls to PQpipelineSync and PQsendFlushRequest may either report failure
> > or success, but not that data could not be written as of yet because the
> > request would block.
>
> Is this a literal quote?  If so, where do you see it?

It is not a literal quote. It is a deduction from the function
signatures and the documented return values:

int PQpipelineSync(PGconn *conn);
int PQsendFlushRequest(PGconn *conn);

which, according to the documentation, only ever return 0 or 1,
indicating success or failure. (Compare with PQflush, which can return
three different values instead of two.)

>
> > Does this mean that
> >
> > 1. these functions will always block when invoked and the socket is not
> > ready to accept the number of bytes that need to be written (assuming the
> > number of bytes required to be written is greater than one byte)
>
> No.

That seems true for "PQsendFlushRequest" as the documentation 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).

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)?

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.

>
> > or
> >
> > 3. the functions will return an error condition when the connection is
> > non-blocking and no data could be written.
>
> No.
>
> > Any clarification in the documentation would be appreciated.
>
> So I checked https://www.postgresql.org/docs/16/libpq-pipeline-mode.html
> and it has this, under PQsendFlushRequest:
>
>    The server flushes its output buffer automatically as a result of
>    PQpipelineSync being called, or on any request when not in pipeline
>    mode; this function is useful to cause the server to flush its output
>    buffer in pipeline mode without establishing a synchronization point.
>    Note that the request is not itself flushed to the server
>    automatically; use PQflush if necessary.
>
> which I think answers what you are asking.

It answers the behavior for "PQsendFlushRequest", but it's still
unclear how "PQpipelineSync" will act.

>
> --
> Álvaro Herrera        Breisgau, Deutschland  ?  https://www.EnterpriseDB.com/

Kind Regards,
Jan Behrens



Re: Non-blocking synchronization in libpq using pipeline mode

От
Jan Behrens
Дата:
On Mon, 25 Mar 2024 15:47:00 +0100
Jan Behrens <jbe-mlist@magnetkern.de> wrote:

> On Tue, 19 Mar 2024 12:49:23 +0100
> Alvaro Herrera <alvherre@alvh.no-ip.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