aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2024-06-06 08:48:21 +0900
committerMichael Paquier <michael@paquier.xyz>2024-06-06 08:48:21 +0900
commitbfc44da2478954330233a8df8a146ee920400947 (patch)
tree4e41b9d3e8168a6f41d95e203394d790d4525f6e
parentbb8425491cb7d5c760913761bf13644a7e86f58f (diff)
downloadpostgresql-bfc44da2478954330233a8df8a146ee920400947.tar.gz
postgresql-bfc44da2478954330233a8df8a146ee920400947.zip
Prevent inconsistent use of stats entry for replication slots
Concurrent activity around replication slot creation and drop could cause a replication slot to use a stats entry it should not have used when created, triggering an assertion failure when retrieving this inconsistent entry from the dshash table used by the stats facility. The issue is that pgstat_drop_replslot() calls pgstat_drop_entry() without checking the result. If pgstat_drop_entry() cannot free the entry related to the object dropped, pgstat_request_entry_refs_gc() should be called. AtEOXact_PgStat_DroppedStats() and surrounding routines dropping stats entries already do that. This is documented in pgstat_internal.h, but let's add a comment at the top of pgstat_drop_entry() as that can be easy to miss. Reported-by: Alexander Lakhin Author: Floris Van Nee Analyzed-by: Andres Freund Discussion: https://postgr.es/m/17947-b9554521ad963c9c@postgresql.org Backpatch-through: 15
-rw-r--r--src/backend/utils/activity/pgstat_replslot.c5
-rw-r--r--src/backend/utils/activity/pgstat_shmem.c11
2 files changed, 14 insertions, 2 deletions
diff --git a/src/backend/utils/activity/pgstat_replslot.c b/src/backend/utils/activity/pgstat_replslot.c
index 1a8473bcd45..4faf06045c5 100644
--- a/src/backend/utils/activity/pgstat_replslot.c
+++ b/src/backend/utils/activity/pgstat_replslot.c
@@ -153,8 +153,9 @@ pgstat_acquire_replslot(ReplicationSlot *slot)
void
pgstat_drop_replslot(ReplicationSlot *slot)
{
- pgstat_drop_entry(PGSTAT_KIND_REPLSLOT, InvalidOid,
- ReplicationSlotIndex(slot));
+ if (!pgstat_drop_entry(PGSTAT_KIND_REPLSLOT, InvalidOid,
+ ReplicationSlotIndex(slot)))
+ pgstat_request_entry_refs_gc();
}
/*
diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c
index 9a4f037959e..9e4ea2e0428 100644
--- a/src/backend/utils/activity/pgstat_shmem.c
+++ b/src/backend/utils/activity/pgstat_shmem.c
@@ -850,6 +850,17 @@ pgstat_drop_database_and_contents(Oid dboid)
pgstat_request_entry_refs_gc();
}
+/*
+ * Drop a single stats entry.
+ *
+ * This routine returns false if the stats entry of the dropped object could
+ * not be freed, true otherwise.
+ *
+ * The callers of this function should call pgstat_request_entry_refs_gc()
+ * if the stats entry could not be freed, to ensure that this entry's memory
+ * can be reclaimed later by a different backend calling
+ * pgstat_gc_entry_refs().
+ */
bool
pgstat_drop_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
{