ssi-tuple-not-row-locking-3.patch
application/octet-stream
Filename: ssi-tuple-not-row-locking-3.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/access/heap/heapam.c | 2 | 0 |
| src/backend/access/index/indexam.c | 1 | 0 |
| src/backend/storage/lmgr/predicate.c | 18 | 3 |
| src/include/storage/predicate.h | 0 | 0 |
| src/test/isolation/expected/multiple-row-versions.out | 1 | 0 |
| src/test/isolation/specs/multiple-row-versions.spec | 2 | 3 |
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 1529,1535 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
OffsetNumber offnum;
bool at_chain_start;
bool valid;
- bool match_found;
if (all_dead)
*all_dead = true;
--- 1529,1534 ----
***************
*** 1539,1545 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
offnum = ItemPointerGetOffsetNumber(tid);
at_chain_start = true;
- match_found = false;
/* Scan through possible multiple members of HOT-chain */
for (;;)
--- 1538,1543 ----
***************
*** 1597,1606 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
PredicateLockTuple(relation, &heapTuple);
if (all_dead)
*all_dead = false;
! if (IsolationIsSerializable())
! match_found = true;
! else
! return true;
}
/*
--- 1595,1601 ----
PredicateLockTuple(relation, &heapTuple);
if (all_dead)
*all_dead = false;
! return true;
}
/*
***************
*** 1629,1635 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
break; /* end of chain */
}
! return match_found;
}
/*
--- 1624,1630 ----
break; /* end of chain */
}
! return false;
}
/*
***************
*** 2855,2866 **** l2:
END_CRIT_SECTION();
- /*
- * Any existing SIREAD locks on the old tuple must be linked to the new
- * tuple for conflict detection purposes.
- */
- PredicateLockTupleRowVersionLink(relation, &oldtup, heaptup);
-
if (newbuf != buffer)
LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
--- 2850,2855 ----
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 612,619 **** index_getnext(IndexScanDesc scan, ScanDirection direction)
* any more members. Otherwise, check for continuation of the
* HOT-chain, and set state for next time.
*/
! if (IsMVCCSnapshot(scan->xs_snapshot)
! && !IsolationIsSerializable())
scan->xs_next_hot = InvalidOffsetNumber;
else if (HeapTupleIsHotUpdated(heapTuple))
{
--- 612,618 ----
* any more members. Otherwise, check for continuation of the
* HOT-chain, and set state for next time.
*/
! if (IsMVCCSnapshot(scan->xs_snapshot))
scan->xs_next_hot = InvalidOffsetNumber;
else if (HeapTupleIsHotUpdated(heapTuple))
{
*** a/src/backend/storage/lmgr/predicate.c
--- b/src/backend/storage/lmgr/predicate.c
***************
*** 155,163 ****
* BlockNumber newblkno);
* PredicateLockPageCombine(Relation relation, BlockNumber oldblkno,
* BlockNumber newblkno);
- * PredicateLockTupleRowVersionLink(const Relation relation,
- * const HeapTuple oldTuple,
- * const HeapTuple newTuple)
* ReleasePredicateLocks(bool isCommit)
*
* conflict detection (may also trigger rollback)
--- 155,160 ----
***************
*** 2252,2341 **** PredicateLockTuple(const Relation relation, const HeapTuple tuple)
PredicateLockAcquire(&tag);
}
- /*
- * If the old tuple has any predicate locks, copy them to the new target.
- *
- * This is called at an UPDATE, where any predicate locks held on the old
- * tuple need to be copied to the new tuple, because logically they both
- * represent the same row. A lock taken before the update must conflict
- * with anyone locking the same row after the update.
- */
- void
- PredicateLockTupleRowVersionLink(const Relation relation,
- const HeapTuple oldTuple,
- const HeapTuple newTuple)
- {
- PREDICATELOCKTARGETTAG oldtupletag;
- PREDICATELOCKTARGETTAG oldpagetag;
- PREDICATELOCKTARGETTAG newtupletag;
- BlockNumber oldblk,
- newblk;
- OffsetNumber oldoff,
- newoff;
- TransactionId oldxmin,
- newxmin;
-
- /*
- * Bail out quickly if there are no serializable transactions
- * running.
- *
- * It's safe to do this check without taking any additional
- * locks. Even if a serializable transaction starts concurrently,
- * we know it can't take any SIREAD locks on the modified tuple
- * because the caller is holding the associated buffer page lock.
- * Memory reordering isn't an issue; the memory barrier in the
- * LWLock acquisition guarantees that this read occurs while the
- * buffer page lock is held.
- */
- if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
- return;
-
- oldblk = ItemPointerGetBlockNumber(&(oldTuple->t_self));
- oldoff = ItemPointerGetOffsetNumber(&(oldTuple->t_self));
- oldxmin = HeapTupleHeaderGetXmin(oldTuple->t_data);
-
- newblk = ItemPointerGetBlockNumber(&(newTuple->t_self));
- newoff = ItemPointerGetOffsetNumber(&(newTuple->t_self));
- newxmin = HeapTupleHeaderGetXmin(newTuple->t_data);
-
- SET_PREDICATELOCKTARGETTAG_TUPLE(oldtupletag,
- relation->rd_node.dbNode,
- relation->rd_id,
- oldblk,
- oldoff,
- oldxmin);
-
- SET_PREDICATELOCKTARGETTAG_PAGE(oldpagetag,
- relation->rd_node.dbNode,
- relation->rd_id,
- oldblk);
-
- SET_PREDICATELOCKTARGETTAG_TUPLE(newtupletag,
- relation->rd_node.dbNode,
- relation->rd_id,
- newblk,
- newoff,
- newxmin);
-
- /*
- * A page-level lock on the page containing the old tuple counts too.
- * Anyone holding a lock on the page is logically holding a lock on the
- * old tuple, so we need to acquire a lock on his behalf on the new tuple
- * too. However, if the new tuple is on the same page as the old one, the
- * old page-level lock already covers the new tuple.
- *
- * A relation-level lock always covers both tuple versions, so we don't
- * need to worry about those here.
- */
- LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
-
- TransferPredicateLocksToNewTarget(oldtupletag, newtupletag, false);
- if (newblk != oldblk)
- TransferPredicateLocksToNewTarget(oldpagetag, newtupletag, false);
-
- LWLockRelease(SerializablePredicateLockListLock);
- }
-
/*
* DeleteLockTarget
--- 2249,2254 ----
***************
*** 2650,2658 **** PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno,
/*
* Bail out quickly if there are no serializable transactions
! * running. As with PredicateLockTupleRowVersionLink, it's safe to
! * check this without taking locks because the caller is holding
! * the buffer page lock.
*/
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
return;
--- 2563,2577 ----
/*
* Bail out quickly if there are no serializable transactions
! * running.
! *
! * It's safe to do this check without taking any additional
! * locks. Even if a serializable transaction starts concurrently,
! * we know it can't take any SIREAD locks on the page being split
! * because the caller is holding the associated buffer page lock.
! * Memory reordering isn't an issue; the memory barrier in the
! * LWLock acquisition guarantees that this read occurs while the
! * buffer page lock is held.
*/
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
return;
***************
*** 3955,3963 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
* before the reader acquired its snapshot. (That is, the reader must not
* have been concurrent with the out-conflict transaction.)
*/
! if (!failure && !SxactIsCommitted(writer))
{
! if (SxactHasSummaryConflictOut(reader))
{
failure = true;
conflict = NULL;
--- 3874,3882 ----
* before the reader acquired its snapshot. (That is, the reader must not
* have been concurrent with the out-conflict transaction.)
*/
! if (!failure)
{
! if (SxactHasSummaryConflictOut(writer))
{
failure = true;
conflict = NULL;
***************
*** 3969,3979 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
offsetof(RWConflictData, outLink));
while (conflict)
{
! if ((reader == conflict->sxactIn && SxactIsCommitted(reader))
! || (SxactIsCommitted(conflict->sxactIn)
! && !SxactIsCommitted(reader)
! && (!SxactIsReadOnly(reader)
! || conflict->sxactIn->commitSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot)))
{
failure = true;
break;
--- 3888,3900 ----
offsetof(RWConflictData, outLink));
while (conflict)
{
! if (SxactIsCommitted(conflict->sxactIn)
! && (!SxactIsCommitted(reader)
! || conflict->sxactIn->commitSeqNo <= reader->commitSeqNo)
! && (!SxactIsCommitted(writer)
! || conflict->sxactIn->commitSeqNo <= writer->commitSeqNo)
! && (!SxactIsReadOnly(reader)
! || conflict->sxactIn->commitSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot))
{
failure = true;
break;
*** a/src/include/storage/predicate.h
--- b/src/include/storage/predicate.h
***************
*** 47,53 **** extern void RegisterPredicateLockingXid(const TransactionId xid);
extern void PredicateLockRelation(const Relation relation);
extern void PredicateLockPage(const Relation relation, const BlockNumber blkno);
extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
- extern void PredicateLockTupleRowVersionLink(const Relation relation, const HeapTuple oldTuple, const HeapTuple newTuple);
extern void PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
extern void PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
extern void ReleasePredicateLocks(const bool isCommit);
--- 47,52 ----
*** a/src/test/isolation/expected/multiple-row-versions.out
--- b/src/test/isolation/expected/multiple-row-versions.out
***************
*** 19,24 **** id txt
1
step c4: COMMIT;
step c3: COMMIT;
- ERROR: could not serialize access due to read/write dependencies among transactions
step wz1: UPDATE t SET txt = 'a' WHERE id = 1;
step c1: COMMIT;
--- 19,24 ----
1
step c4: COMMIT;
step c3: COMMIT;
step wz1: UPDATE t SET txt = 'a' WHERE id = 1;
+ ERROR: could not serialize access due to read/write dependencies among transactions
step c1: COMMIT;
*** a/src/test/isolation/specs/multiple-row-versions.spec
--- b/src/test/isolation/specs/multiple-row-versions.spec
***************
*** 1,8 ****
# Multiple Row Versions test
#
! # This test is designed to ensure that predicate locks taken on one version
! # of a row are detected as conflicts when a later version of the row is
! # updated or deleted by a transaction concurrent to the reader.
#
# Due to long permutation setup time, we are only testing one specific
# permutation, which should get a serialization error.
--- 1,7 ----
# Multiple Row Versions test
#
! # This test is designed to cover some code paths which only occur with
! # four or more transactions interacting with particular timings.
#
# Due to long permutation setup time, we are only testing one specific
# permutation, which should get a serialization error.