From 872acc7d8dc3c4efdabc2ce32ef5556c46440e4d Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 May 2026 03:33:46 +0000 Subject: [PATCH v1] Skip pg_database.dathasloginevt cleanup on standby EventTriggerOnLogin() tries to clear pg_database.dathasloginevt when the database no longer has any login event triggers but the flag is still set. To make that safe against concurrent flag setters, it takes a conditional AccessExclusiveLock on the database object. On a hot standby, that lock acquisition fails outright with FATAL: cannot acquire lock mode AccessExclusiveLock on database objects while recovery is in progress because LockAcquireExtended() refuses locks stronger than RowExclusiveLock on database objects during recovery. The standby already replays the flag's value from the primary, so the dangling flag is the result of replaying a state in which the primary had already dropped its login event triggers but not yet run a login event trigger pass to clear the flag. Any session connecting to the standby in that window therefore fails to connect. Skip the cleanup on a standby. The flag will be cleared via WAL replay once the primary clears it on its side, which is the only correct way to update a catalog from a standby. Reported-by: Egor Chindyaskin Discussion: https://postgr.es/m/19488-...@postgresql.org --- src/backend/commands/event_trigger.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index dcd2f5a09bb..0680421268d 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -18,6 +18,7 @@ #include "access/htup_details.h" #include "access/table.h" #include "access/xact.h" +#include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" @@ -937,8 +938,16 @@ EventTriggerOnLogin(void) * lock to prevent concurrent SetDatabaseHasLoginEventTriggers(), but we * don't want to hang the connection waiting on the lock. Thus, we are * just trying to acquire the lock conditionally. + * + * Skip this on a hot standby: the conditional AccessExclusiveLock on the + * database object would fail with "cannot acquire lock mode ... while + * recovery is in progress", which the caller would surface as a FATAL + * connection error. On a standby we cannot (and must not) clear the + * pg_database flag ourselves; it will be cleared via WAL replay once the + * primary's next login event trigger run clears it on the primary. */ - else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId, + else if (!RecoveryInProgress() && + ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock)) { /* -- 2.43.0