diff options
author | Michael Paquier <michael@paquier.xyz> | 2023-05-10 11:24:30 +0900 |
---|---|---|
committer | Michael Paquier <michael@paquier.xyz> | 2023-05-10 11:24:30 +0900 |
commit | 605994651b6a4209b418cb09d3c223ca269f7cfc (patch) | |
tree | b65232b6ef02413cf2bbe5a0f9c910bd7f164fa7 /src/backend | |
parent | 4d47eff99cc08255f0ae3cc27dc24cc9b30a32e7 (diff) | |
download | postgresql-605994651b6a4209b418cb09d3c223ca269f7cfc.tar.gz |
Fix assertion failure when updating stats_fetch_consistency in a transaction
An update of the GUC stats_fetch_consistency in a transaction would be
able to trigger an assertion when doing cache->snapshot. In this case,
when retrieving a pgstat entry after the switch, a new snapshot would be
rebuilt, confusing pgstat_build_snapshot() because a snapshot is already
cached with an unexpected mode ("cache").
In order to fix this problem, this commit adds a flag to force a
snapshot clear each time this GUC is changed. Some tests are added to
check, while on it.
Some optimizations in avoiding the snapshot clear should be possible
depending on what is cached and the current GUC value, I guess, but this
solution is simple, and ensures that the state of the cache is updated
each time a new pgstat entry is fetched, hence being consistent with the
level wanted by the client that has set the GUC.
Note that cache->none and snapshot->none would not cause issues, as
fetching a pgstat entry would be retrieved from shared memory on the
second attempt, however a snapshot would still be cached. Similarly,
none->snapshot and none->cache would build a new snapshot on the second
fetch attempt. Finally, snapshot->cache would cache a new snapshot on
the second attempt.
Reported-by: Alexander Lakhin
Author: Kyotaro Horiguchi
Discussion: https://postgr.es/m/17804-2a118cd046f2d0e5@postgresql.org
backpatch-through: 15
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/utils/activity/pgstat.c | 36 | ||||
-rw-r--r-- | src/backend/utils/misc/guc_tables.c | 2 |
2 files changed, 35 insertions, 3 deletions
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index b125802b21..f6edfc76ac 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -103,7 +103,7 @@ #include "storage/lwlock.h" #include "storage/pg_shmem.h" #include "storage/shmem.h" -#include "utils/guc.h" +#include "utils/guc_hooks.h" #include "utils/memutils.h" #include "utils/pgstat_internal.h" #include "utils/timestamp.h" @@ -228,6 +228,13 @@ static dlist_head pgStatPending = DLIST_STATIC_INIT(pgStatPending); static bool pgStatForceNextFlush = false; /* + * Force-clear existing snapshot before next use when stats_fetch_consistency + * is changed. + */ +static bool force_stats_snapshot_clear = false; + + +/* * For assertions that check pgstat is not used before initialization / after * shutdown. */ @@ -760,7 +767,8 @@ pgstat_reset_of_kind(PgStat_Kind kind) * request will cause new snapshots to be read. * * This is also invoked during transaction commit or abort to discard - * the no-longer-wanted snapshot. + * the no-longer-wanted snapshot. Updates of stats_fetch_consistency can + * cause this routine to be called. */ void pgstat_clear_snapshot(void) @@ -787,6 +795,9 @@ pgstat_clear_snapshot(void) * forward the reset request. */ pgstat_clear_backend_activity_snapshot(); + + /* Reset this flag, as it may be possible that a cleanup was forced. */ + force_stats_snapshot_clear = false; } void * @@ -885,6 +896,9 @@ pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, Oid objoid) TimestampTz pgstat_get_stat_snapshot_timestamp(bool *have_snapshot) { + if (force_stats_snapshot_clear) + pgstat_clear_snapshot(); + if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT) { *have_snapshot = true; @@ -929,6 +943,9 @@ pgstat_snapshot_fixed(PgStat_Kind kind) static void pgstat_prep_snapshot(void) { + if (force_stats_snapshot_clear) + pgstat_clear_snapshot(); + if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE || pgStatLocal.snapshot.stats != NULL) return; @@ -1671,3 +1688,18 @@ pgstat_reset_after_failure(void) /* and drop variable-numbered ones */ pgstat_drop_all_entries(); } + +/* + * GUC assign_hook for stats_fetch_consistency. + */ +void +assign_stats_fetch_consistency(int newval, void *extra) +{ + /* + * Changing this value in a transaction may cause snapshot state + * inconsistencies, so force a clear of the current snapshot on the next + * snapshot build attempt. + */ + if (pgstat_fetch_consistency != newval) + force_stats_snapshot_clear = true; +} diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 2f42cebaf6..5f90aecd47 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -4821,7 +4821,7 @@ struct config_enum ConfigureNamesEnum[] = }, &pgstat_fetch_consistency, PGSTAT_FETCH_CONSISTENCY_CACHE, stats_fetch_consistency, - NULL, NULL, NULL + NULL, assign_stats_fetch_consistency, NULL }, { |