On Mon, Nov 04, 2024 at 09:27:38AM +0000, Bertrand Drouvot wrote: > On Tue, Sep 10, 2024 at 05:30:32AM +0000, Bertrand Drouvot wrote: > > Hi, > > > > On Thu, Sep 05, 2024 at 04:48:36AM +0000, Bertrand Drouvot wrote: > > > Please find attached a mandatory rebase. > > > > > > In passing, checking if based on the previous discussion (and given that > > > we > > > don't have the relation OID when writing buffers out) you see another > > > approach > > > that the one this patch is implementing? > > > > Attached v5, mandatory rebase due to recent changes in the stats area. > > Attached v6, mandatory rebase due to b14e9ce7d5. > > Note that 0001 is the same as the one proposed in [0] and needs to be applied > here to make the stats machinery working as expected with the relfile added in > the stats hash key (though it deserves its own dedicated thread as explained > in [0]). > > Don't look at 0001 and 0002 as I think we need more design discussion. > > === Sum up the feedback received up-thread > > I re-read this thread and it appears that there is 3 main remarks: > > R1: Andres did propose to add stuff like "n_dead_tup" (see [1]), to provide > even more benefits. > > R2: Robert mentioned ([2]) that we need to decide between "sometimes I > don't know the relation OID so I want to use the relfilenumber > instead, without changing the user experience" and "some > of these stats actually properly pertain to the relfilenode rather > than the relation so I want to associate them with the right object > and that will affect how the user sees things". > > R3: Michael had concerns about adding a new field (the relfile) in the hash > key, > see [3]. > > === My thoughts: > > While my initial idea was that the relfilenode stats would deal only with I/O > activities it also looks like that it would be benficial to add sutff like > "n_dead_tup". > > Then I think we should go with the "sometimes I don't know the relation OID > so I want to use the relfilenumber instead, without changing the user > experience" > way. > > Regarding the concern about adding a new field in the hash key, I think we > can't > avoid that as we don't have the relation OID when writing buffers out. > > === Moving forward > > I would go for trying to store everything that is "relation" related into the > relfilenode stats (that will then include n_dead_tup among other things) and > try to hide the distinction between relfilenode stats and relation stats from > the user. > > Thoughts of moving forward that way? > > [0]: > https://www.postgresql.org/message-id/Zyb7RW1y9dVfO0UH%40ip-10-97-1-34.eu-west-3.compute.internal > [1]: > https://www.postgresql.org/message-id/20240607033806.6gwgolihss72cj6r%40awork3.anarazel.de > [2]: > https://www.postgresql.org/message-id/CA%2BTgmoZtwT6h%3DnyuQ1J9GNSrRyhf0fv7Ai6FzO%3DbH0C9Bf6tew%40mail.gmail.com > [3]: https://www.postgresql.org/message-id/Zo9j69GhexDpeV4k%40paquier.xyz >
+ Andres and Robert as both are quoted in my previous message. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
>From 0dbb3dc1bd66c63730696b69fbe768024c8bfb04 Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot <bertranddrouvot...@gmail.com> Date: Sat, 2 Nov 2024 14:21:18 +0000 Subject: [PATCH v6 1/2] Clear padding in PgStat_HashKey keys PgStat_HashKey keys are currently initialized in a way that could result in random data in the padding bytes (if there was padding in PgStat_HashKey which is not the case currently). We are using sizeof(PgStat_HashKey) in pgstat_cmp_hash_key() and we compute the hash hash key in pgstat_hash_hash_key() using the PgStat_HashKey struct size as input. So, we have to ensure that no random data can be stored in the padding bytes (if any) of a PgStat_HashKey key. --- src/backend/utils/activity/pgstat.c | 3 +++ src/backend/utils/activity/pgstat_shmem.c | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) 100.0% src/backend/utils/activity/ diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index be48432cc3..ea8c5691e8 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -938,6 +938,9 @@ pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid) pgstat_prep_snapshot(); + /* clear padding */ + memset(&key, 0, sizeof(struct PgStat_HashKey)); + key.kind = kind; key.dboid = dboid; key.objid = objid; diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c index a09c6fee05..c1b7ff76b1 100644 --- a/src/backend/utils/activity/pgstat_shmem.c +++ b/src/backend/utils/activity/pgstat_shmem.c @@ -432,11 +432,18 @@ PgStat_EntryRef * pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create, bool *created_entry) { - PgStat_HashKey key = {.kind = kind,.dboid = dboid,.objid = objid}; + PgStat_HashKey key; PgStatShared_HashEntry *shhashent; PgStatShared_Common *shheader = NULL; PgStat_EntryRef *entry_ref; + /* clear padding */ + memset(&key, 0, sizeof(struct PgStat_HashKey)); + + key.kind = kind; + key.dboid = dboid; + key.objid = objid; + /* * passing in created_entry only makes sense if we possibly could create * entry. @@ -908,10 +915,17 @@ pgstat_drop_database_and_contents(Oid dboid) bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid) { - PgStat_HashKey key = {.kind = kind,.dboid = dboid,.objid = objid}; + PgStat_HashKey key; PgStatShared_HashEntry *shent; bool freed = true; + /* clear padding */ + memset(&key, 0, sizeof(struct PgStat_HashKey)); + + key.kind = kind; + key.dboid = dboid; + key.objid = objid; + /* delete local reference */ if (pgStatEntryRefHash) { -- 2.34.1
>From a815f124e4283d4f0eb1ce6e58da81c1dcd5bf8e Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot <bertranddrouvot...@gmail.com> Date: Thu, 16 Nov 2023 02:30:01 +0000 Subject: [PATCH v6 2/2] Provide relfilenode statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently don’t have writes counters for relations. The reason is that we don’t have the relation OID when writing buffers out. Tracking writes per relfilenode would allow us to track/consolidate writes per relation. relfilenode stats is also beneficial for the "Split index and table statistics into different types of stats" work in progress: it would allow us to avoid additional branches in some situations. === Remarks === This is a POC patch. There is still work to do: there is more places we should add relfilenode counters, create more APIS to retrieve the relfilenode stats, the patch takes care of rewrite generated by TRUNCATE but there is more to care about like CLUSTER,VACUUM FULL. The new logic to retrieve stats in pg_statio_all_tables has been implemented only for the new blocks_written stat (we'd need to do the same for the existing buffer read / buffer hit if we agree on the approach implemented here). The goal of this patch is to start the discussion and agree on the design before moving forward. --- src/backend/access/rmgrdesc/xactdesc.c | 5 +- src/backend/catalog/storage.c | 8 ++ src/backend/catalog/system_functions.sql | 2 +- src/backend/catalog/system_views.sql | 5 +- src/backend/postmaster/checkpointer.c | 5 + src/backend/storage/buffer/bufmgr.c | 6 +- src/backend/storage/smgr/md.c | 8 ++ src/backend/utils/activity/pgstat.c | 56 +++++---- src/backend/utils/activity/pgstat_database.c | 12 +- src/backend/utils/activity/pgstat_function.c | 13 +- src/backend/utils/activity/pgstat_relation.c | 112 ++++++++++++++++-- src/backend/utils/activity/pgstat_replslot.c | 13 +- src/backend/utils/activity/pgstat_shmem.c | 19 +-- .../utils/activity/pgstat_subscription.c | 14 +-- src/backend/utils/activity/pgstat_xact.c | 66 ++++++++--- src/backend/utils/adt/pgstatfuncs.c | 34 +++++- src/include/access/tableam.h | 19 +++ src/include/access/xact.h | 1 + src/include/catalog/pg_proc.dat | 14 ++- src/include/pgstat.h | 37 ++++-- src/include/utils/pgstat_internal.h | 33 ++++-- src/test/recovery/t/029_stats_restart.pl | 40 +++---- .../recovery/t/030_stats_cleanup_replica.pl | 6 +- src/test/regress/expected/rules.out | 12 +- src/test/regress/expected/stats.out | 30 ++--- src/test/regress/sql/stats.sql | 30 ++--- src/test/subscription/t/026_stats.pl | 4 +- src/tools/pgindent/typedefs.list | 1 + 28 files changed, 443 insertions(+), 162 deletions(-) 4.2% src/backend/catalog/ 48.9% src/backend/utils/activity/ 6.0% src/backend/utils/adt/ 3.7% src/backend/ 3.0% src/include/access/ 3.1% src/include/catalog/ 5.1% src/include/utils/ 6.3% src/include/ 11.2% src/test/recovery/t/ 5.1% src/test/regress/expected/ diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c index 889cb955c1..5a15cdd460 100644 --- a/src/backend/access/rmgrdesc/xactdesc.c +++ b/src/backend/access/rmgrdesc/xactdesc.c @@ -322,10 +322,11 @@ xact_desc_stats(StringInfo buf, const char *label, uint64 objid = ((uint64) dropped_stats[i].objid_hi) << 32 | dropped_stats[i].objid_lo; - appendStringInfo(buf, " %d/%u/%llu", + appendStringInfo(buf, " %d/%u/%llu/%u", dropped_stats[i].kind, dropped_stats[i].dboid, - (unsigned long long) objid); + (unsigned long long) objid, + dropped_stats[i].relfile); } } } diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index f56b3cc0f2..db6107cd90 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -33,6 +33,7 @@ #include "storage/smgr.h" #include "utils/hsearch.h" #include "utils/memutils.h" +#include "utils/pgstat_internal.h" #include "utils/rel.h" /* GUC variables */ @@ -152,6 +153,7 @@ RelationCreateStorage(RelFileLocator rlocator, char relpersistence, if (needs_wal) log_smgrcreate(&srel->smgr_rlocator.locator, MAIN_FORKNUM); + pgstat_create_transactional(PGSTAT_KIND_RELFILENODE, rlocator.dbOid, rlocator.spcOid, rlocator.relNumber); /* * Add the relation to the list of stuff to delete at abort, if we are * asked to do so. @@ -227,6 +229,8 @@ RelationDropStorage(Relation rel) * for now I'll keep the logic simple. */ + pgstat_drop_transactional(PGSTAT_KIND_RELFILENODE, rel->rd_locator.dbOid, rel->rd_locator.spcOid, rel->rd_locator.relNumber); + RelationCloseSmgr(rel); } @@ -253,6 +257,9 @@ RelationPreserveStorage(RelFileLocator rlocator, bool atCommit) PendingRelDelete *pending; PendingRelDelete *prev; PendingRelDelete *next; + PgStat_SubXactStatus *xact_state; + + xact_state = pgStatXactStack; prev = NULL; for (pending = pendingDeletes; pending != NULL; pending = next) @@ -267,6 +274,7 @@ RelationPreserveStorage(RelFileLocator rlocator, bool atCommit) else pendingDeletes = next; pfree(pending); + PgStat_RemoveRelFileNodeFromDroppedStats(xact_state, rlocator); /* prev does not change */ } else diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index 20d3b9b73f..3b9e822de1 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -718,7 +718,7 @@ REVOKE EXECUTE ON FUNCTION pg_stat_reset_single_function_counters(oid) FROM publ REVOKE EXECUTE ON FUNCTION pg_stat_reset_replication_slot(text) FROM public; -REVOKE EXECUTE ON FUNCTION pg_stat_have_stats(text, oid, int8) FROM public; +REVOKE EXECUTE ON FUNCTION pg_stat_have_stats(text, oid, int8, oid) FROM public; REVOKE EXECUTE ON FUNCTION pg_stat_reset_subscription_stats(oid) FROM public; diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 3456b821bc..7e582399bb 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -746,6 +746,7 @@ CREATE VIEW pg_statio_all_tables AS C.relname AS relname, pg_stat_get_blocks_fetched(C.oid) - pg_stat_get_blocks_hit(C.oid) AS heap_blks_read, + pg_stat_get_blocks_written(C.oid) + pg_stat_get_relfilenode_blocks_written(d.oid, CASE WHEN C.reltablespace <> 0 THEN C.reltablespace ELSE d.dattablespace END, C.relfilenode) AS heap_blks_written, pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit, I.idx_blks_read AS idx_blks_read, I.idx_blks_hit AS idx_blks_hit, @@ -754,7 +755,7 @@ CREATE VIEW pg_statio_all_tables AS pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit, X.idx_blks_read AS tidx_blks_read, X.idx_blks_hit AS tidx_blks_hit - FROM pg_class C LEFT JOIN + FROM pg_database d, pg_class C LEFT JOIN pg_class T ON C.reltoastrelid = T.oid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) LEFT JOIN LATERAL ( @@ -771,7 +772,7 @@ CREATE VIEW pg_statio_all_tables AS sum(pg_stat_get_blocks_hit(indexrelid))::bigint AS idx_blks_hit FROM pg_index WHERE indrelid = T.oid ) X ON true - WHERE C.relkind IN ('r', 't', 'm'); + WHERE C.relkind IN ('r', 't', 'm') AND d.datname = current_database(); CREATE VIEW pg_statio_sys_tables AS SELECT * FROM pg_statio_all_tables diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index 982572a75d..808c9d52d5 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -530,6 +530,11 @@ CheckpointerMain(char *startup_data, size_t startup_data_len) /* Report pending statistics to the cumulative stats system */ pgstat_report_checkpointer(); pgstat_report_wal(true); + /* + * No need to check for transaction state in checkpointer before + * calling pgstat_report_stat(). + */ + pgstat_report_stat(true); /* * If any checkpoint flags have been set, redo the loop to handle the diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 0f02bf62fa..38cd5dddb6 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -1159,9 +1159,9 @@ PinBufferForBlock(Relation rel, * WaitReadBuffers() (so, not for hits, and not for buffers that are * zeroed instead), the per-relation stats always count them. */ - pgstat_count_buffer_read(rel); + pgstat_report_relfilenode_buffer_read(rel); if (*foundPtr) - pgstat_count_buffer_hit(rel); + pgstat_report_relfilenode_buffer_hit(rel); } if (*foundPtr) { @@ -3895,6 +3895,8 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln, IOObject io_object, pgBufferUsage.shared_blks_written++; + pgstat_report_relfilenode_blks_written(reln->smgr_rlocator.locator); + /* * Mark the buffer as clean (unless BM_JUST_DIRTIED has become set) and * end the BM_IO_IN_PROGRESS state. diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index cc8a80ee96..0f71e9016a 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -38,6 +38,7 @@ #include "storage/smgr.h" #include "storage/sync.h" #include "utils/memutils.h" +#include "utils/pgstat_internal.h" /* * The magnetic disk storage manager keeps track of open file @@ -1468,12 +1469,16 @@ DropRelationFiles(RelFileLocator *delrels, int ndelrels, bool isRedo) { SMgrRelation *srels; int i; + int not_freed_count = 0; srels = palloc(sizeof(SMgrRelation) * ndelrels); for (i = 0; i < ndelrels; i++) { SMgrRelation srel = smgropen(delrels[i], INVALID_PROC_NUMBER); + if (!pgstat_drop_entry(PGSTAT_KIND_RELFILENODE, delrels[i].dbOid, delrels[i].spcOid, delrels[i].relNumber)) + not_freed_count++; + if (isRedo) { ForkNumber fork; @@ -1484,6 +1489,9 @@ DropRelationFiles(RelFileLocator *delrels, int ndelrels, bool isRedo) srels[i] = srel; } + if (not_freed_count > 0) + pgstat_request_entry_refs_gc(); + smgrdounlinkall(srels, ndelrels, isRedo); for (i = 0; i < ndelrels; i++) diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index ea8c5691e8..151ba38aa7 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -307,6 +307,19 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE] .delete_pending_cb = pgstat_relation_delete_pending_cb, }, + [PGSTAT_KIND_RELFILENODE] = { + .name = "relfilenode", + + .fixed_amount = false, + + .shared_size = sizeof(PgStatShared_RelFileNode), + .shared_data_off = offsetof(PgStatShared_RelFileNode, stats), + .shared_data_len = sizeof(((PgStatShared_RelFileNode *) 0)->stats), + .pending_size = sizeof(PgStat_StatRelFileNodeEntry), + + .flush_pending_cb = pgstat_relfilenode_flush_cb, + }, + [PGSTAT_KIND_FUNCTION] = { .name = "function", @@ -756,7 +769,7 @@ pgstat_report_stat(bool force) partial_flush = false; - /* flush database / relation / function / ... stats */ + /* flush database / relation / function / relfilenode / ... stats */ partial_flush |= pgstat_flush_pending_entries(nowait); /* flush of fixed-numbered stats */ @@ -845,7 +858,7 @@ pgstat_reset_counters(void) * GRANT system. */ void -pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind); TimestampTz ts = GetCurrentTimestamp(); @@ -854,7 +867,7 @@ pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid) Assert(!pgstat_get_kind_info(kind)->fixed_amount); /* reset the "single counter" */ - pgstat_reset_entry(kind, dboid, objid, ts); + pgstat_reset_entry(kind, dboid, objid, relfile, ts); if (!kind_info->accessed_across_databases) pgstat_reset_database_timestamp(dboid, ts); @@ -925,7 +938,7 @@ pgstat_clear_snapshot(void) } void * -pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { PgStat_HashKey key; PgStat_EntryRef *entry_ref; @@ -944,6 +957,7 @@ pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid) key.kind = kind; key.dboid = dboid; key.objid = objid; + key.relfile = relfile; /* if we need to build a full snapshot, do so */ if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT) @@ -969,7 +983,7 @@ pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid) pgStatLocal.snapshot.mode = pgstat_fetch_consistency; - entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL); + entry_ref = pgstat_get_entry_ref(kind, dboid, objid, relfile, false, NULL); if (entry_ref == NULL || entry_ref->shared_entry->dropped) { @@ -1038,13 +1052,13 @@ pgstat_get_stat_snapshot_timestamp(bool *have_snapshot) } bool -pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { /* fixed-numbered stats always exist */ if (pgstat_get_kind_info(kind)->fixed_amount) return true; - return pgstat_get_entry_ref(kind, dboid, objid, false, NULL) != NULL; + return pgstat_get_entry_ref(kind, dboid, objid, relfile, false, NULL) != NULL; } /* @@ -1259,7 +1273,8 @@ pgstat_build_snapshot_fixed(PgStat_Kind kind) * created, false otherwise. */ PgStat_EntryRef * -pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *created_entry) +pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, + RelFileNumber relfile, bool *created_entry) { PgStat_EntryRef *entry_ref; @@ -1274,7 +1289,7 @@ pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *creat ALLOCSET_SMALL_SIZES); } - entry_ref = pgstat_get_entry_ref(kind, dboid, objid, + entry_ref = pgstat_get_entry_ref(kind, dboid, objid, relfile, true, created_entry); if (entry_ref->pending == NULL) @@ -1297,11 +1312,11 @@ pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *creat * that it shouldn't be needed. */ PgStat_EntryRef * -pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { PgStat_EntryRef *entry_ref; - entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL); + entry_ref = pgstat_get_entry_ref(kind, dboid, objid, relfile, false, NULL); if (entry_ref == NULL || entry_ref->pending == NULL) return NULL; @@ -1330,7 +1345,7 @@ pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref) } /* - * Flush out pending stats for database objects (databases, relations, + * Flush out pending stats for database objects (databases, relations, relfilenodes, * functions). */ static bool @@ -1650,9 +1665,10 @@ pgstat_write_statsfile(XLogRecPtr redo) */ if (!pgstat_is_kind_valid(ps->key.kind)) { - elog(WARNING, "found unknown stats entry %u/%u/%llu", + elog(WARNING, "found unknown stats entry %u/%u/%llu/%u", ps->key.kind, ps->key.dboid, - (unsigned long long) ps->key.objid); + (unsigned long long) ps->key.objid, + ps->key.relfile); continue; } @@ -1888,9 +1904,9 @@ pgstat_read_statsfile(XLogRecPtr redo) if (!pgstat_is_kind_valid(key.kind)) { - elog(WARNING, "invalid stats kind for entry %u/%u/%llu of type %c", + elog(WARNING, "invalid stats kind for entry %u/%u/%llu/%u of type %c", key.kind, key.dboid, - (unsigned long long) key.objid, t); + (unsigned long long) key.objid, key.relfile, t); goto error; } } @@ -1961,9 +1977,9 @@ pgstat_read_statsfile(XLogRecPtr redo) if (found) { dshash_release_lock(pgStatLocal.shared_hash, p); - elog(WARNING, "found duplicate stats entry %u/%u/%llu of type %c", + elog(WARNING, "found duplicate stats entry %u/%u/%llu/%u of type %c", key.kind, key.dboid, - (unsigned long long) key.objid, t); + (unsigned long long) key.objid, key.relfile, t); goto error; } @@ -1974,9 +1990,9 @@ pgstat_read_statsfile(XLogRecPtr redo) pgstat_get_entry_data(key.kind, header), pgstat_get_entry_len(key.kind))) { - elog(WARNING, "could not read data for entry %u/%u/%llu of type %c", + elog(WARNING, "could not read data for entry %u/%u/%llu/%u of type %c", key.kind, key.dboid, - (unsigned long long) key.objid, t); + (unsigned long long) key.objid, key.relfile, t); goto error; } diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c index 29bc090974..cf77f2dbdb 100644 --- a/src/backend/utils/activity/pgstat_database.c +++ b/src/backend/utils/activity/pgstat_database.c @@ -43,7 +43,7 @@ static PgStat_Counter pgLastSessionReportTime = 0; void pgstat_drop_database(Oid databaseid) { - pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid); + pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid, InvalidOid); } /* @@ -66,7 +66,7 @@ pgstat_report_autovac(Oid dboid) * operation so it doesn't matter if we get blocked here a little. */ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, - dboid, InvalidOid, false); + dboid, InvalidOid, InvalidOid, false); dbentry = (PgStatShared_Database *) entry_ref->shared_stats; dbentry->stats.last_autovac_time = GetCurrentTimestamp(); @@ -150,7 +150,7 @@ pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount) * common enough for that to be a problem. */ entry_ref = - pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, dboid, InvalidOid, false); + pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, dboid, InvalidOid, InvalidOid, false); sharedent = (PgStatShared_Database *) entry_ref->shared_stats; sharedent->stats.checksum_failures += failurecount; @@ -242,7 +242,7 @@ PgStat_StatDBEntry * pgstat_fetch_stat_dbentry(Oid dboid) { return (PgStat_StatDBEntry *) - pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid); + pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid, InvalidOid); } void @@ -341,7 +341,7 @@ pgstat_prep_database_pending(Oid dboid) Assert(!OidIsValid(dboid) || OidIsValid(MyDatabaseId)); entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid, - NULL); + InvalidOid, NULL); return entry_ref->pending; } @@ -357,7 +357,7 @@ pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts) PgStatShared_Database *dbentry; dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid, - false); + InvalidOid, false); dbentry = (PgStatShared_Database *) dbref->shared_stats; dbentry->stats.stat_reset_timestamp = ts; diff --git a/src/backend/utils/activity/pgstat_function.c b/src/backend/utils/activity/pgstat_function.c index d26da551a4..440e44e300 100644 --- a/src/backend/utils/activity/pgstat_function.c +++ b/src/backend/utils/activity/pgstat_function.c @@ -46,7 +46,8 @@ pgstat_create_function(Oid proid) { pgstat_create_transactional(PGSTAT_KIND_FUNCTION, MyDatabaseId, - proid); + proid, + InvalidOid); } /* @@ -61,7 +62,8 @@ pgstat_drop_function(Oid proid) { pgstat_drop_transactional(PGSTAT_KIND_FUNCTION, MyDatabaseId, - proid); + proid, + InvalidOid); } /* @@ -86,6 +88,7 @@ pgstat_init_function_usage(FunctionCallInfo fcinfo, entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, fcinfo->flinfo->fn_oid, + InvalidOid, &created_entry); /* @@ -113,7 +116,7 @@ pgstat_init_function_usage(FunctionCallInfo fcinfo, if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid))) { pgstat_drop_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, - fcinfo->flinfo->fn_oid); + fcinfo->flinfo->fn_oid, InvalidOid); ereport(ERROR, errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function call to dropped function")); } @@ -224,7 +227,7 @@ find_funcstat_entry(Oid func_id) { PgStat_EntryRef *entry_ref; - entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id); + entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id, InvalidOid); if (entry_ref) return entry_ref->pending; @@ -239,5 +242,5 @@ PgStat_StatFuncEntry * pgstat_fetch_stat_funcentry(Oid func_id) { return (PgStat_StatFuncEntry *) - pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id); + pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id, InvalidOid); } diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index faba8b64d2..cc71c23760 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -43,6 +43,7 @@ typedef struct TwoPhasePgStatRecord static PgStat_TableStatus *pgstat_prep_relation_pending(Oid rel_id, bool isshared); +PgStat_StatRelFileNodeEntry *pgstat_prep_relfilenode_pending(RelFileLocator locator); static void add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level); static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info); static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool is_drop); @@ -68,6 +69,7 @@ pgstat_copy_relation_stats(Relation dst, Relation src) dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, RelationGetRelid(dst), + InvalidOid, false); dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats; @@ -169,7 +171,7 @@ pgstat_create_relation(Relation rel) { pgstat_create_transactional(PGSTAT_KIND_RELATION, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, - RelationGetRelid(rel)); + RelationGetRelid(rel), InvalidOid); } /* @@ -183,7 +185,7 @@ pgstat_drop_relation(Relation rel) pgstat_drop_transactional(PGSTAT_KIND_RELATION, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, - RelationGetRelid(rel)); + RelationGetRelid(rel), InvalidOid); if (!pgstat_should_count_relation(rel)) return; @@ -224,7 +226,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared, /* block acquiring lock for the same reason as pgstat_report_autovac() */ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, - dboid, tableoid, false); + dboid, tableoid, InvalidOid, false); shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats; tabentry = &shtabentry->stats; @@ -317,6 +319,7 @@ pgstat_report_analyze(Relation rel, /* block acquiring lock for the same reason as pgstat_report_autovac() */ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, dboid, RelationGetRelid(rel), + InvalidOid, false); /* can't get dropped while accessed */ Assert(entry_ref != NULL && entry_ref->shared_stats != NULL); @@ -457,6 +460,19 @@ pgstat_fetch_stat_tabentry(Oid relid) return pgstat_fetch_stat_tabentry_ext(IsSharedRelation(relid), relid); } +/* + * Support function for the SQL-callable pgstat* functions. Returns + * the collected statistics for one relfilenode or NULL. NULL doesn't mean + * that the relfilenode doesn't exist, just that there are no statistics, so the + * caller is better off to report ZERO instead. + */ +PgStat_StatRelFileNodeEntry * +pgstat_fetch_stat_relfilenodeentry(Oid dboid, Oid spcOid, RelFileNumber relfile) +{ + return (PgStat_StatRelFileNodeEntry *) + pgstat_fetch_entry(PGSTAT_KIND_RELFILENODE, dboid, spcOid, relfile); +} + /* * More efficient version of pgstat_fetch_stat_tabentry(), allowing to specify * whether the to-be-accessed table is a shared relation or not. @@ -467,7 +483,7 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid) Oid dboid = (shared ? InvalidOid : MyDatabaseId); return (PgStat_StatTabEntry *) - pgstat_fetch_entry(PGSTAT_KIND_RELATION, dboid, reloid); + pgstat_fetch_entry(PGSTAT_KIND_RELATION, dboid, reloid, InvalidOid); } /* @@ -490,10 +506,10 @@ find_tabstat_entry(Oid rel_id) PgStat_TableStatus *tabentry = NULL; PgStat_TableStatus *tablestatus = NULL; - entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, MyDatabaseId, rel_id); + entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, MyDatabaseId, rel_id, InvalidOid); if (!entry_ref) { - entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, InvalidOid, rel_id); + entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, InvalidOid, rel_id, InvalidOid); if (!entry_ref) return tablestatus; } @@ -877,6 +893,38 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) return true; } +/* + * Flush out pending stats for the relfilenode entry + * + * If nowait is true, this function returns false if lock could not + * immediately acquired, otherwise true is returned. + */ +bool +pgstat_relfilenode_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) +{ + PgStatShared_RelFileNode *sharedent; + PgStat_StatRelFileNodeEntry *pendingent; + + pendingent = (PgStat_StatRelFileNodeEntry *) entry_ref->pending; + sharedent = (PgStatShared_RelFileNode *) entry_ref->shared_stats; + + if (!pgstat_lock_entry(entry_ref, nowait)) + return false; + +#define PGSTAT_ACCUM_RELFILENODECOUNT(item) \ + (sharedent)->stats.item += (pendingent)->item + + PGSTAT_ACCUM_RELFILENODECOUNT(blocks_fetched); + PGSTAT_ACCUM_RELFILENODECOUNT(blocks_hit); + PGSTAT_ACCUM_RELFILENODECOUNT(blocks_written); + + pgstat_unlock_entry(entry_ref); + + memset(pendingent, 0, sizeof(*pendingent)); + + return true; +} + void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref) { @@ -898,7 +946,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared) entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_RELATION, isshared ? InvalidOid : MyDatabaseId, - rel_id, NULL); + rel_id, InvalidOid, NULL); pending = entry_ref->pending; pending->id = rel_id; pending->shared = isshared; @@ -906,6 +954,56 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared) return pending; } +PgStat_StatRelFileNodeEntry * +pgstat_prep_relfilenode_pending(RelFileLocator locator) +{ + PgStat_EntryRef *entry_ref; + + entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_RELFILENODE, locator.dbOid, + locator.spcOid, locator.relNumber, NULL); + + return entry_ref->pending; +} + +void +pgstat_report_relfilenode_blks_written(RelFileLocator locator) +{ + PgStat_StatRelFileNodeEntry *relfileentry = NULL; + + relfileentry = pgstat_prep_relfilenode_pending(locator); + + if (relfileentry) + relfileentry->blocks_written++; +} + +void +pgstat_report_relfilenode_buffer_read(Relation reln) +{ + PgStat_StatRelFileNodeEntry *relfileentry = NULL; + + /* For relation stats to survive after a rewrite */ + pgstat_count_buffer_read(reln); + + relfileentry = pgstat_prep_relfilenode_pending(reln->rd_locator); + + if (relfileentry) + relfileentry->blocks_fetched++; +} + +void +pgstat_report_relfilenode_buffer_hit(Relation reln) +{ + PgStat_StatRelFileNodeEntry *relfileentry = NULL; + + /* For relation stats to survive after a rewrite */ + pgstat_count_buffer_hit(reln); + + relfileentry = pgstat_prep_relfilenode_pending(reln->rd_locator); + + if (relfileentry) + relfileentry->blocks_hit++; +} + /* * add a new (sub)transaction state record */ diff --git a/src/backend/utils/activity/pgstat_replslot.c b/src/backend/utils/activity/pgstat_replslot.c index ddf2ab9928..da7016313e 100644 --- a/src/backend/utils/activity/pgstat_replslot.c +++ b/src/backend/utils/activity/pgstat_replslot.c @@ -62,7 +62,7 @@ pgstat_reset_replslot(const char *name) */ if (SlotIsLogical(slot)) pgstat_reset(PGSTAT_KIND_REPLSLOT, InvalidOid, - ReplicationSlotIndex(slot)); + ReplicationSlotIndex(slot), InvalidOid); LWLockRelease(ReplicationSlotControlLock); } @@ -82,7 +82,7 @@ pgstat_report_replslot(ReplicationSlot *slot, const PgStat_StatReplSlotEntry *re PgStat_StatReplSlotEntry *statent; entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid, - ReplicationSlotIndex(slot), false); + ReplicationSlotIndex(slot), InvalidOid, false); shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats; statent = &shstatent->stats; @@ -116,7 +116,7 @@ pgstat_create_replslot(ReplicationSlot *slot) Assert(LWLockHeldByMeInMode(ReplicationSlotAllocationLock, LW_EXCLUSIVE)); entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid, - ReplicationSlotIndex(slot), false); + ReplicationSlotIndex(slot), InvalidOid, false); shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats; /* @@ -146,7 +146,7 @@ void pgstat_acquire_replslot(ReplicationSlot *slot) { pgstat_get_entry_ref(PGSTAT_KIND_REPLSLOT, InvalidOid, - ReplicationSlotIndex(slot), true, NULL); + ReplicationSlotIndex(slot), InvalidOid, true, NULL); } /* @@ -158,7 +158,7 @@ pgstat_drop_replslot(ReplicationSlot *slot) Assert(LWLockHeldByMeInMode(ReplicationSlotAllocationLock, LW_EXCLUSIVE)); if (!pgstat_drop_entry(PGSTAT_KIND_REPLSLOT, InvalidOid, - ReplicationSlotIndex(slot))) + ReplicationSlotIndex(slot), InvalidOid)) pgstat_request_entry_refs_gc(); } @@ -178,7 +178,7 @@ pgstat_fetch_replslot(NameData slotname) if (idx != -1) slotentry = (PgStat_StatReplSlotEntry *) pgstat_fetch_entry(PGSTAT_KIND_REPLSLOT, - InvalidOid, idx); + InvalidOid, idx, InvalidOid); LWLockRelease(ReplicationSlotControlLock); @@ -210,6 +210,7 @@ pgstat_replslot_from_serialized_name_cb(const NameData *name, PgStat_HashKey *ke key->kind = PGSTAT_KIND_REPLSLOT; key->dboid = InvalidOid; key->objid = idx; + key->relfile = InvalidOid; return true; } diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c index c1b7ff76b1..82f955a0b2 100644 --- a/src/backend/utils/activity/pgstat_shmem.c +++ b/src/backend/utils/activity/pgstat_shmem.c @@ -429,8 +429,8 @@ pgstat_get_entry_ref_cached(PgStat_HashKey key, PgStat_EntryRef **entry_ref_p) * if the entry is newly created, false otherwise. */ PgStat_EntryRef * -pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create, - bool *created_entry) +pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile, + bool create, bool *created_entry) { PgStat_HashKey key; PgStatShared_HashEntry *shhashent; @@ -443,6 +443,7 @@ pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create, key.kind = kind; key.dboid = dboid; key.objid = objid; + key.relfile = relfile; /* * passing in created_entry only makes sense if we possibly could create @@ -652,12 +653,12 @@ pgstat_unlock_entry(PgStat_EntryRef *entry_ref) */ PgStat_EntryRef * pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, uint64 objid, - bool nowait) + RelFileNumber relfile, bool nowait) { PgStat_EntryRef *entry_ref; /* find shared table stats entry corresponding to the local entry */ - entry_ref = pgstat_get_entry_ref(kind, dboid, objid, true, NULL); + entry_ref = pgstat_get_entry_ref(kind, dboid, objid, relfile, true, NULL); /* lock the shared entry to protect the content, skip if failed */ if (!pgstat_lock_entry(entry_ref, nowait)) @@ -827,10 +828,11 @@ pgstat_drop_entry_internal(PgStatShared_HashEntry *shent, */ if (shent->dropped) elog(ERROR, - "trying to drop stats entry already dropped: kind=%s dboid=%u objid=%llu refcount=%u", + "trying to drop stats entry already dropped: kind=%s dboid=%u objid=%llu relfile=%u refcount=%u", pgstat_get_kind_info(shent->key.kind)->name, shent->key.dboid, (unsigned long long) shent->key.objid, + shent->key.relfile, pg_atomic_read_u32(&shent->refcount)); shent->dropped = true; @@ -913,7 +915,7 @@ pgstat_drop_database_and_contents(Oid dboid) * pgstat_gc_entry_refs(). */ bool -pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { PgStat_HashKey key; PgStatShared_HashEntry *shent; @@ -925,6 +927,7 @@ pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid) key.kind = kind; key.dboid = dboid; key.objid = objid; + key.relfile = relfile; /* delete local reference */ if (pgStatEntryRefHash) @@ -995,13 +998,13 @@ shared_stat_reset_contents(PgStat_Kind kind, PgStatShared_Common *header, * Reset one variable-numbered stats entry. */ void -pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts) +pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile, TimestampTz ts) { PgStat_EntryRef *entry_ref; Assert(!pgstat_get_kind_info(kind)->fixed_amount); - entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL); + entry_ref = pgstat_get_entry_ref(kind, dboid, objid, relfile, false, NULL); if (!entry_ref || entry_ref->shared_entry->dropped) return; diff --git a/src/backend/utils/activity/pgstat_subscription.c b/src/backend/utils/activity/pgstat_subscription.c index e06c92727e..417c81246d 100644 --- a/src/backend/utils/activity/pgstat_subscription.c +++ b/src/backend/utils/activity/pgstat_subscription.c @@ -30,7 +30,7 @@ pgstat_report_subscription_error(Oid subid, bool is_apply_error) PgStat_BackendSubEntry *pending; entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_SUBSCRIPTION, - InvalidOid, subid, NULL); + InvalidOid, subid, InvalidOid, NULL); pending = entry_ref->pending; if (is_apply_error) @@ -49,7 +49,7 @@ pgstat_report_subscription_conflict(Oid subid, ConflictType type) PgStat_BackendSubEntry *pending; entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_SUBSCRIPTION, - InvalidOid, subid, NULL); + InvalidOid, subid, InvalidOid, NULL); pending = entry_ref->pending; pending->conflict_count[type]++; } @@ -62,12 +62,12 @@ pgstat_create_subscription(Oid subid) { /* Ensures that stats are dropped if transaction rolls back */ pgstat_create_transactional(PGSTAT_KIND_SUBSCRIPTION, - InvalidOid, subid); + InvalidOid, subid, InvalidOid); /* Create and initialize the subscription stats entry */ - pgstat_get_entry_ref(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, + pgstat_get_entry_ref(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, InvalidOid, true, NULL); - pgstat_reset_entry(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, 0); + pgstat_reset_entry(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, InvalidOid, 0); } /* @@ -79,7 +79,7 @@ void pgstat_drop_subscription(Oid subid) { pgstat_drop_transactional(PGSTAT_KIND_SUBSCRIPTION, - InvalidOid, subid); + InvalidOid, subid, InvalidOid); } /* @@ -90,7 +90,7 @@ PgStat_StatSubEntry * pgstat_fetch_stat_subscription(Oid subid) { return (PgStat_StatSubEntry *) - pgstat_fetch_entry(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid); + pgstat_fetch_entry(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, InvalidOid); } /* diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c index f87a195996..4c663b7d62 100644 --- a/src/backend/utils/activity/pgstat_xact.c +++ b/src/backend/utils/activity/pgstat_xact.c @@ -30,7 +30,7 @@ static void AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool static void AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth); -static PgStat_SubXactStatus *pgStatXactStack = NULL; +PgStat_SubXactStatus *pgStatXactStack = NULL; /* @@ -85,7 +85,7 @@ AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit) * Transaction that dropped an object committed. Drop the stats * too. */ - if (!pgstat_drop_entry(it->kind, it->dboid, objid)) + if (!pgstat_drop_entry(it->kind, it->dboid, objid, it->relfile)) not_freed_count++; } else if (!isCommit && pending->is_create) @@ -94,7 +94,7 @@ AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit) * Transaction that created an object aborted. Drop the stats * associated with the object. */ - if (!pgstat_drop_entry(it->kind, it->dboid, objid)) + if (!pgstat_drop_entry(it->kind, it->dboid, objid, it->relfile)) not_freed_count++; } @@ -106,6 +106,38 @@ AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit) pgstat_request_entry_refs_gc(); } +/* + * Remove a relfilenode stat from the list of stats to be dropped. + */ +void +PgStat_RemoveRelFileNodeFromDroppedStats(PgStat_SubXactStatus *xact_state, RelFileLocator rlocator) +{ + dlist_mutable_iter iter; + + if (dclist_count(&xact_state->pending_drops) == 0) + return; + + dclist_foreach_modify(iter, &xact_state->pending_drops) + { + PgStat_PendingDroppedStatsItem *pending = + dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur); + xl_xact_stats_item *it = &pending->item; + + if (it->kind == PGSTAT_KIND_RELFILENODE && it->dboid == rlocator.dbOid + && it->relfile == rlocator.relNumber) + { + uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo; + + if (objid == rlocator.spcOid) + { + dclist_delete_from(&xact_state->pending_drops, &pending->node); + pfree(pending); + return; + } + } + } +} + /* * Called from access/transam/xact.c at subtransaction commit/abort. */ @@ -160,7 +192,7 @@ AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, * Subtransaction creating a new stats object aborted. Drop the * stats object. */ - if (!pgstat_drop_entry(it->kind, it->dboid, objid)) + if (!pgstat_drop_entry(it->kind, it->dboid, objid, it->relfile)) not_freed_count++; pfree(pending); } @@ -323,7 +355,11 @@ pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, xl_xact_stats_item *it = &items[i]; uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo; - if (!pgstat_drop_entry(it->kind, it->dboid, objid)) + /* leave it to pgstat_drop_transactional() in RelationDropStorage() */ + if (it->kind == PGSTAT_KIND_RELFILENODE) + continue; + + if (!pgstat_drop_entry(it->kind, it->dboid, objid, it->relfile)) not_freed_count++; } @@ -332,7 +368,8 @@ pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, } static void -create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create) +create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, + RelFileNumber relfile, bool is_create) { int nest_level = GetCurrentTransactionNestLevel(); PgStat_SubXactStatus *xact_state; @@ -346,6 +383,7 @@ create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bo drop->item.dboid = dboid; drop->item.objid_lo = (uint32) objid; drop->item.objid_hi = (uint32) (objid >> 32); + drop->item.relfile = relfile; dclist_push_tail(&xact_state->pending_drops, &drop->node); } @@ -358,19 +396,19 @@ create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bo * dropped. */ void -pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { - if (pgstat_get_entry_ref(kind, dboid, objid, false, NULL)) + if (pgstat_get_entry_ref(kind, dboid, objid, relfile, false, NULL)) { ereport(WARNING, - errmsg("resetting existing statistics for kind %s, db=%u, oid=%llu", + errmsg("resetting existing statistics for kind %s, db=%u, oid=%llu, relfile=%u", (pgstat_get_kind_info(kind))->name, dboid, - (unsigned long long) objid)); + (unsigned long long) objid, relfile)); - pgstat_reset(kind, dboid, objid); + pgstat_reset(kind, dboid, objid, relfile); } - create_drop_transactional_internal(kind, dboid, objid, /* create */ true); + create_drop_transactional_internal(kind, dboid, objid, relfile, /* create */ true); } /* @@ -381,7 +419,7 @@ pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid) * alive. */ void -pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid) +pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile) { - create_drop_transactional_internal(kind, dboid, objid, /* create */ false); + create_drop_transactional_internal(kind, dboid, objid, relfile, /* create */ false); } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index f7b50e0b5a..d7cc55c993 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -106,6 +106,30 @@ PG_STAT_GET_RELENTRY_INT64(tuples_updated) /* pg_stat_get_vacuum_count */ PG_STAT_GET_RELENTRY_INT64(vacuum_count) +#define PG_STAT_GET_RELFILEENTRY_INT64(stat) \ +Datum \ +CppConcat(pg_stat_get_relfilenode_,stat)(PG_FUNCTION_ARGS) \ +{ \ + Oid dboid = PG_GETARG_OID(0); \ + Oid spcOid = PG_GETARG_OID(1); \ + RelFileNumber relfile = PG_GETARG_OID(2); \ + int64 result; \ + PgStat_StatRelFileNodeEntry *relfileentry; \ + \ + if ((relfileentry = pgstat_fetch_stat_relfilenodeentry(dboid, spcOid, relfile)) == NULL) \ + result = 0; \ + else \ + result = (int64) (relfileentry->stat); \ + \ + PG_RETURN_INT64(result); \ +} + +/* pg_stat_get_relfilenode_blocks_written */ +PG_STAT_GET_RELFILEENTRY_INT64(blocks_written) + +/* pg_stat_get_blocks_written */ +PG_STAT_GET_RELENTRY_INT64(blocks_written) + #define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat) \ Datum \ CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS) \ @@ -1764,7 +1788,7 @@ pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS) Oid taboid = PG_GETARG_OID(0); Oid dboid = (IsSharedRelation(taboid) ? InvalidOid : MyDatabaseId); - pgstat_reset(PGSTAT_KIND_RELATION, dboid, taboid); + pgstat_reset(PGSTAT_KIND_RELATION, dboid, taboid, InvalidOid); PG_RETURN_VOID(); } @@ -1774,7 +1798,7 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS) { Oid funcoid = PG_GETARG_OID(0); - pgstat_reset(PGSTAT_KIND_FUNCTION, MyDatabaseId, funcoid); + pgstat_reset(PGSTAT_KIND_FUNCTION, MyDatabaseId, funcoid, InvalidOid); PG_RETURN_VOID(); } @@ -1832,7 +1856,7 @@ pg_stat_reset_subscription_stats(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid subscription OID %u", subid))); - pgstat_reset(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid); + pgstat_reset(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, InvalidOid); } PG_RETURN_VOID(); @@ -2059,7 +2083,9 @@ pg_stat_have_stats(PG_FUNCTION_ARGS) char *stats_type = text_to_cstring(PG_GETARG_TEXT_P(0)); Oid dboid = PG_GETARG_OID(1); uint64 objid = PG_GETARG_INT64(2); + Oid relfile = PG_GETARG_OID(3); + PgStat_Kind kind = pgstat_get_kind_from_str(stats_type); - PG_RETURN_BOOL(pgstat_have_entry(kind, dboid, objid)); + PG_RETURN_BOOL(pgstat_have_entry(kind, dboid, objid, relfile)); } diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index adb478a93c..a3ae8465dd 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -21,7 +21,9 @@ #include "access/sdir.h" #include "access/xact.h" #include "executor/tuptable.h" +#include "pgstat.h" #include "storage/read_stream.h" +#include "utils/pgstat_internal.h" #include "utils/rel.h" #include "utils/snapshot.h" @@ -1633,6 +1635,23 @@ table_relation_set_new_filelocator(Relation rel, TransactionId *freezeXid, MultiXactId *minmulti) { + PgStat_StatRelFileNodeEntry *relfileentry; + PgStat_StatTabEntry *tabentry = NULL; + PgStat_EntryRef *entry_ref = NULL; + PgStatShared_Relation *shtabentry; + + entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_RELATION, MyDatabaseId, rel->rd_id, InvalidOid, false, NULL); + if (entry_ref) + { + shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats; + tabentry = &shtabentry->stats; + } + + relfileentry = pgstat_fetch_stat_relfilenodeentry(rel->rd_locator.dbOid, rel->rd_locator.spcOid, rel->rd_locator.relNumber); + + if (tabentry && relfileentry) + tabentry->blocks_written += relfileentry->blocks_written; + rel->rd_tableam->relation_set_new_filelocator(rel, newrlocator, persistence, freezeXid, minmulti); diff --git a/src/include/access/xact.h b/src/include/access/xact.h index fb64d7413a..7a50e5d008 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -290,6 +290,7 @@ typedef struct xl_xact_stats_item */ uint32 objid_lo; uint32 objid_hi; + RelFileNumber relfile; } xl_xact_stats_item; typedef struct xl_xact_stats_items diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index a38e20f5d9..2a565366cd 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5469,6 +5469,14 @@ proname => 'pg_stat_get_tuples_updated', provolatile => 's', proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', prosrc => 'pg_stat_get_tuples_updated' }, +{ oid => '9280', descr => 'statistics: number of blocks written', + proname => 'pg_stat_get_relfilenode_blocks_written', provolatile => 's', + proparallel => 'r', + proargtypes => 'oid oid oid', + prorettype => 'int8', + proallargtypes => '{oid,oid,oid,int8}', + proargmodes => '{i,i,i,o}', + prosrc => 'pg_stat_get_relfilenode_blocks_written' }, { oid => '1933', descr => 'statistics: number of tuples deleted', proname => 'pg_stat_get_tuples_deleted', provolatile => 's', proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', @@ -5508,6 +5516,10 @@ proname => 'pg_stat_get_blocks_hit', provolatile => 's', proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', prosrc => 'pg_stat_get_blocks_hit' }, +{ oid => '8438', descr => 'statistics: number of blocks written', + proname => 'pg_stat_get_blocks_written', provolatile => 's', proparallel => 'r', + prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_blocks_written' }, { oid => '2781', descr => 'statistics: last manual vacuum time for a table', proname => 'pg_stat_get_last_vacuum_time', provolatile => 's', proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid', @@ -5594,7 +5606,7 @@ { oid => '6230', descr => 'statistics: check if a stats object exists', proname => 'pg_stat_have_stats', provolatile => 'v', proparallel => 'r', - prorettype => 'bool', proargtypes => 'text oid int8', + prorettype => 'bool', proargtypes => 'text oid int8 oid', prosrc => 'pg_stat_have_stats' }, { oid => '6231', descr => 'statistics: information about subscription stats', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index df53fa2d4f..ecfbb7cace 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -16,6 +16,7 @@ #include "portability/instr_time.h" #include "postmaster/pgarch.h" /* for MAX_XFN_CHARS */ #include "replication/conflict.h" +#include "storage/relfilelocator.h" #include "utils/backend_progress.h" /* for backward compatibility */ #include "utils/backend_status.h" /* for backward compatibility */ #include "utils/relcache.h" @@ -46,17 +47,18 @@ /* stats for variable-numbered objects */ #define PGSTAT_KIND_DATABASE 1 /* database-wide statistics */ #define PGSTAT_KIND_RELATION 2 /* per-table statistics */ -#define PGSTAT_KIND_FUNCTION 3 /* per-function statistics */ -#define PGSTAT_KIND_REPLSLOT 4 /* per-slot statistics */ -#define PGSTAT_KIND_SUBSCRIPTION 5 /* per-subscription statistics */ +#define PGSTAT_KIND_RELFILENODE 3 /* per-relfilenode statistics */ +#define PGSTAT_KIND_FUNCTION 4 /* per-function statistics */ +#define PGSTAT_KIND_REPLSLOT 5 /* per-slot statistics */ +#define PGSTAT_KIND_SUBSCRIPTION 6 /* per-subscription statistics */ /* stats for fixed-numbered objects */ -#define PGSTAT_KIND_ARCHIVER 6 -#define PGSTAT_KIND_BGWRITER 7 -#define PGSTAT_KIND_CHECKPOINTER 8 -#define PGSTAT_KIND_IO 9 -#define PGSTAT_KIND_SLRU 10 -#define PGSTAT_KIND_WAL 11 +#define PGSTAT_KIND_ARCHIVER 7 +#define PGSTAT_KIND_BGWRITER 8 +#define PGSTAT_KIND_CHECKPOINTER 9 +#define PGSTAT_KIND_IO 10 +#define PGSTAT_KIND_SLRU 11 +#define PGSTAT_KIND_WAL 12 #define PGSTAT_KIND_BUILTIN_MIN PGSTAT_KIND_DATABASE #define PGSTAT_KIND_BUILTIN_MAX PGSTAT_KIND_WAL @@ -452,6 +454,7 @@ typedef struct PgStat_StatTabEntry PgStat_Counter blocks_fetched; PgStat_Counter blocks_hit; + PgStat_Counter blocks_written; TimestampTz last_vacuum_time; /* user initiated vacuum */ PgStat_Counter vacuum_count; @@ -463,6 +466,13 @@ typedef struct PgStat_StatTabEntry PgStat_Counter autoanalyze_count; } PgStat_StatTabEntry; +typedef struct PgStat_StatRelFileNodeEntry +{ + PgStat_Counter blocks_fetched; + PgStat_Counter blocks_hit; + PgStat_Counter blocks_written; +} PgStat_StatRelFileNodeEntry; + typedef struct PgStat_WalStats { PgStat_Counter wal_records; @@ -513,7 +523,7 @@ extern long pgstat_report_stat(bool force); extern void pgstat_force_next_flush(void); extern void pgstat_reset_counters(void); -extern void pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid); +extern void pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile); extern void pgstat_reset_of_kind(PgStat_Kind kind); /* stats accessors */ @@ -522,7 +532,7 @@ extern TimestampTz pgstat_get_stat_snapshot_timestamp(bool *have_snapshot); /* helpers */ extern PgStat_Kind pgstat_get_kind_from_str(char *kind_str); -extern bool pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid); +extern bool pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile); /* @@ -631,6 +641,10 @@ extern void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter); +extern void pgstat_report_relfilenode_blks_written(RelFileLocator locator); +extern void pgstat_report_relfilenode_buffer_read(Relation reln); +extern void pgstat_report_relfilenode_buffer_hit(Relation reln); + /* * If stats are enabled, but pending data hasn't been prepared yet, call * pgstat_assoc_relation() to do so. See its comment for why this is done @@ -690,6 +704,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info, void *recdata, uint32 len); extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid); +extern PgStat_StatRelFileNodeEntry *pgstat_fetch_stat_relfilenodeentry(Oid dboid, Oid spcOid, RelFileNumber relfile); extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid); extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id); diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index 61b2e1f96b..b5700708fc 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -55,6 +55,7 @@ typedef struct PgStat_HashKey Oid dboid; /* database ID. InvalidOid for shared objects. */ uint64 objid; /* object ID (table, function, etc.), or * identifier. */ + RelFileNumber relfile; /* relfilenumber for RelFileLocator. */ } PgStat_HashKey; /* @@ -410,6 +411,12 @@ typedef struct PgStatShared_Relation PgStat_StatTabEntry stats; } PgStatShared_Relation; +typedef struct PgStatShared_RelFileNode +{ + PgStatShared_Common header; + PgStat_StatRelFileNodeEntry stats; +} PgStatShared_RelFileNode; + typedef struct PgStatShared_Function { PgStatShared_Common header; @@ -548,6 +555,9 @@ static inline void *pgstat_get_entry_data(PgStat_Kind kind, PgStatShared_Common static inline void *pgstat_get_custom_shmem_data(PgStat_Kind kind); static inline void *pgstat_get_custom_snapshot_data(PgStat_Kind kind); +extern PgStat_SubXactStatus *pgStatXactStack; +extern void PgStat_RemoveRelFileNodeFromDroppedStats(PgStat_SubXactStatus *xact_state, RelFileLocator rlocator); + /* * Functions in pgstat.c @@ -565,12 +575,14 @@ extern void pgstat_assert_is_up(void); extern void pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref); extern PgStat_EntryRef *pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, - uint64 objid, + uint64 objid, RelFileNumber relfile, bool *created_entry); extern PgStat_EntryRef *pgstat_fetch_pending_entry(PgStat_Kind kind, - Oid dboid, uint64 objid); + Oid dboid, uint64 objid, + RelFileNumber relfile); -extern void *pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid); +extern void *pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid, + RelFileNumber relfile); extern void pgstat_snapshot_fixed(PgStat_Kind kind); @@ -645,6 +657,7 @@ extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state); extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state); extern bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait); +extern bool pgstat_relfilenode_flush_cb(PgStat_EntryRef *entry_ref, bool nowait); extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref); @@ -665,15 +678,17 @@ extern void pgstat_attach_shmem(void); extern void pgstat_detach_shmem(void); extern PgStat_EntryRef *pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, - bool create, bool *created_entry); + RelFileNumber relfile, bool create, + bool *created_entry); extern bool pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait); extern bool pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait); extern void pgstat_unlock_entry(PgStat_EntryRef *entry_ref); -extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid); +extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile); extern void pgstat_drop_all_entries(void); extern PgStat_EntryRef *pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, uint64 objid, - bool nowait); -extern void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts); + RelFileNumber relfile, bool nowait); +extern void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, + RelFileNumber relfile, TimestampTz ts); extern void pgstat_reset_entries_of_kind(PgStat_Kind kind, TimestampTz ts); extern void pgstat_reset_matching_entries(bool (*do_reset) (PgStatShared_HashEntry *, Datum), Datum match_data, @@ -722,8 +737,8 @@ extern void pgstat_subscription_reset_timestamp_cb(PgStatShared_Common *header, */ extern PgStat_SubXactStatus *pgstat_get_xact_stack_level(int nest_level); -extern void pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid); -extern void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid); +extern void pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile); +extern void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid, RelFileNumber relfile); /* diff --git a/src/test/recovery/t/029_stats_restart.pl b/src/test/recovery/t/029_stats_restart.pl index d14ac12418..4c83a0c167 100644 --- a/src/test/recovery/t/029_stats_restart.pl +++ b/src/test/recovery/t/029_stats_restart.pl @@ -40,10 +40,10 @@ trigger_funcrel_stat(); # verify stats objects exist my $sect = "initial"; -is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist"); -is(have_stats('function', $dboid, $funcoid), +is(have_stats('database', $dboid, 0, 0), 't', "$sect: db stats do exist"); +is(have_stats('function', $dboid, $funcoid, 0), 't', "$sect: function stats do exist"); -is(have_stats('relation', $dboid, $tableoid), +is(have_stats('relation', $dboid, $tableoid, 0), 't', "$sect: relation stats do exist"); # regular shutdown @@ -64,10 +64,10 @@ copy($og_stats, $statsfile) or die "Copy failed: $!"; $node->start; $sect = "copy"; -is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist"); -is(have_stats('function', $dboid, $funcoid), +is(have_stats('database', $dboid, 0, 0), 't', "$sect: db stats do exist"); +is(have_stats('function', $dboid, $funcoid, 0), 't', "$sect: function stats do exist"); -is(have_stats('relation', $dboid, $tableoid), +is(have_stats('relation', $dboid, $tableoid, 0), 't', "$sect: relation stats do exist"); $node->stop('immediate'); @@ -81,10 +81,10 @@ $node->start; # stats should have been discarded $sect = "post immediate"; -is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist"); -is(have_stats('function', $dboid, $funcoid), +is(have_stats('database', $dboid, 0, 0), 'f', "$sect: db stats do not exist"); +is(have_stats('function', $dboid, $funcoid, 0), 'f', "$sect: function stats do exist"); -is(have_stats('relation', $dboid, $tableoid), +is(have_stats('relation', $dboid, $tableoid, 0), 'f', "$sect: relation stats do not exist"); # get rid of backup statsfile @@ -95,10 +95,10 @@ unlink $statsfile or die "cannot unlink $statsfile $!"; trigger_funcrel_stat(); $sect = "post immediate, new"; -is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist"); -is(have_stats('function', $dboid, $funcoid), +is(have_stats('database', $dboid, 0, 0), 't', "$sect: db stats do exist"); +is(have_stats('function', $dboid, $funcoid, 0), 't', "$sect: function stats do exist"); -is(have_stats('relation', $dboid, $tableoid), +is(have_stats('relation', $dboid, $tableoid, 0), 't', "$sect: relation stats do exist"); # regular shutdown @@ -114,10 +114,10 @@ $node->start; # no stats present due to invalid stats file $sect = "invalid_overwrite"; -is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist"); -is(have_stats('function', $dboid, $funcoid), +is(have_stats('database', $dboid, 0, 0), 'f', "$sect: db stats do not exist"); +is(have_stats('function', $dboid, $funcoid, 0), 'f', "$sect: function stats do not exist"); -is(have_stats('relation', $dboid, $tableoid), +is(have_stats('relation', $dboid, $tableoid, 0), 'f', "$sect: relation stats do not exist"); @@ -130,10 +130,10 @@ append_file($og_stats, "XYZ"); $node->start; $sect = "invalid_append"; -is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist"); -is(have_stats('function', $dboid, $funcoid), +is(have_stats('database', $dboid, 0, 0), 'f', "$sect: db stats do not exist"); +is(have_stats('function', $dboid, $funcoid, 0), 'f', "$sect: function stats do not exist"); -is(have_stats('relation', $dboid, $tableoid), +is(have_stats('relation', $dboid, $tableoid, 0), 'f', "$sect: relation stats do not exist"); @@ -292,10 +292,10 @@ sub trigger_funcrel_stat sub have_stats { - my ($kind, $dboid, $objid) = @_; + my ($kind, $dboid, $objid, $relfile) = @_; return $node->safe_psql($connect_db, - "SELECT pg_stat_have_stats('$kind', $dboid, $objid)"); + "SELECT pg_stat_have_stats('$kind', $dboid, $objid, $relfile)"); } sub overwrite_file diff --git a/src/test/recovery/t/030_stats_cleanup_replica.pl b/src/test/recovery/t/030_stats_cleanup_replica.pl index 74b516cc7c..317df24c4f 100644 --- a/src/test/recovery/t/030_stats_cleanup_replica.pl +++ b/src/test/recovery/t/030_stats_cleanup_replica.pl @@ -179,9 +179,9 @@ sub test_standby_func_tab_stats_status my %stats; $stats{rel} = $node_standby->safe_psql($connect_db, - "SELECT pg_stat_have_stats('relation', $dboid, $tableoid)"); + "SELECT pg_stat_have_stats('relation', $dboid, $tableoid, 0)"); $stats{func} = $node_standby->safe_psql($connect_db, - "SELECT pg_stat_have_stats('function', $dboid, $funcoid)"); + "SELECT pg_stat_have_stats('function', $dboid, $funcoid, 0)"); is_deeply(\%stats, \%expected, "$sect: standby stats as expected"); @@ -194,7 +194,7 @@ sub test_standby_db_stats_status my ($connect_db, $dboid, $present) = @_; is( $node_standby->safe_psql( - $connect_db, "SELECT pg_stat_have_stats('database', $dboid, 0)"), + $connect_db, "SELECT pg_stat_have_stats('database', $dboid, 0, 0)"), $present, "$sect: standby db stats as expected"); } diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2b47013f11..e2010a7dc4 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2342,6 +2342,11 @@ pg_statio_all_tables| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS heap_blks_read, + (pg_stat_get_blocks_written(c.oid) + pg_stat_get_relfilenode_blocks_written(d.oid, + CASE + WHEN (c.reltablespace <> (0)::oid) THEN c.reltablespace + ELSE d.dattablespace + END, c.relfilenode)) AS heap_blks_written, pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit, i.idx_blks_read, i.idx_blks_hit, @@ -2349,7 +2354,8 @@ pg_statio_all_tables| SELECT c.oid AS relid, pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit, x.idx_blks_read AS tidx_blks_read, x.idx_blks_hit AS tidx_blks_hit - FROM ((((pg_class c + FROM pg_database d, + ((((pg_class c LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN LATERAL ( SELECT (sum((pg_stat_get_blocks_fetched(pg_index.indexrelid) - pg_stat_get_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read, @@ -2360,7 +2366,7 @@ pg_statio_all_tables| SELECT c.oid AS relid, (sum(pg_stat_get_blocks_hit(pg_index.indexrelid)))::bigint AS idx_blks_hit FROM pg_index WHERE (pg_index.indrelid = t.oid)) x ON (true)) - WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); + WHERE ((c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) AND (d.datname = current_database())); pg_statio_sys_indexes| SELECT relid, indexrelid, schemaname, @@ -2381,6 +2387,7 @@ pg_statio_sys_tables| SELECT relid, schemaname, relname, heap_blks_read, + heap_blks_written, heap_blks_hit, idx_blks_read, idx_blks_hit, @@ -2410,6 +2417,7 @@ pg_statio_user_tables| SELECT relid, schemaname, relname, heap_blks_read, + heap_blks_written, heap_blks_hit, idx_blks_read, idx_blks_hit, diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index 56771f83ed..6ae4c13376 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -1111,23 +1111,23 @@ ROLLBACK; -- pg_stat_have_stats behavior ---- -- fixed-numbered stats exist -SELECT pg_stat_have_stats('bgwriter', 0, 0); +SELECT pg_stat_have_stats('bgwriter', 0, 0, 0); pg_stat_have_stats -------------------- t (1 row) -- unknown stats kinds error out -SELECT pg_stat_have_stats('zaphod', 0, 0); +SELECT pg_stat_have_stats('zaphod', 0, 0, 0); ERROR: invalid statistics kind: "zaphod" -- db stats have objid 0 -SELECT pg_stat_have_stats('database', :dboid, 1); +SELECT pg_stat_have_stats('database', :dboid, 1, 0); pg_stat_have_stats -------------------- f (1 row) -SELECT pg_stat_have_stats('database', :dboid, 0); +SELECT pg_stat_have_stats('database', :dboid, 0, 0); pg_stat_have_stats -------------------- t @@ -1144,21 +1144,21 @@ select a from stats_test_tab1 where a = 3; 3 (1 row) -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t (1 row) -- pg_stat_have_stats returns false for dropped index with stats -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t (1 row) DROP index stats_test_idx1; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- f @@ -1174,14 +1174,14 @@ select a from stats_test_tab1 where a = 3; 3 (1 row) -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t (1 row) ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- f @@ -1196,7 +1196,7 @@ select a from stats_test_tab1 where a = 3; 3 (1 row) -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t @@ -1204,7 +1204,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); REINDEX index CONCURRENTLY stats_test_idx1; -- false for previous oid -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- f @@ -1212,7 +1212,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); -- true for new oid SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t @@ -1220,7 +1220,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); -- pg_stat_have_stats returns true for a rolled back drop index with stats BEGIN; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t @@ -1228,7 +1228,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); DROP index stats_test_idx1; ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); pg_stat_have_stats -------------------- t @@ -1513,7 +1513,7 @@ SELECT :io_sum_bulkwrite_strategy_extends_after > :io_sum_bulkwrite_strategy_ext (1 row) -- Test IO stats reset -SELECT pg_stat_have_stats('io', 0, 0); +SELECT pg_stat_have_stats('io', 0, 0, 0); pg_stat_have_stats -------------------- t diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index 7147cc2f89..a992737a34 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -539,12 +539,12 @@ ROLLBACK; -- pg_stat_have_stats behavior ---- -- fixed-numbered stats exist -SELECT pg_stat_have_stats('bgwriter', 0, 0); +SELECT pg_stat_have_stats('bgwriter', 0, 0, 0); -- unknown stats kinds error out -SELECT pg_stat_have_stats('zaphod', 0, 0); +SELECT pg_stat_have_stats('zaphod', 0, 0, 0); -- db stats have objid 0 -SELECT pg_stat_have_stats('database', :dboid, 1); -SELECT pg_stat_have_stats('database', :dboid, 0); +SELECT pg_stat_have_stats('database', :dboid, 1, 0); +SELECT pg_stat_have_stats('database', :dboid, 0, 0); -- pg_stat_have_stats returns true for committed index creation CREATE table stats_test_tab1 as select generate_series(1,10) a; @@ -552,40 +552,40 @@ CREATE index stats_test_idx1 on stats_test_tab1(a); SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset SET enable_seqscan TO off; select a from stats_test_tab1 where a = 3; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); -- pg_stat_have_stats returns false for dropped index with stats -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); DROP index stats_test_idx1; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); -- pg_stat_have_stats returns false for rolled back index creation BEGIN; CREATE index stats_test_idx1 on stats_test_tab1(a); SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset select a from stats_test_tab1 where a = 3; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); -- pg_stat_have_stats returns true for reindex CONCURRENTLY CREATE index stats_test_idx1 on stats_test_tab1(a); SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset select a from stats_test_tab1 where a = 3; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); REINDEX index CONCURRENTLY stats_test_idx1; -- false for previous oid -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); -- true for new oid SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); -- pg_stat_have_stats returns true for a rolled back drop index with stats BEGIN; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); DROP index stats_test_idx1; ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid, 0); -- put enable_seqscan back to on SET enable_seqscan TO on; @@ -759,7 +759,7 @@ SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_after SELECT :io_sum_bulkwrite_strategy_extends_after > :io_sum_bulkwrite_strategy_extends_before; -- Test IO stats reset -SELECT pg_stat_have_stats('io', 0, 0); +SELECT pg_stat_have_stats('io', 0, 0, 0); SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_pre_reset FROM pg_stat_io \gset SELECT pg_stat_reset_shared('io'); diff --git a/src/test/subscription/t/026_stats.pl b/src/test/subscription/t/026_stats.pl index 6b6a5b0b1b..89ebf5aa2c 100644 --- a/src/test/subscription/t/026_stats.pl +++ b/src/test/subscription/t/026_stats.pl @@ -290,7 +290,7 @@ $node_subscriber->safe_psql($db, qq(DROP SUBSCRIPTION $sub1_name)); # Subscription stats for sub1 should be gone is( $node_subscriber->safe_psql( - $db, qq(SELECT pg_stat_have_stats('subscription', 0, $sub1_oid))), + $db, qq(SELECT pg_stat_have_stats('subscription', 0, $sub1_oid, 0))), qq(f), qq(Subscription stats for subscription '$sub1_name' should be removed.)); @@ -309,7 +309,7 @@ DROP SUBSCRIPTION $sub2_name; # Subscription stats for sub2 should be gone is( $node_subscriber->safe_psql( - $db, qq(SELECT pg_stat_have_stats('subscription', 0, $sub2_oid))), + $db, qq(SELECT pg_stat_have_stats('subscription', 0, $sub2_oid, 0))), qq(f), qq(Subscription stats for subscription '$sub2_name' should be removed.)); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 171a7dd5d2..e0b26287f3 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2127,6 +2127,7 @@ PgStatShared_InjectionPoint PgStatShared_InjectionPointFixed PgStatShared_IO PgStatShared_Relation +PgStatShared_RelFileNode PgStatShared_ReplSlot PgStatShared_SLRU PgStatShared_Subscription -- 2.34.1