On 2013-07-19 13:46:44 -0400, Alvaro Herrera wrote: > levert...@googlemail.com wrote: > > > START TRANSACTION ISOLATION LEVEL SERIALIZABLE; > > CREATE TABLE testing( > > x INTEGER PRIMARY KEY > > ); > > INSERT INTO testing VALUES(1); > > SELECT * FROM testing WHERE x = 1 FOR UPDATE; > > SAVEPOINT test; > > UPDATE testing SET x = 2 WHERE x = 1; > > ROLLBACK TO test; > > UPDATE testing SET x = 3 WHERE x = 1; > > ROLLBACK; > > Thanks for the test case. This one-liner patch fixes it: > > diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c > index 55563ea..fa9c9d7 100644 > --- a/src/backend/utils/time/tqual.c > +++ b/src/backend/utils/time/tqual.c > @@ -1287,7 +1287,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, > TransactionId OldestXmin, > { > if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid > invalid */ > return HEAPTUPLE_INSERT_IN_PROGRESS; > - if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) > + if (HeapTupleHeaderIsOnlyLocked(tuple)) > return HEAPTUPLE_INSERT_IN_PROGRESS; > /* inserted and then deleted by same xact */ > return HEAPTUPLE_DELETE_IN_PROGRESS; > > > That is, I was taking a shortcut here by checking only the infomask bits > about locking, and not resolving the MultiXact to see if the updater > (sub)transaction was aborted; but this was wrong, because a tuple which > was created here and updated by an aborted subxact needs to be seen as > in-progress insertion, not an in-progress delete. This causes trouble > to the predicate.c code because it tries to obtain the update XID for > the tuple, but since the update has aborted, that returned zero, causing > the assert failure.
> Sadly, this has performance implications, because what previously was > just an in-place check of bit flags has now become a function call. Well, the impact imo primarily comes from actually resolving the multixact, not from the function call itself... But I don't think we need to overly worried. That path is only entered if xmin is in-progress, so that shouldn't have too big implications. If you're worried you could do a SetHintBits(HEAP_XMAX_INVALID) like it's done if xmin isn't in progress like it's done when xmin has committed. > Perhaps it'd be smart to optimize it a bit so that we first check the > flags, and only call the function if that fails. Sounds like a good idea to me. The duplicated amount of work by that should by fairly minimal. Greetings, Andres Freund -- Andres Freund http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services -- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs