Hackers,

Clearly it's not going to help to all the nested transactions project if
we don't make clear the concept of a snapshot.

In the current implementation, it's sufficient to know
a) what transactions come before me (Xmin),
b) what transactions come after me (Xmax),
c) what transactions are in progress (xip), and
d) what commands come before me in the current transaction
   (curcid)

In the nested transactions case, we also need to know

e) what subtransactions of my own parent transactions come before me,
   and
f) what commands of my parent transactions come before me.


Consider the following scenario:

BEGIN;						xid=1
CREATE TABLE a (p int UNIQUE, q int);		xid=1	cid=1
INSERT INTO a (p) VALUES (1);			xid=1	cid=2
BEGIN;						xid=2
	-- should fail due to unique constraint
	INSERT INTO a (p) VALUES (1);		xid=2	cid=1
ROLLBACK;
BEGIN;						xid=3
	INSERT INTO a (p) VALUES (2);			cid=1
	DELETE FROM a WHERE one=1;			cid=2
	-- "a" should have 1 tuple
COMMIT;
-- should work, because the old tuple doesn't exist anymore
INSERT INTO a (p) VALUES (1);			xid=1	cid=3
COMMIT;

Here, the QuerySnapshot of xid 1, at the time of cid=3 should see the
results of execution from xid 3, but it is not before Xmin, and it's
after Xmax, and is not in the xip array.

Also, the QuerySnapshot of xid 3, should see the results of commands
from xid 1 just like they'd be seen if they where in the same xact but
with a lesser CommandId.

Both cases are not implementable with the current notion of a Snapshot.

I propose that the SnapshotData struct is expanded as:

typedef struct SnapshotData
{
    TransactionId xmin;         /* XID < xmin are visible to me */
    TransactionId xmax;         /* XID >= xmax are invisible to me */
    uint32      xcnt;           /* # of xact ids in xip[] */
    TransactionId *xip;         /* array of xact IDs in progress */
    /* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */
    CommandId   curcid;         /* in my xact, CID < curcid are visible */
    ItemPointerData tid;        /* required for Dirty snapshot -:( */
	/* new members below */
    uint32	childcnt;	/* # of xact ids in childcnt[] */
    TransactionId *childxact;	/* array of completed child xact IDs */
    uint32      parentcnt;	/* # of xact ids in parentcnt[] */
    TransactionId *parentxact;   /* array of parent xact IDs */
} SnapshotData;

And then we will have to rewrite the HeapTupleSatisfies* routines to act
according to this new Snapshot structure, and also with the pg_subtrans
mechanism to know which subtransactions were aborted.

When starting a subtransaction, the child will have the parent's XID in
parentxact, and all the XID's the parent has in parentxact as well.
When ending a subtransaction, the parent will record its XID in
childxact, and all the XID's the child had in childxact.

It's not necessary to keep the parent's CommandId, because there are no
future CommandIds recorded in any tuple (no need to check).


I'm not sure what the SerializableSnapshot should be.  It does need to
take into account the changes made by previous committed
subtransactions, right?

It's also clear that we need to differentiate a parent's QuerySnapshot
from their child's.  It's not clear to me what should be done in the
case of a SerializableSnapshot.

-- 
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"The Postgresql hackers have what I call a "NASA space shot" mentality.
Quite refreshing in a world of "weekend drag racer" developers."
(Scott Marlowe)


Sun, 11 May 2003 19:29:27 -0400
Alvaro Herrera <alvherre@dcc.uchile.cl>
What is a snapshot