Re: two statement_timeout race conditions

From: Tomas Vondra <tomas(at)vondra(dot)me>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: two statement_timeout race conditions
Date: 2026-11-14 23:33:22
Message-ID: 77d08d0c-e087-4f68-b4df-c8d8d190756e@vondra.me
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 11/15/26 00:06, Zsolt Parragi wrote:
> Hello!
>
> I tested this patch a bit, and claude pointed out another corner case:
> lock_timeout firing during COMMIT still leaks into the next command
>
> finish_xact_command() runs the new discard before the commit:
>
> /* cancel active statement timeout after each command */
> disable_statement_timeout();
>
> if (xact_started)
> {
> CommitTransactionCommand();
>
> But commit can still wait for a heavyweight lock: PreCommit_Notify()
> takes the "database 0" lock, and there's no CHECK_FOR_INTERRUPTS()
> after the grant until the end of the commit. If lock_timeout fires
> just as that lock is granted, the COMMIT succeeds and the next command
> fails. Tiny window, so it needs an additional sleep to be reliably
> reproducible.
>
> The same happens with an extended protocol autocommit SELECT
> pg_notify(...) (committed at Sync) followed by any statement, and with
> a pipelined statement after COMMIT.
>
> This seems to fix it:
>
> @@ finish_xact_command
> CommitTransactionCommand();
>
> + /*
> + * Commit itself can wait for a heavyweight lock (PreCommit_Notify),
> + * after disable_statement_timeout() above already ran. Discard a
> + * lock timeout request left behind by a grant at timeout time.
> + */
> + if (get_timeout_indicator(LOCK_TIMEOUT, true))
> + QueryCancelPending = false;
>

Hmmm. I guess that could happen ...

>
> And in 0002:
>
> QueryCancelPending = false;
> + (void) get_timeout_indicator(LOCK_TIMEOUT, true);
> + (void) get_timeout_indicator(STATEMENT_TIMEOUT, true);
> ereport(WARNING,
> (errmsg("canceling wait for synchronous replication due to user request"),
>
> Won't this result in the same incorrect message pattern the patch
> tries to repair elsewhere?

You mean the "statement timeout" reported as "user request"? I believe
that happens because the timeout indicator is cleared, while the
QueryCancelPending is left set. But here we're resetting both, so I
don't see how could it have the same effect.

What's the scenario that'd lead to the same issue?

regards

--
Tomas Vondra

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Zsolt Parragi 2026-11-14 23:50:47 Re: two statement_timeout race conditions
Previous Message Zsolt Parragi 2026-11-14 23:06:35 Re: two statement_timeout race conditions