diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 2d0ebd6f27d..d60b6710bfd 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3050,7 +3050,9 @@ description | Waiting for a newly initialized WAL file to reach durable storage
object is not wal,
or if is enabled
and object is wal,
- otherwise zero)
+ otherwise zero). This may include time spent waiting for a
+ read started by another backend. In that case
+ reads may still be zero.
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 3908529872a..169829eb020 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -1829,8 +1829,9 @@ WaitReadBuffers(ReadBuffersOperation *operation)
needed_wait = true;
/*
- * The IO operation itself was already counted earlier, in
- * AsyncReadBuffers(), this just accounts for the wait time.
+ * This just accounts for the wait time. The IO operation
+ * itself was already counted earlier in AsyncReadBuffers() --
+ * either by us or by another backend if this is a foreign IO.
*/
pgstat_count_io_op_time(io_object, io_context, IOOP_READ,
io_start, 0, 0);
@@ -2018,8 +2019,7 @@ AsyncReadBuffers(ReadBuffersOperation *operation, int *nblocks_progress)
* A secondary benefit is that this would allow us to measure the time in
* pgaio_io_acquire() without causing undue timer overhead in the common,
* non-blocking, case. However, currently the pgstats infrastructure
- * doesn't really allow that, as it a) asserts that an operation can't
- * have time without operations b) doesn't have an API to report
+ * doesn't really allow that because it doesn't have an API to report
* "accumulated" time.
*/
ioh = pgaio_io_acquire_nb(CurrentResourceOwner, &operation->io_return);
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index 4f7a39aaa0e..8ec1aad5078 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -25,9 +25,10 @@ static bool have_iostats = false;
/*
* Check that stats have not been counted for any combination of IOObject,
- * IOContext, and IOOp which are not tracked for the passed-in BackendType. If
- * stats are tracked for this combination and IO times are non-zero, counts
- * should be non-zero.
+ * IOContext, and IOOp which are not tracked for the passed-in BackendType.
+ * Non-zero time with a zero operation count is allowed as there are cases
+ * where this may be appropriate -- like when a backend is waiting on IO
+ * initiated by another backend.
*
* The passed-in PgStat_BktypeIO must contain stats from the BackendType
* specified by the second parameter. Caller is responsible for locking the
@@ -43,19 +44,10 @@ pgstat_bktype_io_stats_valid(PgStat_BktypeIO *backend_io,
{
for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
{
- /* we do track it */
- if (pgstat_tracks_io_op(bktype, io_object, io_context, io_op))
- {
- /* ensure that if IO times are non-zero, counts are > 0 */
- if (backend_io->times[io_object][io_context][io_op] != 0 &&
- backend_io->counts[io_object][io_context][io_op] <= 0)
- return false;
-
- continue;
- }
-
/* we don't track it, and it is not 0 */
- if (backend_io->counts[io_object][io_context][io_op] != 0)
+ if (!pgstat_tracks_io_op(bktype, io_object, io_context, io_op) &&
+ (backend_io->counts[io_object][io_context][io_op] != 0 ||
+ backend_io->times[io_object][io_context][io_op] != 0))
return false;
}
}