Stmt timeout error can be sent after a CommandComplete

Started by Anthonin Bonnefoyabout 7 hours ago1 messages
#1Anthonin Bonnefoy
anthonin.bonnefoy@datadoghq.com
1 attachment(s)

Hi,

At the end of a command, disable_statement_timeout is called as the
timeout applies to a specific command and a CommandComplete message is
sent.

However, it's possible for the stmt timeout to have been fired between
the last message and the call to disable_statement_timeout. The
timeout won't be active anymore, so disable_timeout will be skipped.
However, the timeout is still queued, and the next call to
CHECK_FOR_INTERRUPTS will process and generate a stmt timeout error.

This leads to the confusing situation where we can have a
CompleteCommand message followed by a stmt timeout error for the same
command.

This patch fixes this issue by resetting the timeout indicator if the
timeout is inactive, relying on get_timeout_indicator to reset the
timeout indicator.

Regards,
Anthonin Bonnefoy

Attachments:

v1-0001-Reset-stmt-timeout-indicator-on-disable_statement.patchapplication/octet-stream; name=v1-0001-Reset-stmt-timeout-indicator-on-disable_statement.patchDownload
From fde0f84597c824da977e906853fea75835c1626f Mon Sep 17 00:00:00 2001
From: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Date: Tue, 13 Jan 2026 08:49:23 +0100
Subject: Reset stmt timeout indicator on disable_statement_timeout

At the end of a command, disable_statement_timeout is called as the
timeout applies to a specific command and a CommandComplete message is
sent.

However, it's possible for the stmt timeout to have been fired between
the last message and the call to disable_statement_timeout. The timeout
won't be active anymore, so disable_timeout will be skipped. However,
the timeout is still queued, and the next call to CHECK_FOR_INTERRUPTS
will process and generate a stmt_timeout error.

This leads to the confusing situation where we can have a
CompleteCommand message followed by a StmtTimeout error for the same
command.

This patch fixes this issue by resetting the timeout indicator if the
timeout is inactive.
---
 src/backend/tcop/postgres.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e54bf1e760f..aa9be553fb9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5229,11 +5229,15 @@ enable_statement_timeout(void)
 }
 
 /*
- * Disable statement timeout, if active.
+ * Disable statement timeout, if active. If not active, the I've-been-fired
+ * indicator is reset.
  */
 static void
 disable_statement_timeout(void)
 {
 	if (get_timeout_active(STATEMENT_TIMEOUT))
 		disable_timeout(STATEMENT_TIMEOUT, false);
+	else
+		/* The timeout may have been fired, reset the timeout indicator */
+		get_timeout_indicator(STATEMENT_TIMEOUT, true);
 }
-- 
2.51.0