Hello,
On Thu, Apr 23, 2026, 9:34 AM David Geier <[email protected]> wrote:
> Hi!
>
> I finally got around taking a look at this patch.
>
> On 03.04.2026 15:53, KAZAR Ayoub wrote:
> >> - 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.
>
> An alternative to including it in the view would be using a GUC of type
> PGC_INTERNAL. That seems more inline with how we expose other PostgreSQL
> internal read-only variables that don't change.
>
> Or is there an advantage to including max_safe_fds in the view?
>
There's no significant advantage other than seeing all info related to
vfdcache together in one view
Also in fd.h i just remembered:
/*
* This is private to fd.c, but exported for
save/restore_backend_variables()
*/
extern PGDLLIMPORT int max_safe_fds;
I don't think it's intended to be exported for reads like i'm doing (which
can be fine?), or maybe update this comment about its export.
>
> >> 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 number of used entries already exists, see nfile in fd.c.
>
Would one want the number of all entries (i.e SizeVfdCache see fd.c) or the
number of used entries (i.e entries with fds in use, which is nfile) ? I
thought of the first, that's what 0002 patch contains for the moment.
>
> Including the total cache size would also be virtually free if we don't
> iterate over all VFDs each time, but update the size as we go. That
> would have to happen when resizing the cache and when populating /
> freeing a cache entry because extra memory is allocated / freed for
> Vfd::fileName.
>
Is it a big deal if we miss some bytes of filename globally ?
>
> I'm happy to code this up if there's agreement that it's sensible to
> include it, in the current version of the patch or a follow-up patch.
>
> Beyond that:
>
> While looking through the code I saw a mistake (repetition of "that") in
> a comment in existing code. Maybe you want to fix that as well right away?
Noted.
>
> /*
> * For variable-numbered stats: flush pending stats. Required if
> pending
> * data is used. See flush_static_cb when dealing with stats data
> that
> * that cannot use PgStat_EntryRef->pending.
> */
> bool (*flush_pending_cb) (PgStat_EntryRef *sr, bool
> nowait);
>
> The indentation of the type at the end of the following two structs is
> inconsistent with the rest of the files.
>
Fixed.
That's pg_indent doing me dirty, although i know it's wrong i didn't
understand why it kept indenting like this, only in those two structs.
>
> typedef struct PgStatShared_VfdCache
> {
> /* lock protects ->stats */
> LWLock lock;
> PgStat_VfdCacheStats stats;
> } PgStatShared_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;
>
> Apart from these nit comments the patch looks good to me.
>
Thanks for the review!
Other than the above small changes, i'll be moving forward with 0002 which
is also ready.
Regards,
Ayoub
From a4de812dbd99474ecb18654331e6516fff740452 Mon Sep 17 00:00:00 2001
From: AyoubKAZ <[email protected]>
Date: Wed, 29 Apr 2026 14:39:18 +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 | 22 +++-
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, 412 insertions(+), 5 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 08d5b824552..fd484de0d47 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
@@ -3613,6 +3622,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>
@@ -5824,6 +5916,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 73a1c1c4670..e79de913538 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1556,3 +1556,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 a8be066afe0..6393407864c 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 b67da88c7dc..a64266c1556 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 1408de387ea..f35513d7081 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"
@@ -1336,6 +1337,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
@@ -1961,6 +2003,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();
}
@@ -1981,13 +2024,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 fa9ae79082b..43132551849 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12768,5 +12768,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 dfa2e837638..f2223a538fb 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
@@ -607,6 +620,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
@@ -848,6 +868,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 fe463faaf63..5ede0ee255e 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -296,7 +296,7 @@ typedef struct PgStat_KindInfo
/*
* For variable-numbered stats: flush pending stats. Required if pending
* data is used. See flush_static_cb when dealing with stats data that
- * that cannot use PgStat_EntryRef->pending.
+ * cannot use PgStat_EntryRef->pending.
*/
bool (*flush_pending_cb) (PgStat_EntryRef *sr, bool nowait);
@@ -485,6 +485,13 @@ typedef struct PgStatShared_Wal
PgStat_WalStats stats;
} PgStatShared_Wal;
+typedef struct PgStatShared_VfdCache
+{
+ /* lock protects ->stats */
+ LWLock lock;
+ PgStat_VfdCacheStats stats;
+} PgStatShared_VfdCache;
+
/* ----------
@@ -580,6 +587,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
@@ -616,6 +624,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
@@ -730,6 +740,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 a65a5bf0c4f..034b85a29f2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2415,6 +2415,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 e5dcb85abd8..04daadca909 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -1161,10 +1161,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 610fd21fae4..c9096166cc8 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -528,6 +528,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
From 16e620af6e1e7669c19efbc240958b25fb2b35db Mon Sep 17 00:00:00 2001
From: AyoubKAZ <[email protected]>
Date: Wed, 29 Apr 2026 15:02:59 +0200
Subject: [PATCH 2/2] Add VFD cache footprint metrics to pg_stat_vfdcache
Extend pg_stat_vfdcache with two additional columns:
cache_entries total allocated VFD entries across active backends
cache_bytes total memory used by those entries, in bytes
Unlike hits and misses (cumulative counters), these are live gauges.
Each backend publishes its current VFD cache footprint into backend shared
stats during backend stats flush, and SQL accessors sum those backend shared
stats entries to produce cluster-wide totals.
---
doc/src/sgml/monitoring.sgml | 20 ++++++
src/backend/catalog/system_views.sql | 2 +
src/backend/storage/file/fd.c | 22 +++++++
src/backend/utils/activity/pgstat_backend.c | 48 ++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 71 +++++++++++++++++++++
src/include/catalog/pg_proc.dat | 12 ++++
src/include/pgstat.h | 11 ++++
src/include/storage/fd.h | 2 +
src/include/utils/pgstat_internal.h | 5 +-
src/test/regress/expected/rules.out | 2 +
10 files changed, 194 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fd484de0d47..2c9881982a9 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3671,6 +3671,26 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>cache_entries</structfield> <type>integer</type>
+ </para>
+ <para>
+ Sum of currently allocated VFD cache entries across all active
+ backends
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>cache_bytes</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Sum of memory used by allocated VFD cache entries across all active
+ backends, in bytes
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>max_open_fds</structfield> <type>integer</type>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e79de913538..3e123736d6d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1561,6 +1561,8 @@ 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_cache_entries() AS cache_entries,
+ pg_stat_get_vfd_cache_bytes() AS cache_bytes,
pg_stat_get_vfd_max_open_fds() AS max_open_fds,
CASE
WHEN pg_stat_get_vfd_hits() + pg_stat_get_vfd_misses() = 0
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 6393407864c..29fe2c13c07 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -1512,6 +1512,28 @@ FileAccess(File file)
return 0;
}
+/*
+ * Return the number of currently allocated VFD entries for this backend,
+ * excluding slot 0 which is used as freelist/LRU header.
+ */
+uint64
+GetVfdCacheEntries(void)
+{
+ if (SizeVfdCache == 0)
+ return 0;
+
+ return (uint64) (SizeVfdCache - 1);
+}
+
+/*
+ * Return the memory footprint of currently allocated VFD entries.
+ */
+uint64
+GetVfdCacheBytes(void)
+{
+ return GetVfdCacheEntries() * sizeof(Vfd);
+}
+
/*
* Called whenever a temporary file is deleted to report its size.
*/
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 73461c9bca5..2345cbbcc49 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -27,6 +27,7 @@
#include "access/xlog.h"
#include "executor/instrument.h"
#include "storage/bufmgr.h"
+#include "storage/fd.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/memutils.h"
@@ -48,6 +49,11 @@ static bool backend_has_iostats = false;
*/
static WalUsage prevBackendWalUsage;
+/*
+ * Last per-backend VFD cache gauges published to backend stats.
+ */
+static PgStat_BackendVfdCacheStats prevBackendVfdCacheStats;
+
/*
* Utility routines to report I/O stats for backends, kept here to avoid
* exposing PendingBackendStats to the outside world.
@@ -219,6 +225,19 @@ pgstat_backend_wal_have_pending(void)
return (pgWalUsage.wal_records != prevBackendWalUsage.wal_records);
}
+/*
+ * To determine whether VFD cache gauges changed.
+ */
+static inline bool
+pgstat_backend_vfdcache_have_pending(void)
+{
+ PgStat_Counter current_entries = (PgStat_Counter) GetVfdCacheEntries();
+ PgStat_Counter current_cache_bytes = (PgStat_Counter) GetVfdCacheBytes();
+
+ return current_entries != prevBackendVfdCacheStats.vfd_entries ||
+ current_cache_bytes != prevBackendVfdCacheStats.vfd_cache_bytes;
+}
+
/*
* Flush out locally pending backend WAL statistics. Locking is managed
* by the caller.
@@ -262,6 +281,26 @@ pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
prevBackendWalUsage = pgWalUsage;
}
+/*
+ * Flush out locally pending backend VFD cache gauges. Locking is managed
+ * by the caller.
+ * No need to check whether there is pending data here because caller will have done that before acquiring the lock.
+ */
+static void
+pgstat_flush_backend_entry_vfdcache(PgStat_EntryRef *entry_ref)
+{
+ PgStatShared_Backend *shbackendent;
+ PgStat_BackendVfdCacheStats *bktype_shstats;
+
+ shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
+ bktype_shstats = &shbackendent->stats.vfdcache_stats;
+
+ bktype_shstats->vfd_entries = (PgStat_Counter) GetVfdCacheEntries();
+ bktype_shstats->vfd_cache_bytes = (PgStat_Counter) GetVfdCacheBytes();
+
+ prevBackendVfdCacheStats = *bktype_shstats;
+}
+
/*
* Flush out locally pending backend statistics
*
@@ -286,6 +325,11 @@ pgstat_flush_backend(bool nowait, uint32 flags)
pgstat_backend_wal_have_pending())
has_pending_data = true;
+ /* Some VFD cache data pending? */
+ if ((flags & PGSTAT_BACKEND_FLUSH_VFDCACHE) &&
+ pgstat_backend_vfdcache_have_pending())
+ has_pending_data = true;
+
if (!has_pending_data)
return false;
@@ -301,6 +345,9 @@ pgstat_flush_backend(bool nowait, uint32 flags)
if (flags & PGSTAT_BACKEND_FLUSH_WAL)
pgstat_flush_backend_entry_wal(entry_ref);
+ if (flags & PGSTAT_BACKEND_FLUSH_VFDCACHE)
+ pgstat_flush_backend_entry_vfdcache(entry_ref);
+
pgstat_unlock_entry(entry_ref);
return false;
@@ -339,6 +386,7 @@ pgstat_create_backend(ProcNumber procnum)
MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
backend_has_iostats = false;
+ MemSet(&prevBackendVfdCacheStats, 0, sizeof(prevBackendVfdCacheStats));
/*
* Initialize prevBackendWalUsage with pgWalUsage so that
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index f35513d7081..ed19b9d137e 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "access/xact.h"
#include "access/htup_details.h"
#include "access/xlog.h"
#include "access/xlogprefetcher.h"
@@ -1349,6 +1350,76 @@ pg_stat_get_vfd_misses(PG_FUNCTION_ARGS)
PG_RETURN_INT64(pgstat_fetch_stat_vfdcache()->vfd_misses);
}
+/*
+ * Sum per-backend VFD gauges across currently active backends.
+ */
+static void
+pgstat_get_vfd_backend_sums(PgStat_Counter *entries_sum,
+ PgStat_Counter *bytes_sum)
+{
+ static TimestampTz cached_stmt_start_ts = 0;
+ static PgStat_Counter cached_entries_sum = 0;
+ static PgStat_Counter cached_bytes_sum = 0;
+ TimestampTz stmt_start_ts = GetCurrentStatementStartTimestamp();
+ int num_backends = pgstat_fetch_stat_numbackends();
+
+ if (cached_stmt_start_ts == stmt_start_ts)
+ {
+ *entries_sum = cached_entries_sum;
+ *bytes_sum = cached_bytes_sum;
+ return;
+ }
+
+ *entries_sum = 0;
+ *bytes_sum = 0;
+
+ for (int curr_backend = 1; curr_backend <= num_backends; curr_backend++)
+ {
+ LocalPgBackendStatus *local_beentry;
+ PgBackendStatus *beentry;
+ PgStat_Backend *backend_stats;
+
+ local_beentry = pgstat_get_local_beentry_by_index(curr_backend);
+ beentry = &local_beentry->backendStatus;
+
+ if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
+ continue;
+
+ backend_stats = pgstat_fetch_stat_backend(local_beentry->proc_number);
+ if (!backend_stats)
+ continue;
+
+ *entries_sum += backend_stats->vfdcache_stats.vfd_entries;
+ *bytes_sum += backend_stats->vfdcache_stats.vfd_cache_bytes;
+ }
+
+ cached_entries_sum = *entries_sum;
+ cached_bytes_sum = *bytes_sum;
+ cached_stmt_start_ts = stmt_start_ts;
+}
+
+Datum
+pg_stat_get_vfd_cache_entries(PG_FUNCTION_ARGS)
+{
+ PgStat_Counter entries_sum;
+ PgStat_Counter bytes_sum;
+
+ pgstat_get_vfd_backend_sums(&entries_sum, &bytes_sum);
+
+ PG_RETURN_INT32((int32) entries_sum);
+}
+
+Datum
+pg_stat_get_vfd_cache_bytes(PG_FUNCTION_ARGS)
+{
+ PgStat_Counter entries_sum;
+ PgStat_Counter bytes_sum;
+
+ pgstat_get_vfd_backend_sums(&entries_sum, &bytes_sum);
+
+ PG_RETURN_INT64(bytes_sum);
+}
+
Datum
pg_stat_get_vfd_max_open_fds(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 43132551849..b1d0c55e450 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12780,6 +12780,18 @@
provolatile => 'v', proparallel => 'r',
prorettype => 'int8', proargtypes => '',
prosrc => 'pg_stat_get_vfd_misses' },
+{ oid => '9562',
+ descr => 'statistics: total number of allocated VFD cache entries',
+ proname => 'pg_stat_get_vfd_cache_entries',
+ provolatile => 'v', proparallel => 'r',
+ prorettype => 'int4', proargtypes => '',
+ prosrc => 'pg_stat_get_vfd_cache_entries' },
+{ oid => '9563',
+ descr => 'statistics: total memory footprint of VFD cache entries in bytes',
+ proname => 'pg_stat_get_vfd_cache_bytes',
+ provolatile => 'v', proparallel => 'r',
+ prorettype => 'int8', proargtypes => '',
+ prosrc => 'pg_stat_get_vfd_cache_bytes' },
{ oid => '9564',
descr => 'statistics: timestamp of last VFD cache stats reset',
proname => 'pg_stat_get_vfd_stat_reset_time',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f2223a538fb..c9a2f00a4b7 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -286,6 +286,16 @@ typedef struct PgStat_VfdCacheStats
TimestampTz stat_reset_timestamp;
} PgStat_VfdCacheStats;
+/* -------
+ * PgStat_BackendVfdCacheStats Per-backend VFD cache gauges
+ * -------
+ */
+typedef struct PgStat_BackendVfdCacheStats
+{
+ PgStat_Counter vfd_entries;
+ PgStat_Counter vfd_cache_bytes;
+} PgStat_BackendVfdCacheStats;
+
/*
* Types related to counting IO operations
*/
@@ -536,6 +546,7 @@ typedef struct PgStat_Backend
TimestampTz stat_reset_timestamp;
PgStat_BktypeIO io_stats;
PgStat_WalCounters wal_counters;
+ PgStat_BackendVfdCacheStats vfdcache_stats;
} PgStat_Backend;
/* ---------
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index 8ac466fd346..17da82c915b 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -149,6 +149,8 @@ extern char *FilePathName(File file);
extern int FileGetRawDesc(File file);
extern int FileGetRawFlags(File file);
extern mode_t FileGetRawMode(File file);
+extern uint64 GetVfdCacheEntries(void);
+extern uint64 GetVfdCacheBytes(void);
/* Operations used for sharing named temporary files */
extern File PathNameCreateTemporaryFile(const char *path, bool error_on_failure);
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 5ede0ee255e..5f830c107fc 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -715,7 +715,10 @@ extern void pgstat_archiver_snapshot_cb(void);
/* flags for pgstat_flush_backend() */
#define PGSTAT_BACKEND_FLUSH_IO (1 << 0) /* Flush I/O statistics */
#define PGSTAT_BACKEND_FLUSH_WAL (1 << 1) /* Flush WAL statistics */
-#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL)
+#define PGSTAT_BACKEND_FLUSH_VFDCACHE (1 << 2) /* Flush VFD cache gauges */
+#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | \
+ PGSTAT_BACKEND_FLUSH_WAL | \
+ PGSTAT_BACKEND_FLUSH_VFDCACHE)
extern bool pgstat_flush_backend(bool nowait, uint32 flags);
extern bool pgstat_backend_flush_cb(bool nowait);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 034b85a29f2..f424eb612e5 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2417,6 +2417,8 @@ pg_stat_user_tables| SELECT relid,
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_cache_entries() AS cache_entries,
+ pg_stat_get_vfd_cache_bytes() AS cache_bytes,
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
--
2.34.1