diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c index d743de951a..67f56e1dde 100644 --- a/src/backend/utils/activity/pgstat_shmem.c +++ b/src/backend/utils/activity/pgstat_shmem.c @@ -278,6 +278,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); @@ -301,6 +306,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 */ @@ -341,6 +352,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); } /* @@ -506,7 +518,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); @@ -573,10 +586,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 dbbca31602..2210677839 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -93,6 +93,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 @@ -132,6 +145,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