v1-0001-Skip-pg_database.dathasloginevt-cleanup-on-standb.patch
application/octet-stream
Filename: v1-0001-Skip-pg_database.dathasloginevt-cleanup-on-standb.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: Skip pg_database.dathasloginevt cleanup on standby
| File | + | − |
|---|---|---|
| src/backend/commands/event_trigger.c | 10 | 1 |
From 872acc7d8dc3c4efdabc2ce32ef5556c46440e4d Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
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 <kyzevan23@mail.ru>
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