aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/activity/pgstat_shmem.c40
-rw-r--r--src/include/utils/pgstat_internal.h19
2 files changed, 54 insertions, 5 deletions
diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c
index c1b7ff76b12..041c262b9fb 100644
--- a/src/backend/utils/activity/pgstat_shmem.c
+++ b/src/backend/utils/activity/pgstat_shmem.c
@@ -304,6 +304,11 @@ pgstat_init_entry(PgStat_Kind kind,
* further if a longer lived reference is needed.
*/
pg_atomic_init_u32(&shhashent->refcount, 1);
+
+ /*
+ * Initialize "generation" to 0, as freshly created.
+ */
+ pg_atomic_init_u32(&shhashent->generation, 0);
shhashent->dropped = false;
chunk = dsa_allocate0(pgStatLocal.dsa, pgstat_get_kind_info(kind)->shared_size);
@@ -327,6 +332,12 @@ pgstat_reinit_entry(PgStat_Kind kind, PgStatShared_HashEntry *shhashent)
/* mark as not dropped anymore */
pg_atomic_fetch_add_u32(&shhashent->refcount, 1);
+
+ /*
+ * Increment "generation", to let any backend with local references know
+ * that what they point to is outdated.
+ */
+ pg_atomic_fetch_add_u32(&shhashent->generation, 1);
shhashent->dropped = false;
/* reinitialize content */
@@ -367,6 +378,7 @@ pgstat_acquire_entry_ref(PgStat_EntryRef *entry_ref,
entry_ref->shared_stats = shheader;
entry_ref->shared_entry = shhashent;
+ entry_ref->generation = pg_atomic_read_u32(&shhashent->generation);
}
/*
@@ -532,7 +544,8 @@ pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create,
* case are replication slot stats, where a new slot can be
* created with the same index just after dropping. But oid
* wraparound can lead to other cases as well. We just reset the
- * stats to their plain state.
+ * stats to their plain state, while incrementing its "generation"
+ * in the shared entry for any remaining local references.
*/
shheader = pgstat_reinit_entry(kind, shhashent);
pgstat_acquire_entry_ref(entry_ref, shhashent, shheader);
@@ -599,10 +612,27 @@ pgstat_release_entry_ref(PgStat_HashKey key, PgStat_EntryRef *entry_ref,
if (!shent)
elog(ERROR, "could not find just referenced shared stats entry");
- Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) == 0);
- Assert(entry_ref->shared_entry == shent);
-
- pgstat_free_entry(shent, NULL);
+ /*
+ * This entry may have been reinitialized while trying to release
+ * it, so double-check that it has not been reused while holding a
+ * lock on its shared entry.
+ */
+ if (pg_atomic_read_u32(&entry_ref->shared_entry->generation) ==
+ entry_ref->generation)
+ {
+ /* Same "generation", so we're OK with the removal */
+ Assert(pg_atomic_read_u32(&entry_ref->shared_entry->refcount) == 0);
+ Assert(entry_ref->shared_entry == shent);
+ pgstat_free_entry(shent, NULL);
+ }
+ else
+ {
+ /*
+ * Shared stats entry has been reinitialized, so do not drop
+ * its shared entry, only release its lock.
+ */
+ dshash_release_lock(pgStatLocal.shared_hash, shent);
+ }
}
}
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 61b2e1f96b2..437db06910b 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -95,6 +95,19 @@ typedef struct PgStatShared_HashEntry
pg_atomic_uint32 refcount;
/*
+ * Counter tracking the number of times the entry has been reused.
+ *
+ * Set to 0 when the entry is created, and incremented by one each time
+ * the shared entry is reinitialized with pgstat_reinit_entry().
+ *
+ * May only be incremented / decremented while holding at least a shared
+ * lock on the dshash partition containing the entry. Like refcount, it
+ * needs to be an atomic variable because multiple backends can increment
+ * the generation with just a shared lock.
+ */
+ pg_atomic_uint32 generation;
+
+ /*
* Pointer to shared stats. The stats entry always starts with
* PgStatShared_Common, embedded in a larger struct containing the
* PgStat_Kind specific stats fields.
@@ -134,6 +147,12 @@ typedef struct PgStat_EntryRef
PgStatShared_Common *shared_stats;
/*
+ * Copy of PgStatShared_HashEntry->generation, keeping locally track of
+ * the shared stats entry "generation" retrieved (number of times reused).
+ */
+ uint32 generation;
+
+ /*
* Pending statistics data that will need to be flushed to shared memory
* stats eventually. Each stats kind utilizing pending data defines what
* format its pending data has and needs to provide a