Re: Confine vacuum skip logic to lazy_scan_skip

Поиск
Список
Период
Сортировка
От Heikki Linnakangas
Тема Re: Confine vacuum skip logic to lazy_scan_skip
Дата
Msg-id 38905342-2d61-4095-923b-c9665f7201da@iki.fi
обсуждение исходный текст
Ответ на Re: Confine vacuum skip logic to lazy_scan_skip  (Melanie Plageman <melanieplageman@gmail.com>)
Ответы Re: Confine vacuum skip logic to lazy_scan_skip  (Melanie Plageman <melanieplageman@gmail.com>)
Список pgsql-hackers
On 11/03/2024 18:15, Melanie Plageman wrote:
> On Mon, Mar 11, 2024 at 11:29:44AM +0200, Heikki Linnakangas wrote:
>> diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
>> index ac55ebd2ae..1757eb49b7 100644
>> --- a/src/backend/access/heap/vacuumlazy.c
>> +++ b/src/backend/access/heap/vacuumlazy.c
>> +
> 
>>   /*
>> - *    lazy_scan_skip() -- set up range of skippable blocks using visibility map.
>> + *    heap_vac_scan_next_block() -- get next block for vacuum to process
>>    *
>> - * lazy_scan_heap() calls here every time it needs to set up a new range of
>> - * blocks to skip via the visibility map.  Caller passes the next block in
>> - * line.  We return a next_unskippable_block for this range.  When there are
>> - * no skippable blocks we just return caller's next_block.  The all-visible
>> - * status of the returned block is set in *next_unskippable_allvis for caller,
>> - * too.  Block usually won't be all-visible (since it's unskippable), but it
>> - * can be during aggressive VACUUMs (as well as in certain edge cases).
>> + * lazy_scan_heap() calls here every time it needs to get the next block to
>> + * prune and vacuum.  The function uses the visibility map, vacuum options,
>> + * and various thresholds to skip blocks which do not need to be processed and
>> + * sets blkno to the next block that actually needs to be processed.
> 
> I wonder if "need" is too strong a word since this function
> (heap_vac_scan_next_block()) specifically can set blkno to a block which
> doesn't *need* to be processed but which it chooses to process because
> of SKIP_PAGES_THRESHOLD.

Ok yeah, there's a lot of "needs" here :-). Fixed.

>>    *
>> - * Sets *skipping_current_range to indicate if caller should skip this range.
>> - * Costs and benefits drive our decision.  Very small ranges won't be skipped.
>> + * The block number and visibility status of the next block to process are set
>> + * in *blkno and *all_visible_according_to_vm.  The return value is false if
>> + * there are no further blocks to process.
>> + *
>> + * vacrel is an in/out parameter here; vacuum options and information about
>> + * the relation are read, and vacrel->skippedallvis is set to ensure we don't
>> + * advance relfrozenxid when we have skipped vacuuming all-visible blocks.  It
> 
> Maybe this should say when we have skipped vacuuming all-visible blocks
> which are not all-frozen or just blocks which are not all-frozen.

Ok, rephrased.

>> + * also holds information about the next unskippable block, as bookkeeping for
>> + * this function.
>>    *
>>    * Note: our opinion of which blocks can be skipped can go stale immediately.
>>    * It's okay if caller "misses" a page whose all-visible or all-frozen marking
> 
> Wonder if it makes sense to move this note to
> find_next_nunskippable_block().

Moved.

>> @@ -1098,26 +1081,119 @@ lazy_scan_heap(LVRelState *vacrel)
>>    * older XIDs/MXIDs.  The vacrel->skippedallvis flag will be set here when the
>>    * choice to skip such a range is actually made, making everything safe.)
>>    */
>> -static BlockNumber
>> -lazy_scan_skip(LVRelState *vacrel, Buffer *vmbuffer, BlockNumber next_block,
>> -               bool *next_unskippable_allvis, bool *skipping_current_range)
>> +static bool
>> +heap_vac_scan_next_block(LVRelState *vacrel, BlockNumber *blkno,
>> +                         bool *all_visible_according_to_vm)
>>   {
>> -    BlockNumber rel_pages = vacrel->rel_pages,
>> -                next_unskippable_block = next_block,
>> -                nskippable_blocks = 0;
>> -    bool        skipsallvis = false;
>> +    BlockNumber next_block;
>>   
>> -    *next_unskippable_allvis = true;
>> -    while (next_unskippable_block < rel_pages)
>> +    /* relies on InvalidBlockNumber + 1 overflowing to 0 on first call */
>> +    next_block = vacrel->current_block + 1;
>> +
>> +    /* Have we reached the end of the relation? */
> 
> No strong opinion on this, but I wonder if being at the end of the
> relation counts as a fourth state?

Yeah, perhaps. But I think it makes sense to treat it as a special case.

>> +    if (next_block >= vacrel->rel_pages)
>> +    {
>> +        if (BufferIsValid(vacrel->next_unskippable_vmbuffer))
>> +        {
>> +            ReleaseBuffer(vacrel->next_unskippable_vmbuffer);
>> +            vacrel->next_unskippable_vmbuffer = InvalidBuffer;
>> +        }
>> +        *blkno = vacrel->rel_pages;
>> +        return false;
>> +    }
>> +
>> +    /*
>> +     * We must be in one of the three following states:
>> +     */
>> +    if (vacrel->next_unskippable_block == InvalidBlockNumber ||
>> +        next_block > vacrel->next_unskippable_block)
>> +    {
>> +        /*
>> +         * 1. We have just processed an unskippable block (or we're at the
>> +         * beginning of the scan).  Find the next unskippable block using the
>> +         * visibility map.
>> +         */
> 
> I would reorder the options in the comment or in the if statement since
> they seem to be in the reverse order.

Reordered them in the statement.

It feels a bit wrong to test next_block > vacrel->next_unskippable_block 
before vacrel->next_unskippable_block == InvalidBlockNumber. But it 
works, and that order makes more sense in the comment IMHO.

>> +        bool        skipsallvis;
>> +
>> +        find_next_unskippable_block(vacrel, &skipsallvis);
>> +
>> +        /*
>> +         * We now know the next block that we must process.  It can be the
>> +         * next block after the one we just processed, or something further
>> +         * ahead.  If it's further ahead, we can jump to it, but we choose to
>> +         * do so only if we can skip at least SKIP_PAGES_THRESHOLD consecutive
>> +         * pages.  Since we're reading sequentially, the OS should be doing
>> +         * readahead for us, so there's no gain in skipping a page now and
>> +         * then.  Skipping such a range might even discourage sequential
>> +         * detection.
>> +         *
>> +         * This test also enables more frequent relfrozenxid advancement
>> +         * during non-aggressive VACUUMs.  If the range has any all-visible
>> +         * pages then skipping makes updating relfrozenxid unsafe, which is a
>> +         * real downside.
>> +         */
>> +        if (vacrel->next_unskippable_block - next_block >= SKIP_PAGES_THRESHOLD)
>> +        {
>> +            next_block = vacrel->next_unskippable_block;
>> +            if (skipsallvis)
>> +                vacrel->skippedallvis = true;
>> +        }
> 
>> +
>> +/*
>> + * Find the next unskippable block in a vacuum scan using the visibility map.
> 
> To expand this comment, I might mention it is a helper function for
> heap_vac_scan_next_block(). I would also say that the next unskippable
> block and its visibility information are recorded in vacrel. And that
> skipsallvis is set to true if any of the intervening skipped blocks are
> not all-frozen.

Added comments.

> Otherwise LGTM

Ok, pushed! Thank you, this is much more understandable now!

-- 
Heikki Linnakangas
Neon (https://neon.tech)



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

Предыдущее
От: Matthias van de Meent
Дата:
Сообщение: Re: btree: downlink right separator/HIKEY optimization
Следующее
От: Stephen Frost
Дата:
Сообщение: Re: Statistics Import and Export