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