Infinite loop in pgbench when running COPY command

Started by Anthonin Bonnefoy3 months ago3 messages
#1Anthonin Bonnefoy
anthonin.bonnefoy@datadoghq.com
1 attachment(s)

Hi,

Currently, pgbench processes a copy response as unexpected and will
move to the error loop. However, PQgetResult will alway return an
empty result when there's no async result through getCopyResult,
leading to an infinite loop in the error handling as res will never be
NULL.

This patch forcefully exits the copy state with PQendcopy before
moving to the error handler, avoiding the infinite loop.

Regards,
Anthonin Bonnefoy

Attachments:

v01-0001-Exit-copy-state-in-pgbench-to-avoid-infinite-loo.patchapplication/octet-stream; name=v01-0001-Exit-copy-state-in-pgbench-to-avoid-infinite-loo.patchDownload
From 02dfe9220f15719cc37431b325e730510f887d2c Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Wed, 1 Oct 2025 10:37:24 +0200
Subject: [PATCH v01] Exit copy state in pgbench to avoid infinite loop

Currently, pgbench aborts when a copy response is received in
readCommandResponse. However, PQgetResult will return an empty
result when there's no async result through getCopyResult, leading to an
infinite loop in the error handling.

This patch forcefully exits the copy state with PQendcopy before moving
to the error handler, avoiding the infinite loop.
---
 src/bin/pgbench/pgbench.c                    | 13 +++++++++++++
 src/bin/pgbench/t/001_pgbench_with_server.pl | 11 +++++++++++
 2 files changed, 24 insertions(+)

diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 36c6469149e..af948c8328c 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -3359,6 +3359,19 @@ readCommandResponse(CState *st, MetaCommand meta, char *varprefix)
 								 PQresultErrorMessage(res));
 				break;
 
+			case PGRES_COPY_IN:
+			case PGRES_COPY_OUT:
+			case PGRES_COPY_BOTH:
+				pg_log_error("COPY is not supported in pgbench, aborting");
+
+				/*
+				 * We need to exit copy state. Otherwise, PQgetResult will
+				 * always return an empty PGresult from getCopyResult, leading
+				 * to an infinite loop during error cleanup
+				 */
+				PQendcopy(st->con);
+				goto error;
+
 			case PGRES_NONFATAL_ERROR:
 			case PGRES_FATAL_ERROR:
 				st->estatus = getSQLErrorStatus(PQresultErrorField(res,
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 7dd78940300..f61dcf68234 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -1810,6 +1810,17 @@ update counter set i = i+1 returning i \gset
 }
 	});
 
+# Test copy in pgbench
+$node->pgbench(
+	'-t 10',
+	2,
+	[],
+	[ qr{COPY is not supported in pgbench, aborting} ],
+	'Test copy in script',
+	{
+		'001_copy' => q{ COPY pgbench_accounts FROM stdin }
+	});
+
 # Clean up
 $node->safe_psql('postgres', 'DROP TABLE counter;');
 
-- 
2.51.0

#2Michael Paquier
michael@paquier.xyz
In reply to: Anthonin Bonnefoy (#1)
Re: Infinite loop in pgbench when running COPY command

On Wed, Oct 01, 2025 at 11:25:00AM +0200, Anthonin Bonnefoy wrote:

Currently, pgbench processes a copy response as unexpected and will
move to the error loop. However, PQgetResult will alway return an
empty result when there's no async result through getCopyResult,
leading to an infinite loop in the error handling as res will never be
NULL.

This patch forcefully exits the copy state with PQendcopy before
moving to the error handler, avoiding the infinite loop.

Fun. It seems like nobody has ever tested this scenario, even for a
COPY TO. Why did you try that? Did you have a benchmark scenario in
mind?

+            case PGRES_COPY_IN:
+            case PGRES_COPY_OUT:
+            case PGRES_COPY_BOTH:
+                pg_log_error("COPY is not supported in pgbench, aborting");
+
+                /*
+                 * We need to exit copy state. Otherwise, PQgetResult will
+                 * always return an empty PGresult from getCopyResult, leading
+                 * to an infinite loop during error cleanup
+                 */
+                PQendcopy(st->con);
+                goto error;

That sounds like a good choice to just fail for the time being, all
these result types don't seem to work (quickly checked). If somebody
wants to support this case, we could always sort it out, but I am
ready to bet that the odds are in the favor of doing nothing.
--
Michael

#3Anthonin Bonnefoy
anthonin.bonnefoy@datadoghq.com
In reply to: Michael Paquier (#2)
Re: Infinite loop in pgbench when running COPY command

On Thu, Oct 2, 2025 at 10:27 AM Michael Paquier <michael@paquier.xyz> wrote:

Fun. It seems like nobody has ever tested this scenario, even for a
COPY TO. Why did you try that? Did you have a benchmark scenario in
mind?

I was trying to trigger pipeline errors while reviewing the
--continue-client-on-abort pgbench patch. And from past experience,
mixing pipelining and COPY is a good way to trigger those.

That sounds like a good choice to just fail for the time being, all
these result types don't seem to work (quickly checked). If somebody
wants to support this case, we could always sort it out, but I am
ready to bet that the odds are in the favor of doing nothing.

Yeah, that was also my take.