Refactor recovery conflict signaling a little

Heikki Linnakangas <hlinnaka@iki.fi>

From: Heikki Linnakangas <hlinnaka@iki.fi>
To: "pgsql-hackers@postgresql.org" <pgsql-hackers@postgresql.org>
Date: 2026-01-22T23:20:20Z
Lists: pgsql-hackers

Attachments

I had a look at recovery conflict signaling and a few things caught my 
eye. No functional changes, but some cleanups and readability improvements:

Patch 0001: Remove useless errdetail_abort()
--------------------------------------------

The function is supposed to add DETAIL to errors when you are in an 
aborted transaction, if the transaction was aborted by a recovery 
conflict, like this:

ERROR:  current transaction is aborted, commands ignored until end of 
transaction block"
DETAIL:  Abort reason: recovery conflict

But I don't see how to reach that. If a transaction is aborted by 
recovery conflict, you get a different error like this:

ERROR:  canceling statement due to conflict with recovery
DETAIL:  User was holding a relation lock for too long.

The transaction abort clears the 'recoveryConflictPending' flag, so even 
if that happens in a transaction block, you don't get that "DETAIL: 
Abort reason: recovery conflict" in the subsequent errors.

errdetail_abort() was introduced in commit a8ce974cdd. I suppose it was 
needed back then, but the signal handling has changed a lot since. 
Looking at that commit now, though, I don't really understand how it was 
reachable even back then. (Except with a race with an unrelated 
transaction abort, see commit message)

Has anyone seen the "DETAIL:  Abort reason: recovery conflict" in recent 
years, or ever? If not, let's rip it out.


0002: Don't hint that you can reconnect when the database is dropped
--------------------------------------------------------------------

If you're connected to a database is being dropped, during recovery, you 
get an error like this:

FATAL:  terminating connection due to conflict with recovery
DETAIL:  User was connected to a database that must be dropped.
HINT:  In a moment you should be able to reconnect to the database and 
repeat your command.

The hint seems misleading. The database is being dropped, you most 
likely can *not* reconnect to it. Let's remove it.


0003-0004:  Separate RecoveryConflictReasons from procsignals
-------------------------------------------------------------

We're currently using different PROCSIG_* flags to indicate different 
kinds of recovery conflicts. We're also abusing the same flags in 
functions like LogRecoveryConflict, which isn't related to inter-process 
signaling. It seems better to have a separate enum for the recovery 
conflict reasons. With this patch, there's just a single 
PROCSIG_RECOVERY_CONFLICT to wake up a process on a recovery conflict, 
and the reason is communicated by setting a flag in a bitmask in PGPROC.

I was inspired to do this in preparation of my project to replaces 
latches with "interrupts". By having just a single PROCSIG flag, we 
reduce the need for "interrupt bits" with that project. But it seems 
nicer on its own merits too.


0005: Refactor ProcessRecoveryConflictInterrupt for readability
---------------------------------------------------------------

The function had a switch-statement with fallthrough through all the 
cases. It took me a while to understand how it works. Once I finally 
understood it, I refactored it to not rely on the fallthrough. I hope 
this makes it easier for others too.

- Heikki

Commits

  1. Don't clear pendingRecoveryConflicts at end of transaction

  2. Refactor ProcessRecoveryConflictInterrupt for readability

  3. Separate RecoveryConflictReasons from procsignals

  4. Use ProcNumber rather than pid in ReplicationSlot

  5. Don't hint that you can reconnect when the database is dropped

  6. Remove useless errdetail_abort()

  7. Teach standby conflict resolution to use SIGUSR1