Re: Is this a problem in GenericXLogFinish()?

From: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
To: Jeff Davis <pgsql(at)j-davis(dot)com>, Robert Haas <robertmhaas(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: Is this a problem in GenericXLogFinish()?
Date: 2023-10-11 11:53:02
Message-ID: bf396b45-318a-4940-87eb-3d295a93da8b@iki.fi
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 10/10/2023 22:57, Jeff Davis wrote:
> On Thu, 2023-09-28 at 12:05 -0700, Jeff Davis wrote:
>> Also, I ran into some problems with GIN that might require a bit more
>> refactoring in ginPlaceToPage(). Perhaps we could consider
>> REGBUF_NO_CHANGE a general bypass of the Assert(BufferIsDirty()), and
>> use it temporarily until we have a chance to analyze/refactor each of
>> the callers that need it.
>
> For the Assert, I have attached a new patch for v17.

Thanks for working on this!

> +/*
> + * BufferIsDirty
> + *
> + * Checks if buffer is already dirty.
> + *
> + * Buffer must be pinned and exclusive-locked. (If caller does not hold
> + * exclusive lock, then the result may be stale before it's returned.)
> + */
> +bool
> +BufferIsDirty(Buffer buffer)
> +{
> + BufferDesc *bufHdr;
> +
> + if (BufferIsLocal(buffer))
> + {
> + int bufid = -buffer - 1;
> + bufHdr = GetLocalBufferDescriptor(bufid);
> + }
> + else
> + {
> + bufHdr = GetBufferDescriptor(buffer - 1);
> + }
> +
> + Assert(BufferIsPinned(buffer));
> + Assert(LWLockHeldByMeInMode(BufferDescriptorGetContentLock(bufHdr),
> + LW_EXCLUSIVE));
> +
> + return pg_atomic_read_u32(&bufHdr->state) & BM_DIRTY;
> +}
The comment suggests that you don't need to hold an exclusive lock when
you call this, but there's an assertion that you do.

Is it a new requirement that you must hold an exclusive lock on the
buffer when you call XLogRegisterBuffer()? I think that's reasonable.

I thought if that might be a problem when you WAL-log a page when you
set hint bits on it and checksums are enabled. Hint bits can be set
holding just a share lock. But it uses XLogSaveBufferForHint(), which
calls XLogRegisterBlock() instead of XLogRegisterBuffer()

There is a comment above XLogSaveBufferForHint() that implies that
XLogRegisterBuffer() requires you to hold an exclusive-lock but I don't
see that spelled out explicitly in XLogRegisterBuffer() itself. Maybe
add an assertion for that in XLogRegisterBuffer() to make it more explicit.

> --- a/src/backend/access/transam/xloginsert.c
> +++ b/src/backend/access/transam/xloginsert.c
> @@ -246,6 +246,7 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
>
> /* NO_IMAGE doesn't make sense with FORCE_IMAGE */
> Assert(!((flags & REGBUF_FORCE_IMAGE) && (flags & (REGBUF_NO_IMAGE))));
> + Assert((flags & REGBUF_CLEAN_OK) || BufferIsDirty(buffer));
> Assert(begininsert_called);
>
> if (block_id >= max_registered_block_id)

I'd suggest a comment here to explain what's wrong if someone hits this
assertion. Something like "The buffer must be marked as dirty before
registering, unless REGBUF_CLEAN_OK is set to indicate that the buffer
is not being modified".

> I changed the name of the flag to REGBUF_CLEAN_OK, because for some of
> the callsites it was not clear to me whether the page is always
> unchanged or sometimes unchanged. In other words, REGBUF_CLEAN_OK is a
> way to bypass the Assert for callsites where we either know that we
> actually want to register an unchanged page, or for callsites where we
> don't know if the page is changed or not.

Ok. A flag to explicitly mark that the page is not modified would
actually be nice for pg_rewind. It could ignore such page references.
Not that it matters much in practice, since those records are so rare.
And for that, we'd need to include the flag in the WAL record too. So I
think this is fine.

> If we commit this, ideally someone would look into the places where
> REGBUF_CLEAN_OK is used and make sure that it's not a bug, and perhaps
> refactor so that it would benefit from the Assert. But refactoring
> those places is outside of the scope of this patch.

About that: you added the flag to a couple of XLogRegisterBuffer() calls
in GIN, because they call MarkBufferDirty() only after
XLogRegisterBuffer(). Those would be easy to refactor so I'd suggest
doing that now.

> It sounds like we have no intention to change the hash index code, so
> are we OK if this flag just lasts forever? Do you still think it offers
> a useful check?

Yeah, I think this is a useful assertion. It might catch some bugs in
extensions too.

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

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Robert Haas 2023-10-11 12:25:12 Re: Is this a problem in GenericXLogFinish()?
Previous Message Heikki Linnakangas 2023-10-11 11:12:47 Re: Refactoring backend fork+exec code