Re: ResourceOwner refactoring
Heikki Linnakangas <hlinnaka@iki.fi>
From: Heikki Linnakangas <hlinnaka@iki.fi>
To: Alexander Lakhin <exclusion@gmail.com>
Cc: Aleksander Alekseev <aleksander@timescale.com>,
"pgsql-hackers@postgresql.org" <pgsql-hackers@postgresql.org>,
Julien Rouhaud <rjuju123@gmail.com>, Robert Haas <robertmhaas@gmail.com>,
Peter Eisentraut <peter@eisentraut.org>, Andres Freund <andres@anarazel.de>
Date: 2024-02-02T19:32:36Z
Lists: pgsql-hackers
On 02/02/2024 11:00, Alexander Lakhin wrote: > Please try the following script: > mkdir /tmp/50m > sudo mount -t tmpfs -o size=50M tmpfs /tmp/50m > export PGDATA=/tmp/50m/tmpdb > > initdb > pg_ctl -l server.log start > > cat << 'EOF' | psql > CREATE TEMP TABLE t (a name, b name, c name, d name); > INSERT INTO t SELECT 'a', 'b', 'c', 'd' FROM generate_series(1, 1000) g; > > COPY t TO '/tmp/t.data'; > SELECT 'COPY t FROM ''/tmp/t.data''' FROM generate_series(1, 100) > \gexec > EOF > > which produces an unexpected error, a warning, and an assertion failure, > starting from b8bff07da: Fixed, thanks for the report! Comparing ExtendBufferedRelLocal() and ExtendBufferedRelShared(), it's easy to see that ExtendBufferedRelLocal() was missing a ResourceOwnerEnlarge() call in the loop. But it's actually a bit more subtle: it was correct without the ResourceOwnerEnlarge() call until commit b8bff07da, because ExtendBufferedRelLocal() unpins the old buffer pinning the new one, while ExtendBufferedRelShared() does it the other way 'round. The implicit assumption was that unpinning the old buffer ensures that you can pin a new one. That no longer holds with commit b8bff07da. Remembering a new resource expects there to be a free slot in the fixed-size array, but if the forgotten resource was in the hash, rather than the array, forgetting it doesn't make space in the array. We also make that assumption here in BufferAlloc: > /* > * Got a collision. Someone has already done what we were about to do. > * We'll just handle this as if it were found in the buffer pool in > * the first place. First, give up the buffer we were planning to > * use. > * > * We could do this after releasing the partition lock, but then we'd > * have to call ResourceOwnerEnlarge() & ReservePrivateRefCountEntry() > * before acquiring the lock, for the rare case of such a collision. > */ > UnpinBuffer(victim_buf_hdr); It turns out to be OK in that case, because it unpins the buffer that was the last one pinned. That does ensure that you have one free slot in the array, but forgetting anything other than the most recently remembered resource does not. I've added a note to that in ResourceOwnerForget. I read through the other callers of ResourceOwnerRemember and PinBuffer, but didn't find any other unsafe uses. I'm not too happy with this subtlety, but at least it's documented now. -- Heikki Linnakangas Neon (https://neon.tech)
Commits
-
Make RelationFlushRelation() work without ResourceOwner during abort
- e6cd85772647 17.0 landed
-
Fix bug in bulk extending temp relation after failure
- d212957254de 17.0 landed
-
Add missing PGDLLIMPORT markings
- c6b86eaa55ff 17.0 landed
-
Add test_dsa module.
- 325f54033e59 17.0 landed
-
Clear CurrentResourceOwner earlier in CommitTransaction.
- c21e6e2fd48c 17.0 landed
-
Fix dsa.c with different resource owners.
- a8b330ffb6f7 17.0 landed
-
Fix bug in the new ResourceOwner implementation.
- 8f4a1ab471e6 17.0 landed
-
Change pgcrypto to use the new ResourceOwner mechanism.
- cd694f60dc97 17.0 landed
-
Use a faster hash function in resource owners.
- 954e43564d99 17.0 landed
-
Make ResourceOwners more easily extensible.
- b8bff07daa85 17.0 landed
-
Move a few ResourceOwnerEnlarge() calls for safety and clarity.
- b70c2143bbbe 17.0 landed