Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Prevent inconsistent use of stats entry for replication slots
authorMichael Paquier <michael@paquier.xyz>
Wed, 5 Jun 2024 23:48:21 +0000 (08:48 +0900)
committerMichael Paquier <michael@paquier.xyz>
Wed, 5 Jun 2024 23:48:21 +0000 (08:48 +0900)
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

src/backend/utils/activity/pgstat_replslot.c
src/backend/utils/activity/pgstat_shmem.c

index 1a8473bcd452ae18deed1770a910ce847d0f1d01..4faf06045c54fba74d844abf45d5545ee9d4e4ef 100644 (file)
@@ -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();
 }
 
 /*
index 9a4f037959edf01f4d7375c4c382b5b117dc2c38..9e4ea2e04286c10579a417f1258ba81f6a2e8426 100644 (file)
@@ -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)
 {