Re: LISTEN/NOTIFY bug: VACUUM sets frozenxid past a xid in async queue
Heikki Linnakangas <hlinnaka@iki.fi>
From: Heikki Linnakangas <hlinnaka@iki.fi>
To: Matheus Alcantara <matheusssilv97@gmail.com>,
Arseniy Mukhin <arseniy.mukhin.dev@gmail.com>
Cc: Joel Jacobson <joel@compiler.org>, Tom Lane <tgl@sss.pgh.pa.us>, pgsql-hackers <pgsql-hackers@postgresql.org>, Álvaro Herrera <alvherre@kurilemu.de>
Date: 2025-11-06T10:05:20Z
Lists: pgsql-hackers
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Clear 'xid' in dummy async notify entries written to fill up pages
- 84f1bf4afa5e 14.21 landed
- 21a9014cf00a 15.16 landed
- 0e8eaa2181d4 16.12 landed
- d80d5f099502 17.8 landed
- 82fa6b78dba1 18.2 landed
- 0bdc777e8007 19 (unreleased) landed
-
Fix remaining race condition with CLOG truncation and LISTEN/NOTIFY
- c2e58c0711fe 14.21 landed
- 0c862646cf2a 15.16 landed
- 44e8c60be66c 16.12 landed
- c2682810ab7d 17.8 landed
- 7b069a1876e4 18.2 landed
- 797e9ea6e54b 19 (unreleased) landed
-
Fix bug where we truncated CLOG that was still needed by LISTEN/NOTIFY
- eba917d360e7 14.21 landed
- 1a469d7b5b7d 15.16 landed
- 053e1868b7ee 16.12 landed
- d02c03ddc5e3 17.8 landed
- 321ec54625fd 18.2 landed
- 8eeb4a0f7c06 19 (unreleased) landed
-
Escalate ERRORs during async notify processing to FATAL
- 7cb05dd2d198 14.21 landed
- b1da37de21d4 15.16 landed
- c1a5bde003b8 16.12 landed
- b821c92920f0 17.8 landed
- aab4a84bb070 18.2 landed
- 1b4699090eaf 19 (unreleased) landed
-
Limit the size of TID lists during parallel GIN build
- c98dffcb7c70 19 (unreleased) cited
Attachments
- v3-0001-Escalate-ERRORs-during-async-notify-processing-to.patch (text/x-patch) patch v3-0001
- v3-0002-Fix-bug-where-we-truncated-CLOG-that-was-still-ne.patch (text/x-patch) patch v3-0002
- v3-0003-Fix-remaining-race-condition-with-CLOG-truncation.patch (text/x-patch) patch v3-0003
On 05/11/2025 21:02, Matheus Alcantara wrote: > On Wed Nov 5, 2025 at 9:59 AM -03, Heikki Linnakangas wrote: >>> In case of an error on TransactionIdDidCommit() I think that we will >>> have the same behavior as we advance the "current" position before of >>> DidCommit call the PG_FINALLY block will set the backend position past >>> the failing notification entry. >> >> With my patch, if TransactionIdDidCommit() fails, we will lose all the >> notifications that we have buffered in the local buffer but haven't >> passed to NotifyMyFrontEnd() yet. On 'master', you only lose a single >> notification, the one where TransactionIdDidCommit() failed. >> > Yeah, that's true. > >>> How bad would be to store the notification entries within a list and >>> store the next position with the notification entry and then wrap the >>> NotifyMyFrontEnd() call within a PG_TRY and update the "current" to the >>> saved "next position" on PG_CATCH? Something like this: >> >> [ ...] >> >> That addresses the failure on NotifyMyFrontEnd, but not on >> TransactionIdDidCommit. >> >> IMHO we should just make these errors FATAL. TransactionIdDidCommit() >> should not really fail, after fixing the bug we're discussing. >> NotifyMyFrontEnd() could fail on OOM, but that seems pretty unlikely on >> an otherwise idle connection. Or it could fail if the client connection >> is lost, but then the backend is about to die anyway. And arguably >> closing the connection is better than losing even a single notification, >> anyway. >> > My only concern with making these errors FATAL is that if a notification > entry causes a different, recoverable error, all subsequent messages > will be lost. This is because if backend die and the user open a new > connection and execute LISTEN on the channel it will not see these > notifications past the one that caused the error. I'm not sure if we are > completely safe from this case of a recoverable error, what do you > think? I did some more testing of the current behavior, using the encoding conversion to cause an error: In backend A: SET client_encoding='latin1'; LISTEN foo; Backend b: NOTIFY foo, 'ハ'; Observations: - If the connection is idle when the notification is received, the ERROR is turned into FATAL anyway: postgres=# SET client_encoding='latin1'; SET postgres=# LISTEN foo; LISTEN postgres=# select 1; -- do the NOTIFY in another connection before this ERROR: character with byte sequence 0xe3 0x83 0x8f in encoding "UTF8" has no equivalent in encoding "LATIN1" FATAL: terminating connection because protocol synchronization was lost server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. - If there are multiple notifications pending, we stop processing the subsequent notifications on the first error. The subsequent notifications will only be processed when another notify interrupt is received, i.e. when a backend sends yet another notification. I'm getting more and more convinced that escalating all ERRORs to FATALs during notify processing is the right way to go. Attached is a new patch set that does that. - Heikki