aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 2b3e7c491a9..cec48c050ba 100644
--- a/src/backend/utils/activity/pgstat_shmem.c
+++ b/src/backend/utils/activity/pgstat_shmem.c
@@ -273,6 +273,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);
@@ -296,6 +301,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 */
@@ -336,6 +347,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);
}
/*
@@ -501,7 +513,8 @@ pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, Oid objoid, 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);
@@ -568,10 +581,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 4b65dfef7d4..efe5dfc2321 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -94,6 +94,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.
@@ -133,6 +146,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