Re: Strangeness in xid allocation / snapshot setup

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: "Vadim Mikheev" <vmikheev(at)sectorbase(dot)com>
Cc: pgsql-hackers(at)postgreSQL(dot)org
Subject: Re: Strangeness in xid allocation / snapshot setup
Date: 2001-07-12 14:47:21
Message-ID: 17046.994949241@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

"Vadim Mikheev" <vmikheev(at)sectorbase(dot)com> writes:
> You forget about Tx Old! The point is that changes made by Tx Old *over*
> Tx New' changes effectively make those Tx New' changes *visible* to
> Tx S!

Yes, but what's that got to do with the order of operations in
GetSnapshotData? The scenario you describe can occur anyway.
Only if Tx Old is running in Read Committed mode, of course.
But if it is, then it's capable of deciding to update a row updated
by Tx New. Whether Tx S's xmax value is before or after Tx New's ID
is not going to change the behavior of Tx Old.

> And this is how it worked (MyProc->xid was updated while holding
> XXXGenLockId) in varsup.c from version 1.21 (Jun 1999) till
> version 1.36 (Mar 2001) when you occasionally moved it outside
> of locked code part:

Okay, so that part was my error. I've changed it back.

I'd still like to change GetSnapshotData to read the nextXid before
it acquires SInvalLock, though. If we did that, it'd be safe to make
GetNewTransactionId be

SpinAcquire(XidGenLockId);
xid = nextXid++;
SpinAcquire(SInvalLockId);
MyProc->xid = xid;
SpinRelease(SInvalLockId);
SpinRelease(XidGenLockId);

which is really necessary if you want to avoid assuming that
TransactionIds can be fetched and stored atomically.

Two other changes I think are needed in this area:

* In AbortTransaction, the clearing of MyProc->xid and MyProc->xmin
should be moved down to after RecordTransactionAbort and surrounded
by acquire/release SInvalLock (to avoid atomic fetch/store assumption).

* In HeapTupleSatisfiesVacuum (new tqual.c routine I just made
yesterday, by extracting the tuple time qual checks from vacuum.c),
the order of checking for process status should be
TransactionIdIsInProgress
TransactionIdDidCommit
TransactionIdDidAbort
not the present
TransactionIdDidAbort
TransactionIdDidCommit
TransactionIdIsInProgress

The current way is wrong because if the other process is just in process
of committing, we can get

VACUUM other

TransactionIdDidAbort - no
TransactionIdDidCommit - no

RecordTransactionCommit();
MyProc->xid = 0;

TransactionIdIsInProgress - no

whereupon vacuum decides that the other process crashed --- oops. If
we test TransactionIdIsInProgress *first* in tqual, and xact.c records
commit or abort *before* clearing MyProc->xid, then we cannot have this
race condition where the xact is no longer considered in progress but
not seen to be committed/aborted either.

(Note: this bug is not a problem for existing VACUUM, since it can
never see any tuples from open transactions anyway. But it will be
fatal for concurrent VACUUM.)

Comments?

regards, tom lane

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Peter Eisentraut 2001-07-12 14:51:39 Re: [PATCHES] Re: [PATCH] Re: Setuid functions
Previous Message Bruce Momjian 2001-07-12 14:27:33 Re: Child itemid in update-chain marked as unused - can't continue repair_frag