Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3103f9a

Browse files
committed
The row-version chaining in Serializable Snapshot Isolation was still wrong.
On further analysis, it turns out that it is not needed to duplicate predicate locks to the new row version at update, the lock on the version that the transaction saw as visible is enough. However, there was a different bug in the code that checks for dangerous structures when a new rw-conflict happens. Fix that bug, and remove all the row-version chaining related code. Kevin Grittner & Dan Ports, with some comment editorialization by me.
1 parent 5177dfe commit 3103f9a

File tree

7 files changed

+150
-150
lines changed

7 files changed

+150
-150
lines changed

src/backend/access/heap/heapam.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,7 +1529,6 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
15291529
OffsetNumber offnum;
15301530
bool at_chain_start;
15311531
bool valid;
1532-
bool match_found;
15331532

15341533
if (all_dead)
15351534
*all_dead = true;
@@ -1539,7 +1538,6 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
15391538
Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
15401539
offnum = ItemPointerGetOffsetNumber(tid);
15411540
at_chain_start = true;
1542-
match_found = false;
15431541

15441542
/* Scan through possible multiple members of HOT-chain */
15451543
for (;;)
@@ -1597,10 +1595,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
15971595
PredicateLockTuple(relation, &heapTuple);
15981596
if (all_dead)
15991597
*all_dead = false;
1600-
if (IsolationIsSerializable())
1601-
match_found = true;
1602-
else
1603-
return true;
1598+
return true;
16041599
}
16051600

16061601
/*
@@ -1629,7 +1624,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
16291624
break; /* end of chain */
16301625
}
16311626

1632-
return match_found;
1627+
return false;
16331628
}
16341629

16351630
/*
@@ -2855,12 +2850,6 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
28552850

28562851
END_CRIT_SECTION();
28572852

2858-
/*
2859-
* Any existing SIREAD locks on the old tuple must be linked to the new
2860-
* tuple for conflict detection purposes.
2861-
*/
2862-
PredicateLockTupleRowVersionLink(relation, &oldtup, heaptup);
2863-
28642853
if (newbuf != buffer)
28652854
LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
28662855
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);

src/backend/access/index/indexam.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,7 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
612612
* any more members. Otherwise, check for continuation of the
613613
* HOT-chain, and set state for next time.
614614
*/
615-
if (IsMVCCSnapshot(scan->xs_snapshot)
616-
&& !IsolationIsSerializable())
615+
if (IsMVCCSnapshot(scan->xs_snapshot))
617616
scan->xs_next_hot = InvalidOffsetNumber;
618617
else if (HeapTupleIsHotUpdated(heapTuple))
619618
{

src/backend/storage/lmgr/README-SSI

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,54 @@ is based on the top level xid. When looking at an xid that comes
402402
from a tuple's xmin or xmax, for example, we always call
403403
SubTransGetTopmostTransaction() before doing much else with it.
404404

405+
* PostgreSQL does not use "update in place" with a rollback log
406+
for its MVCC implementation. Where possible it uses "HOT" updates on
407+
the same page (if there is room and no indexed value is changed).
408+
For non-HOT updates the old tuple is expired in place and a new tuple
409+
is inserted at a new location. Because of this difference, a tuple
410+
lock in PostgreSQL doesn't automatically lock any other versions of a
411+
row. We don't try to copy or expand a tuple lock to any other
412+
versions of the row, based on the following proof that any additional
413+
serialization failures we would get from that would be false
414+
positives:
415+
416+
o If transaction T1 reads a row (thus acquiring a predicate
417+
lock on it) and a second transaction T2 updates that row, must a
418+
third transaction T3 which updates the new version of the row have a
419+
rw-conflict in from T1 to prevent anomalies? In other words, does it
420+
matter whether this edge T1 -> T3 is there?
421+
422+
o If T1 has a conflict in, it certainly doesn't. Adding the
423+
edge T1 -> T3 would create a dangerous structure, but we already had
424+
one from the edge T1 -> T2, so we would have aborted something
425+
anyway.
426+
427+
o Now let's consider the case where T1 doesn't have a
428+
conflict in. If that's the case, for this edge T1 -> T3 to make a
429+
difference, T3 must have a rw-conflict out that induces a cycle in
430+
the dependency graph, i.e. a conflict out to some transaction
431+
preceding T1 in the serial order. (A conflict out to T1 would work
432+
too, but that would mean T1 has a conflict in and we would have
433+
rolled back.)
434+
435+
o So now we're trying to figure out if there can be an
436+
rw-conflict edge T3 -> T0, where T0 is some transaction that precedes
437+
T1. For T0 to precede T1, there has to be has to be some edge, or
438+
sequence of edges, from T0 to T1. At least the last edge has to be a
439+
wr-dependency or ww-dependency rather than a rw-conflict, because T1
440+
doesn't have a rw-conflict in. And that gives us enough information
441+
about the order of transactions to see that T3 can't have a
442+
rw-dependency to T0:
443+
- T0 committed before T1 started (the wr/ww-dependency implies this)
444+
- T1 started before T2 committed (the T1->T2 rw-conflict implies this)
445+
- T2 committed before T3 started (otherwise, T3 would be aborted
446+
because of an update conflict)
447+
448+
o That means T0 committed before T3 started, and therefore
449+
there can't be a rw-conflict from T3 to T0.
450+
451+
o In both cases, we didn't need the T1 -> T3 edge.
452+
405453
* Predicate locking in PostgreSQL will start at the tuple level
406454
when possible, with automatic conversion of multiple fine-grained
407455
locks to coarser granularity as need to avoid resource exhaustion.

0 commit comments

Comments
 (0)