Re: Split index and table statistics into different types of stats

From: Bertrand Drouvot <bertranddrouvot(dot)pg(at)gmail(dot)com>
To: Michael Paquier <michael(at)paquier(dot)xyz>
Cc: Andres Freund <andres(at)anarazel(dot)de>, Daniel Gustafsson <daniel(at)yesql(dot)se>, "Gregory Stark (as CFM)" <stark(dot)cfm(at)gmail(dot)com>, vignesh C <vignesh21(at)gmail(dot)com>, Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com>, Melanie Plageman <melanieplageman(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Split index and table statistics into different types of stats
Date: 2026-08-13 04:29:35
Message-ID: an1IL5eBZZ1kU4u1@bdtpg
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

On Thu, Aug 13, 2026 at 07:34:37AM +0900, Michael Paquier wrote:
> On Wed, Aug 12, 2026 at 11:52:16AM +0000, Bertrand Drouvot wrote:
> > I wonder if we could keep the single pointer design while using a small common
> > header in separate table and index pending structures? That would allow
> > Relation.pgstat_info to point to the common header while using a different
> > pending_size for each kind.
>
> TBH, I find this a bit unattractive because it reduces code clarity.
> There is a bit of memory wasted for pending index data for indexes due
> to the fact that the union's size is calculated based on its largest
> number.

Yeah that was my point.

> It's nothing new, the split just makes cleaner the handling
> of the fields, in terms of which stats kind can touch each part.

Out of curiosity, I did a quick check to see the difference.

On my machine that gives:

(gdb) p sizeof(PgStat_RelationStatus)
$1 = 144
(gdb) p sizeof(((PgStat_RelationStatus *) 0)->tab)
$2 = 128
(gdb) p sizeof(PgStat_IndexCounts)
$3 = 40
(gdb) p (size_t) &((PgStat_RelationStatus *) 0)->idx
$4 = 16

So, at the struct level an index specific would need 56 bytes instead of 144 bytes.

For allocations:

(gdb) set $idx_size = (size_t) &((PgStat_RelationStatus *) 0)->idx + sizeof(PgStat_IndexCounts)
(gdb) p $idx_size
$5 = 56

(gdb) set $ctx = (MemoryContext) AllocSetContextCreateInternal(TopMemoryContext, "gdb pgstat", 0, 1024, 8192)
(gdb) set $current = (void *) MemoryContextAlloc($ctx, sizeof(PgStat_RelationStatus))
(gdb) set $split = (void *) MemoryContextAlloc($ctx, $idx_size)

(gdb) p GetMemoryChunkSpace($current)
$6 = 272
(gdb) p GetMemoryChunkSpace($split)
$7 = 80

So, 272 bytes for the current structure and 80 bytes for the index specific one,
including allocation overhead. That's 192 bytes of additional allocated space
per pending index entry.

1000 pending index entries would lead to about 188 KB per backend.
Given that this is not a new cost, I agree that it is probably not worth reducing
the code clarity for this.

> Updated patch attached.

Thanks! LGTM, just one "nit" comment:

PgStat_RelationStatus *
find_relstat_entry_kind(PgStat_Kind kind, Oid rel_id)
.
.
bla bla
.
.
/*
* For index entries, just return the copy. There is no transactional
* data.
*/

So, index entries return the copy unchanged, worth to modify this comment on top
of the function then?

"
* If an entry is found, copy it and increment the copy's counters with their
"

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Fujii Masao 2026-08-13 04:29:44 Re: pgbench --continue-on-error: clarify TPS and failure reporting
Previous Message Michael Paquier 2026-08-13 04:16:19 Re: Allow a condition string in an injection point