Hello, Thanks for the review! On Tue, Mar 31, 2026 at 8:27 PM Tomas Vondra <[email protected]> wrote:
> Hi, > > Thanks for working on this, I think having some stats about the vfd > cache would be quite helpful. I took a quick look at the patch, and in > general it goes in the right direction. > > Here's a couple comments / suggestions: > > 6) pgstat.h > > - Aren't evictions mostly the same as misses, at least after a while? > Correct, the time where they are not the same is pretty much meaningless info, i removed it. > > - I think it would be useful to report how many file descriptors we > are allowed to open (it's less than max_files_per_process, depending > on the ulimits etc.) > Agree, This should be max_safe_fds calculated by postmaster, I added this but let me know if its acceptable to export max_safe_fds in the way I did. > > - I know io_uring can consume quite a few descriptors, and it can cause > issues, I wonder if this would make it easier to observe > > > I also suggest to split the patch into smaller patches, to make it > easier to review and evaluate. Not because of size - the patch is fairly > small. But it's better to not mix multiple features with different > cost/benefit trade offs, because then it's possible to evaluate them > separately. Maybe even commit the first part and continue discussion > about the following one(s). > > This patch seems to mix two different types of stats - global stats of > the vfd cache, and then also per-backend stats. Those seems like very > different things, both in terms of overhead and benefits. > > The global cache stats is going to be virtually free (at least the > hits/misses, I'm not sure about the number of entries and bytes), and > it's obviously useful for tuning the max_files_per_process GUC. I'd even > contemplate getting this into PG19, maybe. > > The per-backend stats seem like a much harder sell to me, but I can be > convinced. Maybe it's not an issue in terms of overhead, maybe the stats > we get from that are worth it. Not sure. But I'd keep it in a separate > 0002 patch, on top of 0001 with just the "global" stats. > > > > regards > > -- > Tomas Vondra > > I fixed style related issues and followed your suggestions on splitting the patch to do global stats first then the per-backend stats of cache size and entries count reporting, attached is v3-0001 that does just the global stats counting. When we make sure this is correct i'll proceed with the per-backend stats patch. Regards, Ayoub
From 91cf8d91a2e57ae6dde5291660439ca4ce0a6bc5 Mon Sep 17 00:00:00 2001 From: AyoubKAZ <[email protected]> Date: Fri, 3 Apr 2026 12:49:42 +0200 Subject: [PATCH] Add pg_stat_vfdcache view for VFD cache statistics PostgreSQL's virtual file descriptor (VFD) layer maintains a per-backend cache of open file descriptors bounded by max_files_per_process (default 1000). When the cache is full, the least-recently-used entry is evicted (its OS fd closed) so a new file can be opened. A subsequent access to an evicted file must call open() again. A trivial example is with partitioned tables: a table with 1500 partitions requires up to many file descriptors per full scan (main fork, vm ...), which is more than the default limit, causing potential evictions and reopens. This commit adds: pg_stat_vfdcache -- a single-row view exposing cluster-wide VFD cache statistics: hits number of VFD cache hits misses number of VFD cache misses max_open_fds maximum number of file descriptors available to each backend process hit_ratio hits / (hits + misses) stats_reset timestamp of last counter reset pg_stat_reset_vfdcache() -- resets shared VFD counters The implementation follows the same cumulative shared statistics infrastructure like pgstat_bgwriter and others do. Event counting remains cheap in backend-local pending storage and is flushed into shared fixed stats which requires locking. Hit and miss counters are placed in FileAccess(), which is the single gate through which all VFD-mediated file reads, writes, truncations, and size checks pass. --- doc/src/sgml/monitoring.sgml | 107 ++++++++++++++++++ src/backend/catalog/system_views.sql | 16 +++ src/backend/storage/file/fd.c | 5 +- src/backend/utils/activity/Makefile | 1 + src/backend/utils/activity/meson.build | 1 + src/backend/utils/activity/pgstat.c | 17 +++ src/backend/utils/activity/pgstat_vfdcache.c | 113 +++++++++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 47 +++++++- src/include/catalog/pg_proc.dat | 30 +++++ src/include/pgstat.h | 26 +++++ src/include/utils/pgstat_internal.h | 20 ++++ src/include/utils/pgstat_kind.h | 3 +- src/test/regress/expected/rules.out | 8 ++ src/test/regress/expected/stats.out | 16 ++- src/test/regress/sql/stats.sql | 5 + 15 files changed, 411 insertions(+), 4 deletions(-) create mode 100644 src/backend/utils/activity/pgstat_vfdcache.c diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index bb75ed1069b..cab7d4457e6 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -543,6 +543,15 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser </entry> </row> + <row> + <entry><structname>pg_stat_vfdcache</structname><indexterm><primary>pg_stat_vfdcache</primary></indexterm></entry> + <entry>One row only, showing cluster-wide statistics about virtual file + descriptor (VFD) cache activity. See + <link linkend="monitoring-pg-stat-vfdcache-view"> + <structname>pg_stat_vfdcache</structname></link> for details. + </entry> + </row> + <row> <entry><structname>pg_stat_wal</structname><indexterm><primary>pg_stat_wal</primary></indexterm></entry> <entry>One row only, showing statistics about WAL activity. See @@ -3603,6 +3612,89 @@ description | Waiting for a newly initialized WAL file to reach durable storage </para> </sect2> + <sect2 id="monitoring-pg-stat-vfdcache-view"> + <title><structname>pg_stat_vfdcache</structname></title> + + <indexterm zone="monitoring-pg-stat-vfdcache-view"> + <primary>pg_stat_vfdcache</primary> + </indexterm> + + <para> + The <structname>pg_stat_vfdcache</structname> view will always have a + single row, containing data about cluster-wide VFD (Virtual File + Descriptor) cache activity. + </para> + + <table id="pg-stat-vfdcache-view" xreflabel="pg_stat_vfdcache"> + <title><structname>pg_stat_vfdcache</structname> View</title> + <tgroup cols="1"> + <thead> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + Column Type + </para> + <para> + Description + </para></entry> + </row> + </thead> + + <tbody> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>hits</structfield> <type>bigint</type> + </para> + <para> + Number of file accesses where the physical file descriptor was + already open in the cache, requiring no system call + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>misses</structfield> <type>bigint</type> + </para> + <para> + Number of file accesses where the physical file descriptor had + been evicted from the cache, requiring <function>open()</function> + to be called again before the access could proceed + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>max_open_fds</structfield> <type>integer</type> + </para> + <para> + Maximum number of file descriptors available to each backend process + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>hit_ratio</structfield> <type>float8</type> + </para> + <para> + Fraction of file accesses that were cache hits: + <literal>hits / (hits + misses)</literal> + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>stats_reset</structfield> <type>timestamp with time zone</type> + </para> + <para> + Time at which the counters were last reset by + <function>pg_stat_reset_vfdcache()</function> + </para></entry> + </row> + </tbody> + </tgroup> + </table> + + </sect2> + <sect2 id="monitoring-pg-stat-wal-view"> <title><structname>pg_stat_wal</structname></title> @@ -5640,6 +5732,21 @@ description | Waiting for a newly initialized WAL file to reach durable storage can be granted EXECUTE to run the function. </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>pg_stat_reset_vfdcache</primary> + </indexterm> + <function>pg_stat_reset_vfdcache</function> () + <returnvalue>void</returnvalue> + </para> + <para> + Reset shared VFD cache statistics counters to zero. The reset + timestamp is recorded in + <structname>pg_stat_vfdcache</structname>.<structfield>stats_reset</structfield>. + </para></entry> + </row> </tbody> </tgroup> </table> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index e54018004db..34839e60146 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1516,3 +1516,19 @@ CREATE VIEW pg_aios AS SELECT * FROM pg_get_aios(); REVOKE ALL ON pg_aios FROM PUBLIC; GRANT SELECT ON pg_aios TO pg_read_all_stats; + +CREATE VIEW pg_stat_vfdcache AS + SELECT + pg_stat_get_vfd_hits() AS hits, + pg_stat_get_vfd_misses() AS misses, + pg_stat_get_vfd_max_open_fds() AS max_open_fds, + CASE + WHEN pg_stat_get_vfd_hits() + pg_stat_get_vfd_misses() = 0 + THEN NULL::float8 + ELSE pg_stat_get_vfd_hits()::float8 + / (pg_stat_get_vfd_hits() + pg_stat_get_vfd_misses()) + END AS hit_ratio, + pg_stat_get_vfd_stat_reset_time() AS stats_reset; + +REVOKE ALL ON pg_stat_vfdcache FROM PUBLIC; +GRANT SELECT ON pg_stat_vfdcache TO PUBLIC; diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index 01f1bd6e687..8427e98ff75 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -1480,6 +1480,7 @@ static int FileAccess(File file) { int returnValue; + bool is_open; DO_DB(elog(LOG, "FileAccess %d (%s)", file, VfdCache[file].fileName)); @@ -1488,8 +1489,10 @@ FileAccess(File file) * Is the file open? If not, open it and put it at the head of the LRU * ring (possibly closing the least recently used file to get an FD). */ + is_open = !FileIsNotOpen(file); + pgstat_count_vfd_access(is_open); - if (FileIsNotOpen(file)) + if (!is_open) { returnValue = LruInsert(file); if (returnValue != 0) diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile index ca3ef89bf59..fe8fc00d966 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -32,6 +32,7 @@ OBJS = \ pgstat_shmem.o \ pgstat_slru.o \ pgstat_subscription.o \ + pgstat_vfdcache.o \ pgstat_wal.o \ pgstat_xact.o \ wait_event.o \ diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build index 1aa7ece5290..5c1bcec7f8b 100644 --- a/src/backend/utils/activity/meson.build +++ b/src/backend/utils/activity/meson.build @@ -17,6 +17,7 @@ backend_sources += files( 'pgstat_shmem.c', 'pgstat_slru.c', 'pgstat_subscription.c', + 'pgstat_vfdcache.c', 'pgstat_wal.c', 'pgstat_xact.c', ) diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index eb8ccbaa628..95d366b6ce9 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -500,6 +500,23 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE] .reset_all_cb = pgstat_wal_reset_all_cb, .snapshot_cb = pgstat_wal_snapshot_cb, }, + + [PGSTAT_KIND_VFDCACHE] = { + .name = "vfdcache", + + .fixed_amount = true, + .write_to_file = true, + + .snapshot_ctl_off = offsetof(PgStat_Snapshot, vfdcache), + .shared_ctl_off = offsetof(PgStat_ShmemControl, vfdcache), + .shared_data_off = offsetof(PgStatShared_VfdCache, stats), + .shared_data_len = sizeof(((PgStatShared_VfdCache *) 0)->stats), + + .flush_static_cb = pgstat_vfdcache_flush_cb, + .init_shmem_cb = pgstat_vfdcache_init_shmem_cb, + .reset_all_cb = pgstat_vfdcache_reset_all_cb, + .snapshot_cb = pgstat_vfdcache_snapshot_cb, + }, }; /* diff --git a/src/backend/utils/activity/pgstat_vfdcache.c b/src/backend/utils/activity/pgstat_vfdcache.c new file mode 100644 index 00000000000..b036759031c --- /dev/null +++ b/src/backend/utils/activity/pgstat_vfdcache.c @@ -0,0 +1,113 @@ +/* ------------------------------------------------------------------------- + * + * pgstat_vfdcache.c + * Implementation of VFD cache statistics. + * + * VFD events are first counted in backend-local pending storage and then + * flushed into shared-memory cumulative stats, following the same model as + * other fixed stats kinds. + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/activity/pgstat_vfdcache.c + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "pgstat.h" +#include "utils/memutils.h" +#include "utils/pgstat_internal.h" + +/* + * Backend-local VFD counters waiting to be flushed. + */ +PgStat_VfdCacheStats PendingVfdCacheStats = {0}; + +/* + * Count a VFD cache access as either a hit or miss. + */ +void +pgstat_count_vfd_access(bool hit) +{ + if (hit) + PendingVfdCacheStats.vfd_hits++; + else + PendingVfdCacheStats.vfd_misses++; + pgstat_report_fixed = true; +} + +/* + * Flush out backend-local pending VFD cache stats. + */ +bool +pgstat_vfdcache_flush_cb(bool nowait) +{ + PgStatShared_VfdCache *stats_shmem = &pgStatLocal.shmem->vfdcache; + + if (pg_memory_is_all_zeros(&PendingVfdCacheStats, + sizeof(struct PgStat_VfdCacheStats))) + return false; + + if (!nowait) + LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); + else if (!LWLockConditionalAcquire(&stats_shmem->lock, LW_EXCLUSIVE)) + return true; + + stats_shmem->stats.vfd_hits += PendingVfdCacheStats.vfd_hits; + stats_shmem->stats.vfd_misses += PendingVfdCacheStats.vfd_misses; + + LWLockRelease(&stats_shmem->lock); + + MemSet(&PendingVfdCacheStats, 0, sizeof(PendingVfdCacheStats)); + + return false; +} + +/* + * Support function for SQL-callable pg_stat_get_vfd_* functions. + */ +PgStat_VfdCacheStats * +pgstat_fetch_stat_vfdcache(void) +{ + pgstat_snapshot_fixed(PGSTAT_KIND_VFDCACHE); + + return &pgStatLocal.snapshot.vfdcache; +} + +void +pgstat_vfdcache_init_shmem_cb(void *stats) +{ + PgStatShared_VfdCache *stats_shmem = (PgStatShared_VfdCache *) stats; + + LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA); +} + +void +pgstat_vfdcache_reset_all_cb(TimestampTz ts) +{ + PgStatShared_VfdCache *stats_shmem = &pgStatLocal.shmem->vfdcache; + + LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); + MemSet(&stats_shmem->stats, 0, sizeof(stats_shmem->stats)); + stats_shmem->stats.stat_reset_timestamp = ts; + LWLockRelease(&stats_shmem->lock); +} + +void +pgstat_vfdcache_snapshot_cb(void) +{ + PgStatShared_VfdCache *stats_shmem = &pgStatLocal.shmem->vfdcache; + + LWLockAcquire(&stats_shmem->lock, LW_SHARED); + memcpy(&pgStatLocal.snapshot.vfdcache, &stats_shmem->stats, + sizeof(pgStatLocal.snapshot.vfdcache)); + LWLockRelease(&stats_shmem->lock); +} + +void +pgstat_reset_vfdcache(void) +{ + pgstat_reset_of_kind(PGSTAT_KIND_VFDCACHE); +} diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 9185a8e6b83..3c9e78049ba 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -26,6 +26,7 @@ #include "pgstat.h" #include "postmaster/bgworker.h" #include "replication/logicallauncher.h" +#include "storage/fd.h" #include "storage/proc.h" #include "storage/procarray.h" #include "utils/acl.h" @@ -1340,6 +1341,47 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS) PG_RETURN_INT64(pgstat_fetch_stat_bgwriter()->buf_alloc); } +Datum +pg_stat_get_vfd_hits(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_stat_vfdcache()->vfd_hits); +} + +Datum +pg_stat_get_vfd_misses(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_stat_vfdcache()->vfd_misses); +} + +Datum +pg_stat_get_vfd_max_open_fds(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT32(max_safe_fds); +} + +Datum +pg_stat_get_vfd_stat_reset_time(PG_FUNCTION_ARGS) +{ + TimestampTz ts = pgstat_fetch_stat_vfdcache()->stat_reset_timestamp; + + if (ts == 0) + PG_RETURN_NULL(); + + PG_RETURN_TIMESTAMPTZ(ts); +} + +/* + * pg_stat_reset_vfdcache + * Reset shared VFD cache counters. + */ +Datum +pg_stat_reset_vfdcache(PG_FUNCTION_ARGS) +{ + pgstat_reset_vfdcache(); + PG_RETURN_VOID(); +} + + /* * When adding a new column to the pg_stat_io view and the * pg_stat_get_backend_io() function, add a new enum value here above @@ -1965,6 +2007,7 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS) XLogPrefetchResetStats(); pgstat_reset_of_kind(PGSTAT_KIND_SLRU); pgstat_reset_of_kind(PGSTAT_KIND_WAL); + pgstat_reset_of_kind(PGSTAT_KIND_VFDCACHE); PG_RETURN_VOID(); } @@ -1985,13 +2028,15 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS) XLogPrefetchResetStats(); else if (strcmp(target, "slru") == 0) pgstat_reset_of_kind(PGSTAT_KIND_SLRU); + else if (strcmp(target, "vfdcache") == 0) + pgstat_reset_of_kind(PGSTAT_KIND_VFDCACHE); else if (strcmp(target, "wal") == 0) pgstat_reset_of_kind(PGSTAT_KIND_WAL); else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized reset target: \"%s\"", target), - errhint("Target must be \"archiver\", \"bgwriter\", \"checkpointer\", \"io\", \"recovery_prefetch\", \"slru\", or \"wal\"."))); + errhint("Target must be \"archiver\", \"bgwriter\", \"checkpointer\", \"io\", \"recovery_prefetch\", \"slru\", \"vfdcache\", or \"wal\"."))); PG_RETURN_VOID(); } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index acf16254b21..16adfd5c731 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12859,5 +12859,35 @@ { oid => '8281', descr => 'hash', proname => 'hashoid8extended', prorettype => 'int8', proargtypes => 'oid8 int8', prosrc => 'hashoid8extended' }, +{ oid => '9560', + descr => 'statistics: number of VFD cache hits', + proname => 'pg_stat_get_vfd_hits', + provolatile => 'v', proparallel => 'r', + prorettype => 'int8', proargtypes => '', + prosrc => 'pg_stat_get_vfd_hits' }, +{ oid => '9561', + descr => 'statistics: number of VFD cache misses', + proname => 'pg_stat_get_vfd_misses', + provolatile => 'v', proparallel => 'r', + prorettype => 'int8', proargtypes => '', + prosrc => 'pg_stat_get_vfd_misses' }, +{ oid => '9564', + descr => 'statistics: timestamp of last VFD cache stats reset', + proname => 'pg_stat_get_vfd_stat_reset_time', + provolatile => 'v', proparallel => 'r', + prorettype => 'timestamptz', proargtypes => '', + prosrc => 'pg_stat_get_vfd_stat_reset_time' }, +{ oid => '9565', + descr => 'statistics: reset shared VFD cache counters', + proname => 'pg_stat_reset_vfdcache', + provolatile => 'v', proparallel => 'r', + prorettype => 'void', proargtypes => '', + prosrc => 'pg_stat_reset_vfdcache' }, +{ oid => '9566', + descr => 'statistics: max number of file descriptors available to backend', + proname => 'pg_stat_get_vfd_max_open_fds', + provolatile => 'v', proparallel => 'r', + prorettype => 'int4', proargtypes => '', + prosrc => 'pg_stat_get_vfd_max_open_fds' }, ] diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 8e3549c3752..ccb3ba28286 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -272,6 +272,19 @@ typedef struct PgStat_CheckpointerStats TimestampTz stat_reset_timestamp; } PgStat_CheckpointerStats; +/* --------- + * PgStat_VfdCacheStats Virtual File Descriptor cache statistics + * + * Tracks hit/miss events in the VFD cache (fd.c). These counters + * are accumulated in shared fixed stats and exposed by pg_stat_vfdcache. + * --------- + */ +typedef struct PgStat_VfdCacheStats +{ + PgStat_Counter vfd_hits; /* fd was open, no open() was needed */ + PgStat_Counter vfd_misses; /* fd was VFD_CLOSED, open() was required */ + TimestampTz stat_reset_timestamp; +} PgStat_VfdCacheStats; /* * Types related to counting IO operations @@ -611,6 +624,13 @@ extern PgStat_BgWriterStats *pgstat_fetch_stat_bgwriter(void); extern void pgstat_report_checkpointer(void); extern PgStat_CheckpointerStats *pgstat_fetch_stat_checkpointer(void); +/* + * Functions in pgstat_vfdcache.c + */ + +extern PgStat_VfdCacheStats * pgstat_fetch_stat_vfdcache(void); +extern void pgstat_reset_vfdcache(void); +extern void pgstat_count_vfd_access(bool hit); /* * Functions in pgstat_io.c @@ -851,6 +871,12 @@ extern PGDLLIMPORT int pgstat_fetch_consistency; /* updated directly by bgwriter and bufmgr */ extern PGDLLIMPORT PgStat_BgWriterStats PendingBgWriterStats; +/* + * Variables in pgstat_vfdcache.c + */ + +/* updated by VFD counting functions called from fd.c */ +extern PGDLLIMPORT PgStat_VfdCacheStats PendingVfdCacheStats; /* * Variables in pgstat_checkpointer.c diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index eed4c6b359c..41cd562fa74 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -488,6 +488,13 @@ typedef struct PgStatShared_Wal PgStat_WalStats stats; } PgStatShared_Wal; +typedef struct PgStatShared_VfdCache +{ + /* lock protects ->stats */ + LWLock lock; + PgStat_VfdCacheStats stats; +} PgStatShared_VfdCache; + /* ---------- @@ -583,6 +590,7 @@ typedef struct PgStat_ShmemControl PgStatShared_Lock lock; PgStatShared_SLRU slru; PgStatShared_Wal wal; + PgStatShared_VfdCache vfdcache; /* * Custom stats data with fixed-numbered objects, indexed by (PgStat_Kind @@ -619,6 +627,8 @@ typedef struct PgStat_Snapshot PgStat_WalStats wal; + PgStat_VfdCacheStats vfdcache; + /* * Data in snapshot for custom fixed-numbered statistics, indexed by * (PgStat_Kind - PGSTAT_KIND_CUSTOM_MIN). Each entry is allocated in @@ -732,6 +742,16 @@ extern void pgstat_checkpointer_reset_all_cb(TimestampTz ts); extern void pgstat_checkpointer_snapshot_cb(void); +/* + * Functions in pgstat_vfdcache.c + */ + +extern bool pgstat_vfdcache_flush_cb(bool nowait); +extern void pgstat_vfdcache_init_shmem_cb(void *stats); +extern void pgstat_vfdcache_reset_all_cb(TimestampTz ts); +extern void pgstat_vfdcache_snapshot_cb(void); + + /* * Functions in pgstat_database.c */ diff --git a/src/include/utils/pgstat_kind.h b/src/include/utils/pgstat_kind.h index 2d78a029683..319ec0bd63b 100644 --- a/src/include/utils/pgstat_kind.h +++ b/src/include/utils/pgstat_kind.h @@ -39,9 +39,10 @@ #define PGSTAT_KIND_LOCK 11 #define PGSTAT_KIND_SLRU 12 #define PGSTAT_KIND_WAL 13 +#define PGSTAT_KIND_VFDCACHE 14 #define PGSTAT_KIND_BUILTIN_MIN PGSTAT_KIND_DATABASE -#define PGSTAT_KIND_BUILTIN_MAX PGSTAT_KIND_WAL +#define PGSTAT_KIND_BUILTIN_MAX PGSTAT_KIND_VFDCACHE #define PGSTAT_KIND_BUILTIN_SIZE (PGSTAT_KIND_BUILTIN_MAX + 1) /* Custom stats kinds */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2b3cf6d8569..9c0e4708285 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2362,6 +2362,14 @@ pg_stat_user_tables| SELECT relid, stats_reset FROM pg_stat_all_tables WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text)); +pg_stat_vfdcache| SELECT pg_stat_get_vfd_hits() AS hits, + pg_stat_get_vfd_misses() AS misses, + pg_stat_get_vfd_max_open_fds() AS max_open_fds, + CASE + WHEN ((pg_stat_get_vfd_hits() + pg_stat_get_vfd_misses()) = 0) THEN NULL::double precision + ELSE ((pg_stat_get_vfd_hits())::double precision / ((pg_stat_get_vfd_hits() + pg_stat_get_vfd_misses()))::double precision) + END AS hit_ratio, + pg_stat_get_vfd_stat_reset_time() AS stats_reset; pg_stat_wal| SELECT wal_records, wal_fpi, wal_bytes, diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index ea7f7846895..2c168a40ed9 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -1127,10 +1127,24 @@ SELECT stats_reset > :'wal_reset_ts'::timestamptz FROM pg_stat_wal; t (1 row) +-- Test that reset_shared with vfdcache specified as the stats type works +SELECT stats_reset AS vfdcache_reset_ts FROM pg_stat_vfdcache \gset +SELECT pg_stat_reset_shared('vfdcache'); + pg_stat_reset_shared +---------------------- + +(1 row) + +SELECT stats_reset > :'vfdcache_reset_ts'::timestamptz FROM pg_stat_vfdcache; + ?column? +---------- + t +(1 row) + -- Test error case for reset_shared with unknown stats type SELECT pg_stat_reset_shared('unknown'); ERROR: unrecognized reset target: "unknown" -HINT: Target must be "archiver", "bgwriter", "checkpointer", "io", "recovery_prefetch", "slru", or "wal". +HINT: Target must be "archiver", "bgwriter", "checkpointer", "io", "recovery_prefetch", "slru", "vfdcache", or "wal". -- Test that reset works for pg_stat_database and pg_stat_database_conflicts -- Since pg_stat_database stats_reset starts out as NULL, reset it once first so that we -- have a baseline for comparison. The same for pg_stat_database_conflicts as it shares diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index 65d8968c83e..0b486e0f884 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -520,6 +520,11 @@ SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset SELECT pg_stat_reset_shared('wal'); SELECT stats_reset > :'wal_reset_ts'::timestamptz FROM pg_stat_wal; +-- Test that reset_shared with vfdcache specified as the stats type works +SELECT stats_reset AS vfdcache_reset_ts FROM pg_stat_vfdcache \gset +SELECT pg_stat_reset_shared('vfdcache'); +SELECT stats_reset > :'vfdcache_reset_ts'::timestamptz FROM pg_stat_vfdcache; + -- Test error case for reset_shared with unknown stats type SELECT pg_stat_reset_shared('unknown'); -- 2.34.1
