Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wh3a6-0001RQ-2l for pgsql-hackers@arkaria.postgresql.org; Tue, 07 Jul 2026 11:03:15 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wh3Z4-000QiK-2m for pgsql-hackers@arkaria.postgresql.org; Tue, 07 Jul 2026 11:02:11 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wh3Z4-000QiC-1I for pgsql-hackers@lists.postgresql.org; Tue, 07 Jul 2026 11:02:11 +0000 Received: from mail-wr1-x433.google.com ([2a00:1450:4864:20::433]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wh3Z2-000000001Uk-0QmN for pgsql-hackers@lists.postgresql.org; Tue, 07 Jul 2026 11:02:10 +0000 Received: by mail-wr1-x433.google.com with SMTP id ffacd0b85a97d-476a130c138so4198610f8f.0 for ; Tue, 07 Jul 2026 04:02:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783422126; x=1784026926; darn=lists.postgresql.org; h=content-disposition:content-type:mime-version:message-id:subject:to :from:date:from:to:cc:subject:date:message-id:reply-to:content-type; bh=KVN2idzARTgVXmWBN5RozaOJpKBPrOzCbvkDuVKvqcQ=; b=hYmSs1rj8dB4/wVbvXgUUHm9HIeYJJNhlLHXc/26rHV/3/EoXv9Rk104xd97ZdIlAe V0y4WsJyP0/2+cmicjZMMl8FzEnfYbBB9rAQiv51c6R/sU8OU9m84QIVZCkt/EerDikq NHP+i0r+Gg/zd/QqUYDqzxPwPafxDt2bOemWprRH3HpKmz0DEKyPHZhfcv/Gh8OC6SV1 PZL0jdj6by1kMmLvc7xoy5cUGI8TPYNJ3lIOqJZZF1k6lSYFTxXBPJIWROafrBfvHlT9 1pyObh0EO3pD8KDcJrodgkhZoLtzMbH97nKQsPijAnmsg0Guz/P27dgGJ3niUABFkCZr iaCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783422126; x=1784026926; h=content-disposition:content-type:mime-version:message-id:subject:to :from:date:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=KVN2idzARTgVXmWBN5RozaOJpKBPrOzCbvkDuVKvqcQ=; b=QPvxF/BVKSy2FalYJTZG4PHG0n7Ptk1pPcYrUDacfGUinUIrefW0VMimkc13fN2sC4 kpO94JYeG5hyiXdFYSWI62rheFD208YQ1vfjLIsI9btyPanoGi7cV/pIM/slIE6G3Pit kjqhc5lY9UwtQahQJgBZqQS2W6avV9SmqiTJLA3JbZnnUV3jUlWqwxp1IoVF/sQ/xcuo iZCs3sP0Fs+uSOqa5xnZVRKujJyXb72d+iSVVZWa6nkaLEqGHhxGtaG8nM/JjuvURZSX FTMbZmtGW1TWNtHDFoZzpkaMam4czwrQxNWRmXaIX5vtYhl0EedaUELQ4WJEyaLQVSgR pLrg== X-Gm-Message-State: AOJu0Yzt/X3jhUGtCU+cv7cqsvpFGPRafXbJztTXg4Vm1y+noYBU2E59 0sV4ur/RwXGzByFhTUtVYATsUtKDbttdzhmJJ12AX9UKDYiN2a9XBgrpd1FETg== X-Gm-Gg: AfdE7cmtwRioociJFRX67eypvHxfXgzYI0FTH4DRCosQa7J8T2VB+N+KigIAB0xdWTa mMt5TtgxhwX7QowRhVYJHDY7P4aZ3EZtNUBVYFR1VK280BM3cm4ejUPyfSPVuIBxFV3Vfle/2vo e0DNWQIM4yYy23EBEC/B3NgBtRPjFBURO6ZGfbkgEUEMB+DF6CX72BhzwYCsof9ovM0tKTSF1nu rg2so9sXvP6ftDG5e+X73mTsa6bXMXfFL06O93N4AlcKzus44Qrq74TnGHy+dsj0Kzky6bRvLFf pO5Ul62vd4m4zeK5+8IHh8bq+XV/1FJzl84vtxFGdMg39nl7zvvWrC+qgUWucHI0lrKWl5yj1YO 3P1MBzQR01bvW41mEIWG4TvqcugdA+jVVssoMDLVC8mdJ7FBGqAYUoJHYc3JVkrW206wUu1TL+M kwH+5IrATS+aq0pKMi0y0Tr5uxRE4KhNjQk5l9iRvJ5dOj0UGQSYzgm/3wkzyFCAU1oUdnRv8L X-Received: by 2002:a05:6000:1f10:b0:475:a05c:c63e with SMTP id ffacd0b85a97d-47de66d3203mr3853246f8f.31.1783422125574; Tue, 07 Jul 2026 04:02:05 -0700 (PDT) Received: from bdtpg (ec2-15-237-197-144.eu-west-3.compute.amazonaws.com. [15.237.197.144]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47aa0f214d2sm32136659f8f.33.2026.07.07.04.02.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jul 2026 04:02:04 -0700 (PDT) Date: Tue, 7 Jul 2026 11:02:03 +0000 From: Bertrand Drouvot To: pgsql-hackers@lists.postgresql.org Subject: Add per-backend AIO statistics Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Pi0/Nu3LVhi0Ij+H" Content-Disposition: inline List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Pi0/Nu3LVhi0Ij+H Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi hackers, Currently to monitor AIO we can use: 1/ pg_aios that lists all AIO handles that are currently in use. That shows what's happening right now, but not what has happened. 2/ pg_stat_get_backend_io() that shows how much IO was done, but not how it was done. There's no way to see whether IOs ran synchronously or asynchronously, whether a backend was stalling on handle exhaustion, or how completions are distributed across backends. This patch helps answering those questions by exposing cumulative per-backend AIO counters: - started: total AIO operations initiated - executed_sync: IOs executed synchronously (fallback path) - executed_async: IOs submitted asynchronously - completed_self: IO completions processed by the issuing backend - completed_other: IO completions processed on behalf of another backend - handle_waits: times waited for a free AIO handle - submitted: number of submit calls to the IO method These counters are useful for understanding and tuning AIO behavior: - executed_async / started. A ratio near zero means the backend is falling back to synchronous execution (TOAST chunk fetches, temp buffers, ...). - a non-zero handle_waits means the backend exhausted all its AIO handles. That could mean that io_max_concurrency is too low. - completed_self vs completed_other reveals cross-backend completion patterns. That helps see how IO completion work is distributed and could help interpret per backend IO statistics values. - executed_async / submitted gives the average batch size per submit call. As far as the technical implementation: This data can be retrieved with a new system function called pg_stat_get_backend_aio(), that returns one row based on the PID provided in input. pgstat_flush_backend() gains a new flag value, able to control the flush of the AIO stats. This patch relies mostly on the infrastructure provided by 9aea73fc61d4, that has introduced backend statistics. The overhead (4 functions calls and counters increments) kind of follow the same patterns as pgstat_count_backend_io_op() and I did not observe measurable regression (I did not expect to). Also that does not add that much memory per-backend: PgStat_AioCounters is 56 bytes. There is no "double" counting as a global view to show those counters does not exist. I think that's better to start with the per-backend side of it and see if we want to also add a global view. For example, completed_other identifies which backends did IOs for other backends. Also this allows correlating with pg_stat_activity and pg_stat_get_backend_io(). Examples based on Franck's blog post [1]: 1/ query the smalldocs table: postgres=# select count(*),avg(length(data)) from smalldocs; count | avg ---------+----------------------- 1024000 | 1024.0000000000000000 (1 row) postgres=# SELECT * FROM pg_stat_get_backend_aio(pg_backend_pid()); started | executed_sync | executed_async | completed_self | completed_other | handle_waits | submitted | stats_reset ---------+---------------+----------------+----------------+-----------------+--------------+-----------+------------------------------- 3125 | 46 | 3079 | 46 | 0 | 0 | 3078 | 2026-07-07 09:28:27.412136+00 We can see that the sequential scan fully benefits from AIO. 2/ query the largedocs table: postgres=# select count(*),avg(length(data)) from largedocs; count | avg -------+---------------------- 1000 | 1048576.000000000000 (1 row) postgres=# SELECT * FROM pg_stat_get_backend_aio(pg_backend_pid()); started | executed_sync | executed_async | completed_self | completed_other | handle_waits | submitted | stats_reset ---------+---------------+----------------+----------------+-----------------+--------------+-----------+------------------------------- 121154 | 121150 | 4 | 121150 | 0 | 0 | 4 | 2026-07-07 09:35:00.504872+00 We can see that the sequential scan bypasses AIO. Looking forward to your feedback. [1]: https://dev.to/franckpachot/iouring-buffered-reads-in-postgresql-19-iouring-mcn Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com --Pi0/Nu3LVhi0Ij+H Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-Add-per-backend-AIO-statistics.patch" From 6d3e3a04ade066e39e79777a6e4054aa57d81dd0 Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot Date: Thu, 11 Jun 2026 09:45:19 +0000 Subject: [PATCH v1] Add per-backend AIO statistics This commit adds per-backend AIO statistics, providing per-backend AIO behavior details. This data can be retrieved with a new system function called pg_stat_get_backend_aio(), that returns the following counters based on the PID provided in input: - started: total number of AIO operations initiated - executed_sync: IOs that were executed synchronously (fallback path) - executed_async: IOs that were submitted asynchronously to the IO method - completed_self: IO completions processed by the issuing backend itself - completed_other: IO completions processed on behalf of another backend - handle_waits: times the backend had to wait for a free AIO handle - submitted: number of submitted calls to the IO method These counters are useful for understanding and tuning AIO behavior: - executed_async / started. A ratio near zero means the backend is falling back to synchronous execution (TOAST chunk fetches, temp buffers, ...). - a non-zero handle_waits means the backend exhausted all its AIO handles. That could mean that io_max_concurrency is too low. - completed_self vs completed_other reveals cross-backend completion patterns. That helps see how IO completion work is distributed and could help interpret per backend IO statistics values. - executed_async / submitted gives the average batch size per submit call. This commit is straight-forward, relying on the infrastructure provided by 9aea73fc61d4 (backend-level pgstats). XXX: Bump catalog version. No need to touch PGSTAT_FILE_FORMAT_ID as backend statistics are never written to disk. Author: Bertrand Drouvot Reviewed-by: Discussion: --- doc/src/sgml/monitoring.sgml | 70 +++++++++++++ src/backend/storage/aio/aio.c | 13 +++ src/backend/utils/activity/pgstat_backend.c | 109 ++++++++++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 60 +++++++++++ src/include/catalog/pg_proc.dat | 7 ++ src/include/pgstat.h | 25 +++++ src/include/utils/pgstat_internal.h | 3 +- src/test/modules/test_aio/t/001_aio.pl | 32 ++++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 319 insertions(+), 1 deletion(-) 24.4% doc/src/sgml/ 3.2% src/backend/storage/aio/ 25.3% src/backend/utils/activity/ 20.5% src/backend/utils/adt/ 4.9% src/include/catalog/ 10.0% src/include/ 11.1% src/test/modules/test_aio/t/ diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 12b9ee20d4a..1c566cf0e0e 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -5641,6 +5641,76 @@ description | Waiting for a newly initialized WAL file to reach durable storage + + + + pg_stat_get_backend_aio + + pg_stat_get_backend_aio ( integer ) + record + + + Returns AIO (Asynchronous I/O) statistics about the + backend with the specified process ID. The returned values are: + + + + started: Total IOs initiated. + + + + + executed_sync: IOs executed synchronously. + + + + + executed_async: IOs submitted asynchronously. + + + + + completed_self: IO completions processed by this + backend. + + + + + completed_other: IO completions processed on + behalf of other backends. + + + + + handle_waits: Times waited for a free AIO handle. + + + + + submitted: Number of submit calls to the IO + method. Compare with executed_async to determine + average batch size. + + + + + stats_reset: Timestamp of last stats reset. + + + + + + The completed_other column is only meaningful + when io_method is set to io_uring; + with worker mode, IO completions are processed by + IO worker processes which do not track these statistics. + + + The function does not return AIO statistics for the checkpointer, + the background writer, the startup process and the autovacuum launcher. + + + diff --git a/src/backend/storage/aio/aio.c b/src/backend/storage/aio/aio.c index 8f7e26607b9..507f1727d41 100644 --- a/src/backend/storage/aio/aio.c +++ b/src/backend/storage/aio/aio.c @@ -40,6 +40,7 @@ #include "lib/ilist.h" #include "miscadmin.h" +#include "pgstat.h" #include "port/atomics.h" #include "storage/aio.h" #include "storage/aio_internal.h" @@ -458,6 +459,8 @@ pgaio_io_stage(PgAioHandle *ioh, PgAioOp op) "staged (synchronous: %d, in_batch: %d)", needs_synchronous, pgaio_my_backend->in_batchmode); + pgstat_count_backend_aio_start(needs_synchronous); + if (!needs_synchronous) { pgaio_my_backend->staged_ios[pgaio_my_backend->num_staged_ios++] = ioh; @@ -544,6 +547,12 @@ pgaio_io_process_completion(PgAioHandle *ioh, int result) /* condition variable broadcast ensures state is visible before wakeup */ ConditionVariableBroadcast(&ioh->cv); + /* Track AIO completion stats */ + if (ioh->owner_procno == MyProcNumber) + pgstat_count_backend_aio_complete_self(); + else + pgstat_count_backend_aio_complete_other(); + /* contains call to pgaio_io_call_complete_local() */ if (ioh->owner_procno == MyProcNumber) pgaio_io_reclaim(ioh); @@ -762,6 +771,8 @@ pgaio_io_wait_for_free(void) { int reclaimed = 0; + pgstat_count_backend_aio_handle_wait(); + pgaio_debug(DEBUG2, "waiting for free IO with %d pending, %u in-flight, %u idle IOs", pgaio_my_backend->num_staged_ios, dclist_count(&pgaio_my_backend->in_flight_ios), @@ -1150,6 +1161,8 @@ pgaio_submit_staged(void) Assert(total_submitted == did_submit); + pgstat_count_backend_aio_submitted(); + pgaio_my_backend->num_staged_ios = 0; pgaio_debug(DEBUG4, diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c index b736b2ccc6f..4ad755f7f18 100644 --- a/src/backend/utils/activity/pgstat_backend.c +++ b/src/backend/utils/activity/pgstat_backend.c @@ -40,6 +40,7 @@ static PgStat_BackendPending PendingBackendStats; static bool backend_has_iostats = false; static bool backend_has_lockstats = false; +static bool backend_has_aiostats = false; /* * WAL usage counters saved from pgWalUsage at the previous call to @@ -120,6 +121,74 @@ pgstat_count_backend_lock_fastpath_exceeded(uint8 locktag_type) pgstat_report_fixed = true; } +/* + * Utility routines to report AIO stats for backends, kept here to avoid + * exposing PendingBackendStats to the outside world. + */ +void +pgstat_count_backend_aio_start(bool synchronous) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + PendingBackendStats.aio_counters.started++; + if (synchronous) + PendingBackendStats.aio_counters.executed_sync++; + else + PendingBackendStats.aio_counters.executed_async++; + + backend_has_aiostats = true; + pgstat_report_fixed = true; +} + +void +pgstat_count_backend_aio_complete_self(void) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + PendingBackendStats.aio_counters.completed_self++; + + backend_has_aiostats = true; + pgstat_report_fixed = true; +} + +void +pgstat_count_backend_aio_complete_other(void) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + PendingBackendStats.aio_counters.completed_other++; + + backend_has_aiostats = true; + pgstat_report_fixed = true; +} + +void +pgstat_count_backend_aio_handle_wait(void) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + PendingBackendStats.aio_counters.handle_waits++; + + backend_has_aiostats = true; + pgstat_report_fixed = true; +} + +void +pgstat_count_backend_aio_submitted(void) +{ + if (!pgstat_tracks_backend_bktype(MyBackendType)) + return; + + PendingBackendStats.aio_counters.submitted++; + + backend_has_aiostats = true; + pgstat_report_fixed = true; +} + /* * Returns statistics of a backend by proc number. */ @@ -326,6 +395,38 @@ pgstat_flush_backend_entry_lock(PgStat_EntryRef *entry_ref) backend_has_lockstats = false; } +/* + * Flush out locally pending backend AIO statistics. Locking is managed + * by the caller. + */ +static void +pgstat_flush_backend_entry_aio(PgStat_EntryRef *entry_ref) +{ + PgStatShared_Backend *shbackendent; + PgStat_AioCounters *bktype_shstats; + + if (!backend_has_aiostats) + return; + + shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats; + bktype_shstats = &shbackendent->stats.aio_counters; + +#define AIOSTAT_ACC(fld) \ + (bktype_shstats->fld += PendingBackendStats.aio_counters.fld) + AIOSTAT_ACC(started); + AIOSTAT_ACC(executed_sync); + AIOSTAT_ACC(executed_async); + AIOSTAT_ACC(completed_self); + AIOSTAT_ACC(completed_other); + AIOSTAT_ACC(handle_waits); + AIOSTAT_ACC(submitted); +#undef AIOSTAT_ACC + + MemSet(&PendingBackendStats.aio_counters, 0, sizeof(PgStat_AioCounters)); + + backend_has_aiostats = false; +} + /* * Flush out locally pending backend statistics * @@ -354,6 +455,10 @@ pgstat_flush_backend(bool nowait, uint32 flags) if ((flags & PGSTAT_BACKEND_FLUSH_LOCK) && backend_has_lockstats) has_pending_data = true; + /* Some AIO data pending? */ + if ((flags & PGSTAT_BACKEND_FLUSH_AIO) && backend_has_aiostats) + has_pending_data = true; + if (!has_pending_data) return false; @@ -372,6 +477,9 @@ pgstat_flush_backend(bool nowait, uint32 flags) if (flags & PGSTAT_BACKEND_FLUSH_LOCK) pgstat_flush_backend_entry_lock(entry_ref); + if (flags & PGSTAT_BACKEND_FLUSH_AIO) + pgstat_flush_backend_entry_aio(entry_ref); + pgstat_unlock_entry(entry_ref); return false; @@ -411,6 +519,7 @@ pgstat_create_backend(ProcNumber procnum) MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending)); backend_has_iostats = false; backend_has_lockstats = false; + backend_has_aiostats = false; /* * Initialize prevBackendWalUsage with pgWalUsage so that diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 565d0e70768..fb62a56f3fe 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -1722,6 +1722,66 @@ pg_stat_get_backend_wal(PG_FUNCTION_ARGS) return (pg_stat_wal_build_tuple(bktype_stats, backend_stats->stat_reset_timestamp)); } +/* + * Returns AIO statistics for a backend with given PID. + */ +Datum +pg_stat_get_backend_aio(PG_FUNCTION_ARGS) +{ +#define PG_STAT_BACKEND_AIO_COLS 8 + TupleDesc tupdesc; + Datum values[PG_STAT_BACKEND_AIO_COLS] = {0}; + bool nulls[PG_STAT_BACKEND_AIO_COLS] = {0}; + int pid; + PgStat_Backend *backend_stats; + PgStat_AioCounters aio_counters; + + pid = PG_GETARG_INT32(0); + backend_stats = pgstat_fetch_stat_backend_by_pid(pid, NULL); + + if (!backend_stats) + PG_RETURN_NULL(); + + aio_counters = backend_stats->aio_counters; + + /* Initialise attributes information in the tuple descriptor */ + tupdesc = CreateTemplateTupleDesc(PG_STAT_BACKEND_AIO_COLS); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "started", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "executed_sync", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "executed_async", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "completed_self", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "completed_other", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "handle_waits", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "submitted", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "stats_reset", + TIMESTAMPTZOID, -1, 0); + TupleDescFinalize(tupdesc); + BlessTupleDesc(tupdesc); + + /* Fill values */ + values[0] = Int64GetDatum(aio_counters.started); + values[1] = Int64GetDatum(aio_counters.executed_sync); + values[2] = Int64GetDatum(aio_counters.executed_async); + values[3] = Int64GetDatum(aio_counters.completed_self); + values[4] = Int64GetDatum(aio_counters.completed_other); + values[5] = Int64GetDatum(aio_counters.handle_waits); + values[6] = Int64GetDatum(aio_counters.submitted); + + if (backend_stats->stat_reset_timestamp != 0) + values[7] = TimestampTzGetDatum(backend_stats->stat_reset_timestamp); + else + nulls[7] = true; + + PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); +} + /* * Returns statistics of WAL activity */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 3cb84359adf..a089860ada4 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6108,6 +6108,13 @@ proargmodes => '{i,o,o,o,o,o}', proargnames => '{backend_pid,locktype,waits,wait_time,fastpath_exceeded,stats_reset}', prosrc => 'pg_stat_get_backend_lock' }, +{ oid => '9082', descr => 'statistics: backend AIO activity', + proname => 'pg_stat_get_backend_aio', provolatile => 'v', proparallel => 'r', + prorettype => 'record', proargtypes => 'int4', + proallargtypes => '{int4,int8,int8,int8,int8,int8,int8,int8,timestamptz}', + proargmodes => '{i,o,o,o,o,o,o,o,o}', + proargnames => '{backend_pid,started,executed_sync,executed_async,completed_self,completed_other,handle_waits,submitted,stats_reset}', + prosrc => 'pg_stat_get_backend_aio' }, { oid => '6248', descr => 'statistics: information about WAL prefetching', proname => 'pg_stat_get_recovery_prefetch', prorows => '1', proretset => 't', provolatile => 'v', prorettype => 'record', proargtypes => '', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 58a44857f13..64afb2bc082 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -514,6 +514,21 @@ typedef struct PgStat_WalStats TimestampTz stat_reset_timestamp; } PgStat_WalStats; +/* ------- + * PgStat_AioCounters AIO activity counters + * ------- + */ +typedef struct PgStat_AioCounters +{ + PgStat_Counter started; + PgStat_Counter executed_sync; + PgStat_Counter executed_async; + PgStat_Counter completed_self; + PgStat_Counter completed_other; + PgStat_Counter handle_waits; + PgStat_Counter submitted; +} PgStat_AioCounters; + /* ------- * PgStat_Backend Backend statistics * ------- @@ -524,6 +539,7 @@ typedef struct PgStat_Backend PgStat_BktypeIO io_stats; PgStat_WalCounters wal_counters; PgStat_PendingLock lock_stats; + PgStat_AioCounters aio_counters; } PgStat_Backend; /* --------- @@ -542,6 +558,8 @@ typedef struct PgStat_BackendPending * PGSTAT_KIND_LOCK. */ PgStat_PendingLock pending_lock; + /* Store the AIO statistics counters */ + PgStat_AioCounters aio_counters; } PgStat_BackendPending; /* @@ -598,6 +616,13 @@ extern void pgstat_count_backend_io_op(IOObject io_object, extern void pgstat_count_backend_lock_waits(uint8 locktag_type, PgStat_Counter usecs); extern void pgstat_count_backend_lock_fastpath_exceeded(uint8 locktag_type); +/* used by aio.c for AIO stats tracked in backends */ +extern void pgstat_count_backend_aio_start(bool synchronous); +extern void pgstat_count_backend_aio_complete_self(void); +extern void pgstat_count_backend_aio_complete_other(void); +extern void pgstat_count_backend_aio_handle_wait(void); +extern void pgstat_count_backend_aio_submitted(void); + extern PgStat_Backend *pgstat_fetch_stat_backend(ProcNumber procNumber); extern PgStat_Backend *pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype); diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index b3dc3ff7d8b..b2092fb42b9 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -706,7 +706,8 @@ extern void pgstat_archiver_snapshot_cb(void); #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_LOCK (1 << 2) /* Flush lock statistics */ -#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_LOCK) +#define PGSTAT_BACKEND_FLUSH_AIO (1 << 3) /* Flush AIO statistics */ +#define PGSTAT_BACKEND_FLUSH_ALL (PGSTAT_BACKEND_FLUSH_IO | PGSTAT_BACKEND_FLUSH_WAL | PGSTAT_BACKEND_FLUSH_LOCK | PGSTAT_BACKEND_FLUSH_AIO) extern bool pgstat_flush_backend(bool nowait, uint32 flags); extern bool pgstat_backend_flush_cb(bool nowait); diff --git a/src/test/modules/test_aio/t/001_aio.pl b/src/test/modules/test_aio/t/001_aio.pl index 63cadd64c15..8ecf3d3ad91 100644 --- a/src/test/modules/test_aio/t/001_aio.pl +++ b/src/test/modules/test_aio/t/001_aio.pl @@ -1842,6 +1842,37 @@ read_buffers('$table', 0, 4)|, $psql_c->quit(); } +# Test per-backend AIO statistics counters +sub test_aio_stats +{ + my $io_method = shift; + my $node = shift; + + my $psql = $node->background_psql('postgres', on_error_stop => 0); + + # Reset backend stats, evict relation, then read it back to force + # physical IO through the AIO layer. + $psql->query_safe(qq(SELECT pg_stat_reset_backend_stats(pg_backend_pid()))); + $psql->query_safe(qq(SELECT evict_rel('tbl_ok'))); + $psql->query_safe(qq(SELECT count(*) FROM tbl_ok)); + $psql->query_safe(qq(SELECT pg_stat_force_next_flush())); + + # started must be > 0 after physical IO + my $started = $psql->query_safe( + qq(SELECT started FROM pg_stat_get_backend_aio(pg_backend_pid()))); + cmp_ok($started, '>', 0, + "$io_method: AIO stats: started > 0 after physical IO"); + + # invariant: started = executed_sync + executed_async + my $consistent = $psql->query_safe( + qq(SELECT started = executed_sync + executed_async + FROM pg_stat_get_backend_aio(pg_backend_pid()))); + is($consistent, 't', + "$io_method: AIO stats: started = executed_sync + executed_async"); + + $psql->quit(); +} + # Run all tests that for the specified node / io_method sub test_io_method { @@ -1878,6 +1909,7 @@ CHECKPOINT; test_ignore_checksum($io_method, $node); test_checksum_createdb($io_method, $node); test_read_buffers($io_method, $node); + test_aio_stats($io_method, $node); # generic injection tests SKIP: diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 117e7379f10..6ce3077fa5b 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2329,6 +2329,7 @@ PgStatShared_ReplSlot PgStatShared_SLRU PgStatShared_Subscription PgStatShared_Wal +PgStat_AioCounters PgStat_ArchiverStats PgStat_Backend PgStat_BackendPending -- 2.34.1 --Pi0/Nu3LVhi0Ij+H--