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: Álvaro Herrera <alvherre@kurilemu.de>, pgsql-hackers <pgsql-hackers@postgresql.org>
Cc: Matheus Alcantara <matheusssilv97@gmail.com>, Arseniy Mukhin <arseniy.mukhin.dev@gmail.com>, Joel Jacobson <joel@compiler.org>, Tom Lane <tgl@sss.pgh.pa.us>
Date: 2025-11-12T19:39:47Z
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 →
  1. Clear 'xid' in dummy async notify entries written to fill up pages

  2. Fix remaining race condition with CLOG truncation and LISTEN/NOTIFY

  3. Fix bug where we truncated CLOG that was still needed by LISTEN/NOTIFY

  4. Escalate ERRORs during async notify processing to FATAL

  5. Limit the size of TID lists during parallel GIN build

On 07/11/2025 16:14, Álvaro Herrera wrote:
> One thing I noticed while testing this is that asyncQueueAddEntries()
> fills the end of a page with a dummy entry, when the next notify doesn't
> fit.  However, this dummy entry contains a very valid TransactionId,
> which the new freezing code will try to look up and freeze.  I think
> this is somewhat bogus -- we shouldn't even try to look up that XID in
> the first place.  I propose to clear it like this
> 
> @@ -1419,6 +1424,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
>   			 */
>   			qe.length = QUEUE_PAGESIZE - offset;
>   			qe.dboid = InvalidOid;
> +			qe.xid = InvalidTransactionId;
>   			qe.data[0] = '\0';	/* empty channel */
>   			qe.data[1] = '\0';	/* empty payload */
>   		}
> 
> 
> (Line numbers do not match, because I have other local changes.)

Committed. I committed the above separately, because I forgot to include 
it in the main commit. Oops.

Just to summarize what was committed, out of all the different variants 
discussed:

* Any ERROR while processing an async notification is now turned into FATAL
* Vacuum scans the async notification queue and freezes xids before 
truncating CLOG
* The TransactionIdDidCommit() calls are now made while holding the SLRU 
lock. NotifyMyFrontEnd() calls are still made after releasing the lock
* listenChannels == NIL special case is checked before 
TransactionIdDidCommit(). This avoids the problem that no backend can 
LISTEN to anything anymore, if there's one broken entry in the queue for 
some reason
* 'xid' field on dummy entries is now set to InvalidTransactionId so 
that they don't need to be frozen

Thanks everyone!

- Heikki