Re: Pgbench: remove synchronous prepare

Dmitrii Bondar <d.bondar@postgrespro.ru>

From: Dmitrii Bondar <d.bondar@postgrespro.ru>
To: Robert Haas <robertmhaas@gmail.com>
Cc: pgsql-hackers@lists.postgresql.org
Date: 2026-04-28T09:29:51Z
Lists: pgsql-hackers
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