From: Dmitrii Bondar <d.bondar@postgrespro.ru>
To: Robert Haas <robertmhaas@gmail.com>
Cc: pgsql-hackers@lists.postgresql.org
Subject: Re: Pgbench: remove synchronous prepare
Date: Tue, 28 Apr 2026 16:29:51 +0700
Message-ID: <6e161344-d26a-4e3d-aa12-83a05a3ca8dc@postgrespro.ru> (raw)
In-Reply-To: <CA+TgmoZN2hfeqCrguSezTqPmxKE3jSf8Bnf1-o6ubexgVfj0sA@mail.gmail.com>
References: <2da56b59-acb6-4ee3-af6b-6e800115dc7f@postgrespro.ru>
<d8b4e3b2-a21f-4989-9663-3689f0d4232b@postgrespro.ru>
<cdb264ce-acb5-4a20-bd69-5eb924bef253@postgrespro.ru>
<CA+TgmoZN2hfeqCrguSezTqPmxKE3jSf8Bnf1-o6ubexgVfj0sA@mail.gmail.com>
On 4/27/26 12:01 AM, Robert Haas wrote:
> On Mon, Mar 16, 2026 at 3:46 AM Dmitrii Bondar<d.bondar@postgrespro.ru> wrote:
>> Rebase.
> Hi,
>
> I think that this patch is changing more behavior than is explained in
> the commit message. The existing code calls PQsendQueryPrepared, which
> only tries to execute an already-prepared query. The replacement code
> tries to prepare the query. It is not clear to me what's going on
> here. I would have expected that we would only ever reach that point
> in the code with the query already prepared; otherwise, the existing
> code would presumably fail. But if that is the case then how is the
> new code managing to do anything different than the old code?
>
> Another way to see that the patch must be changing more behavior than
> advertised is the change to 001_pgbench_with_server.pl. That change
> comes with no comment changes and no explanation of any kind.
>
> If this patch were just about doing something asynchronously instead
> of synchronously, I think that would be fine, but I don't think that's
> all that is happening here. The original post explains the problem
> behavior (pgbench freezing under certain circumstances) but I don't
> understand what causes that behavior. I think I would understand
> better if the original complaint were about something other than
> session pooling mode: then, I might expect that we might unexpectedly
> discover that our session does not have something prepared which we
> expected to find prepared, and maybe this revised logic in
> sendCommand() would somehow fix that. But in session pooling mode,
> shouldn't everything be the same as if connection pooling is not in
> use at all? What's actually different?
>
Hi,
The patch does not change the existing behavior when a query has already
been prepared. Both the old and new code paths use
|PQsendQueryPrepared|, which sends a bind-execute-sync packet sequence
without waiting for a response.
The main difference appears when the query has not yet been prepared. In
the old code, |PQprepare| is called, which sends a parse message and
then waits for the result via |PQexecFinish|. Since |PQexecFinish|
blocks until a response arrives, it can block the entire thread if the
server has not responded yet.
I replaced the call to |PQprepare| with a call to the new |PQsendPBES|
function. Like |PQsendQueryPrepared|, it works asynchronously, but it
sends a parse-bind-execute-sync sequence instead. This change avoids
thread blocking because it eliminates the need to call |PQexecFinish|.
I chose to send a parse-bind-execute-sync sequence to match the behavior
of extended query mode, in which pgbench sends the same sequence, but
with an unnamed statement.
The expected output for |001_pgbench_with_server.pl| was changed for the
following reason. In the old pgbench code, |prepareCommand| is called
and receives |ERROR: syntax error|. Since |prepareCommand| does not
return a status, pgbench continues execution and then attempts to run
the command with |PQsendQueryPrepared|. This leads to the error
|prepared statement .* does not exist|, which is caused by the bind packet.
In the new code, |PQsendPBES| sends a parse-bind-execute-sync packet
sequence. If the parse step fails with |ERROR: syntax error|, all
subsequent messages are ignored until the sync packet is processed. That
is why the additional |prepared statement .* does not exist| error from
the bind packet no longer appears.
Session mode is indeed the most transparent way to use a pooler.
However, pgbench can become stuck when the number of clients exceeds the
pool size. If the pooler cannot reserve a backend for a client, it
places the client in a waiting queue. In that case, pgbench may wait
indefinitely because it is blocked in |PQprepare|, and the pgbench
thread cannot process responses for other clients.
Regards,
Dmitrii Bondar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: d.bondar@postgrespro.ru, robertmhaas@gmail.com, pgsql-hackers@lists.postgresql.org
Subject: Re: Pgbench: remove synchronous prepare
In-Reply-To: <6e161344-d26a-4e3d-aa12-83a05a3ca8dc@postgrespro.ru>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox