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

Поиск
Список
Период
Сортировка
От Drouvot, Bertrand
Тема Re: Split index and table statistics into different types of stats
Дата
Msg-id 9d3b27e7-869d-40f7-8807-8406c9d7030e@gmail.com
обсуждение исходный текст
Ответ на Re: Split index and table statistics into different types of stats  (Andres Freund <andres@anarazel.de>)
Ответы Re: Split index and table statistics into different types of stats  (Bertrand Drouvot <bertranddrouvot.pg@gmail.com>)
Список pgsql-hackers
Hi,

On 11/13/23 9:44 PM, Andres Freund wrote:
> Hi,
> 
> On 2023-11-13 09:26:56 +0100, Drouvot, Bertrand wrote:
>> --- a/src/backend/storage/buffer/bufmgr.c
>> +++ b/src/backend/storage/buffer/bufmgr.c
>> @@ -799,11 +799,19 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
>>        * Read the buffer, and update pgstat counters to reflect a cache hit or
>>        * miss.
>>        */
>> -    pgstat_count_buffer_read(reln);
>> +    if (reln->rd_rel->relkind == RELKIND_INDEX)
>> +        pgstat_count_index_buffer_read(reln);
>> +    else
>> +        pgstat_count_table_buffer_read(reln);
> 
> It's not nice from a layering POV that we need this level of awareness in
> bufmgr.c.  I wonder if this is an argument for first splitting out stats like
> blocks_hit, blocks_fetched into something like "relfilenode stats" - they're
> agnostic of the relkind. 

Thanks for looking at it! Yeah I think that would make a lot of sense
to track some stats per relfilenode.

> There aren't that many such stats right now,
> admittedly, but I think we'll want to also track dirtied, written blocks on a
> per relation basis once we can (i.e. we key the relevant stats by relfilenode
> instead of oid, so we can associate stats when writing out buffers).
> 
> 

Agree. Then, I think that would make sense to start this effort before the
split index/table one. I can work on a per relfilenode stat patch first.

Does this patch ordering make sense to you?

1) Introduce per relfilenode stats
2) Split index and table stats

>> +/*
>> + * Initialize a relcache entry to count access statistics.  Called whenever an
>> + * index is opened.
>> + *
>> + * We assume that a relcache entry's pgstatind_info field is zeroed by relcache.c
>> + * when the relcache entry is made; thereafter it is long-lived data.
>> + *
>> + * This does not create a reference to a stats entry in shared memory, nor
>> + * allocate memory for the pending stats. That happens in
>> + * pgstat_assoc_index().
>> + */
>> +void
>> +pgstat_init_index(Relation rel)
>> +{
>> +    /*
>> +     * We only count stats for indexes
>> +     */
>> +    Assert(rel->rd_rel->relkind == RELKIND_INDEX);
>> +
>> +    if (!pgstat_track_counts)
>> +    {
>> +        if (rel->pgstatind_info != NULL)
>> +            pgstat_unlink_index(rel);
>> +
>> +        /* We're not counting at all */
>> +        rel->pgstat_enabled = false;
>> +        rel->pgstatind_info = NULL;
>> +        return;
>> +    }
>> +
>> +    rel->pgstat_enabled = true;
>> +}
>> +
>> +/*
>> + * Prepare for statistics for this index to be collected.
>> + *
>> + * This ensures we have a reference to the stats entry before stats can be
>> + * generated. That is important because an index drop in another
>> + * connection could otherwise lead to the stats entry being dropped, which then
>> + * later would get recreated when flushing stats.
>> + *
>> + * This is separate from pgstat_init_index() as it is not uncommon for
>> + * relcache entries to be opened without ever getting stats reported.
>> + */
>> +void
>> +pgstat_assoc_index(Relation rel)
>> +{
>> +    Assert(rel->pgstat_enabled);
>> +    Assert(rel->pgstatind_info == NULL);
>> +
>> +    /* Else find or make the PgStat_IndexStatus entry, and update link */
>> +    rel->pgstatind_info = pgstat_prep_index_pending(RelationGetRelid(rel),
>> +                                                    rel->rd_rel->relisshared);
>> +
>> +    /* don't allow link a stats to multiple relcache entries */
>> +    Assert(rel->pgstatind_info->relation == NULL);
>> +
>> +    /* mark this relation as the owner */
>> +    rel->pgstatind_info->relation = rel;
>> +}
>> +
>> +/*
>> + * Break the mutual link between a relcache entry and pending index stats entry.
>> + * This must be called whenever one end of the link is removed.
>> + */
>> +void
>> +pgstat_unlink_index(Relation rel)
>> +{
>> +
>> +    if (rel->pgstatind_info == NULL)
>> +        return;
>> +
>> +    /* link sanity check for the index stats */
>> +    if (rel->pgstatind_info)
>> +    {
>> +        Assert(rel->pgstatind_info->relation == rel);
>> +        rel->pgstatind_info->relation = NULL;
>> +        rel->pgstatind_info = NULL;
>> +    }
>> +}
>> ...
> 
> This is a fair bit of duplicated code - perhaps we could have shared helpers?
> 

Yeah, I had it in mind and that was part of the "Will now work on addressing the
up-thread remaining comments" remark I made up-thread.

> 
>> +/* ----------
>> + * PgStat_IndexStatus            Per-index status within a backend
>> + *
>> + * Many of the event counters are nontransactional, ie, we count events
>> + * in committed and aborted transactions alike.  For these, we just count
>> + * directly in the PgStat_IndexStatus.
>> + * ----------
>> + */
>> +typedef struct PgStat_IndexStatus
>> +{
>> +    Oid            r_id;            /* relation's OID */
>> +    bool        r_shared;        /* is it a shared catalog? */
>> +    struct PgStat_IndexXactStatus *trans;    /* lowest subxact's counts */
>> +    PgStat_IndexCounts counts;    /* event counts to be sent */
>> +    Relation    relation;        /* rel that is using this entry */
>> +} PgStat_IndexStatus;
>> +
>>   /* ----------
>>    * PgStat_TableXactStatus        Per-table, per-subtransaction status
>>    * ----------
>> @@ -227,6 +264,29 @@ typedef struct PgStat_TableXactStatus
>>   } PgStat_TableXactStatus;
>>   
>>   
>> +/* ----------
>> + * PgStat_IndexXactStatus        Per-index, per-subtransaction status
>> + * ----------
>> + */
>> +typedef struct PgStat_IndexXactStatus
>> +{
>> +    PgStat_Counter tuples_inserted; /* tuples inserted in (sub)xact */
>> +    PgStat_Counter tuples_updated;    /* tuples updated in (sub)xact */
>> +    PgStat_Counter tuples_deleted;    /* tuples deleted in (sub)xact */
>> +    bool        truncdropped;    /* relation truncated/dropped in this
>> +                                 * (sub)xact */
>> +    /* tuples i/u/d prior to truncate/drop */
>> +    PgStat_Counter inserted_pre_truncdrop;
>> +    PgStat_Counter updated_pre_truncdrop;
>> +    PgStat_Counter deleted_pre_truncdrop;
>> +    int            nest_level;        /* subtransaction nest level */
>> +    /* links to other structs for same relation: */
>> +    struct PgStat_IndexXactStatus *upper;    /* next higher subxact if any */
>> +    PgStat_IndexStatus *parent; /* per-table status */
>> +    /* structs of same subxact level are linked here: */
>> +    struct PgStat_IndexXactStatus *next;    /* next of same subxact */
>> +} PgStat_IndexXactStatus;
> 
> I don't think much of this is used? It doesn't look like you're using most of
> the fields. Which makes sense - there's not really the same transactional
> behaviour for indexes as there is for tables.
> 
> 

Fully agree. I had in mind to revisit this stuff too.

Regards,

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



В списке pgsql-hackers по дате отправления:

Предыдущее
От: Bharath Rupireddy
Дата:
Сообщение: Re: add log messages when replication slots become active and inactive (was Re: Is it worth adding ReplicationSlot active_pid to ReplicationSlotPersistentData?)
Следующее
От: Dilip Kumar
Дата:
Сообщение: Re: trying again to get incremental backup