#include "utils/relcache.h"#include "utils/snapshot.h"

Go to the source code of this file.
| #define NUM_OLDSERXID_BUFFERS 16 |
Definition at line 28 of file predicate.h.
Referenced by OldSerXidInit(), and PredicateLockShmemSize().
| void AtPrepare_PredicateLocks | ( | void | ) |
Definition at line 4729 of file predicate.c.
References TwoPhasePredicateRecord::data, SERIALIZABLEXACT::flags, TwoPhasePredicateXactRecord::flags, InvalidSerializableXact, TwoPhasePredicateRecord::lockRecord, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, NULL, offsetof, SERIALIZABLEXACT::predicateLocks, RegisterTwoPhaseRecord(), SerializablePredicateLockListLock, SHMQueueNext(), PREDICATELOCKTARGET::tag, PREDICATELOCK::tag, TwoPhasePredicateLockRecord::target, TWOPHASE_RM_PREDICATELOCK_ID, TwoPhasePredicateRecord::type, PREDICATELOCK::xactLink, TwoPhasePredicateRecord::xactRecord, SERIALIZABLEXACT::xmin, and TwoPhasePredicateXactRecord::xmin.
Referenced by PrepareTransaction().
{
PREDICATELOCK *predlock;
SERIALIZABLEXACT *sxact;
TwoPhasePredicateRecord record;
TwoPhasePredicateXactRecord *xactRecord;
TwoPhasePredicateLockRecord *lockRecord;
sxact = MySerializableXact;
xactRecord = &(record.data.xactRecord);
lockRecord = &(record.data.lockRecord);
if (MySerializableXact == InvalidSerializableXact)
return;
/* Generate a xact record for our SERIALIZABLEXACT */
record.type = TWOPHASEPREDICATERECORD_XACT;
xactRecord->xmin = MySerializableXact->xmin;
xactRecord->flags = MySerializableXact->flags;
/*
* Note that we don't include the list of conflicts in our out in the
* statefile, because new conflicts can be added even after the
* transaction prepares. We'll just make a conservative assumption during
* recovery instead.
*/
RegisterTwoPhaseRecord(TWOPHASE_RM_PREDICATELOCK_ID, 0,
&record, sizeof(record));
/*
* Generate a lock record for each lock.
*
* To do this, we need to walk the predicate lock list in our sxact rather
* than using the local predicate lock table because the latter is not
* guaranteed to be accurate.
*/
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(sxact->predicateLocks),
offsetof(PREDICATELOCK, xactLink));
while (predlock != NULL)
{
record.type = TWOPHASEPREDICATERECORD_LOCK;
lockRecord->target = predlock->tag.myTarget->tag;
RegisterTwoPhaseRecord(TWOPHASE_RM_PREDICATELOCK_ID, 0,
&record, sizeof(record));
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(predlock->xactLink),
offsetof(PREDICATELOCK, xactLink));
}
LWLockRelease(SerializablePredicateLockListLock);
}
Definition at line 4249 of file predicate.c.
References BufferGetBlockNumber(), BufferIsValid, CheckTargetForConflictsIn(), RelFileNode::dbNode, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, HeapTupleHeaderGetXmin, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, MyXactDidWrite, NULL, RelationData::rd_id, RelationData::rd_node, SerializationNeededForWrite(), SET_PREDICATELOCKTARGETTAG_PAGE, SET_PREDICATELOCKTARGETTAG_RELATION, SET_PREDICATELOCKTARGETTAG_TUPLE, SxactIsDoomed, HeapTupleHeaderData::t_ctid, and HeapTupleData::t_data.
Referenced by _bt_doinsert(), heap_delete(), heap_insert(), heap_multi_insert(), heap_update(), and index_insert().
{
PREDICATELOCKTARGETTAG targettag;
if (!SerializationNeededForWrite(relation))
return;
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during conflict in checking."),
errhint("The transaction might succeed if retried.")));
/*
* We're doing a write which might cause rw-conflicts now or later.
* Memorize that fact.
*/
MyXactDidWrite = true;
/*
* It is important that we check for locks from the finest granularity to
* the coarsest granularity, so that granularity promotion doesn't cause
* us to miss a lock. The new (coarser) lock will be acquired before the
* old (finer) locks are released.
*
* It is not possible to take and hold a lock across the checks for all
* granularities because each target could be in a separate partition.
*/
if (tuple != NULL)
{
SET_PREDICATELOCKTARGETTAG_TUPLE(targettag,
relation->rd_node.dbNode,
relation->rd_id,
ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)),
ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)),
HeapTupleHeaderGetXmin(tuple->t_data));
CheckTargetForConflictsIn(&targettag);
}
if (BufferIsValid(buffer))
{
SET_PREDICATELOCKTARGETTAG_PAGE(targettag,
relation->rd_node.dbNode,
relation->rd_id,
BufferGetBlockNumber(buffer));
CheckTargetForConflictsIn(&targettag);
}
SET_PREDICATELOCKTARGETTAG_RELATION(targettag,
relation->rd_node.dbNode,
relation->rd_id);
CheckTargetForConflictsIn(&targettag);
}
| void CheckForSerializableConflictOut | ( | bool | valid, | |
| Relation | relation, | |||
| HeapTuple | tuple, | |||
| Buffer | buffer, | |||
| Snapshot | snapshot | |||
| ) |
Definition at line 3868 of file predicate.c.
References Assert, SERIALIZABLEXACT::earliestOutConflictCommit, elog, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, FlagRWConflict(), SERIALIZABLEXACT::flags, GetTopTransactionIdIfAny(), HASH_FIND, hash_search(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleSatisfiesVacuum(), SERIALIZABLEXACT::inConflicts, InvalidSerCommitSeqNo, SERIALIZABLEXACT::lastCommitBeforeSnapshot, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXID::myXact, NULL, OldSerXidGetMinConflictCommitSeqNo(), RWConflictExists(), SERIALIZABLEXACT::SeqNo, SerializableXactHashLock, SerializationNeededForRead(), SHMQueueEmpty(), SubTransGetTopmostTransaction(), SxactHasConflictOut, SxactHasSummaryConflictIn, SxactHasSummaryConflictOut, SxactIsCommitted, SxactIsDoomed, SxactIsPrepared, SxactIsReadOnly, HeapTupleData::t_data, SERIALIZABLEXACT::topXid, TransactionIdEquals, TransactionIdFollowsOrEquals(), TransactionIdIsValid, TransactionIdPrecedes(), TransactionXmin, SERIALIZABLEXIDTAG::xid, and XidIsConcurrent().
Referenced by bitgetpage(), heap_fetch(), heap_get_latest_tid(), heap_hot_search_buffer(), heapgetpage(), and heapgettup().
{
TransactionId xid;
SERIALIZABLEXIDTAG sxidtag;
SERIALIZABLEXID *sxid;
SERIALIZABLEXACT *sxact;
HTSV_Result htsvResult;
if (!SerializationNeededForRead(relation, snapshot))
return;
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
{
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during conflict out checking."),
errhint("The transaction might succeed if retried.")));
}
/*
* Check to see whether the tuple has been written to by a concurrent
* transaction, either to create it not visible to us, or to delete it
* while it is visible to us. The "visible" bool indicates whether the
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
htsvResult = HeapTupleSatisfiesVacuum(tuple->t_data, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
if (visible)
return;
xid = HeapTupleHeaderGetXmin(tuple->t_data);
break;
case HEAPTUPLE_RECENTLY_DEAD:
if (!visible)
return;
xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
break;
case HEAPTUPLE_DELETE_IN_PROGRESS:
xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
break;
case HEAPTUPLE_INSERT_IN_PROGRESS:
xid = HeapTupleHeaderGetXmin(tuple->t_data);
break;
case HEAPTUPLE_DEAD:
return;
default:
/*
* The only way to get to this default clause is if a new value is
* added to the enum type without adding it to this switch
* statement. That's a bug, so elog.
*/
elog(ERROR, "unrecognized return value from HeapTupleSatisfiesVacuum: %u", htsvResult);
/*
* In spite of having all enum values covered and calling elog on
* this default, some compilers think this is a code path which
* allows xid to be used below without initialization. Silence
* that warning.
*/
xid = InvalidTransactionId;
}
Assert(TransactionIdIsValid(xid));
Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
/*
* Find top level xid. Bail out if xid is too early to be a conflict, or
* if it's our own xid.
*/
if (TransactionIdEquals(xid, GetTopTransactionIdIfAny()))
return;
xid = SubTransGetTopmostTransaction(xid);
if (TransactionIdPrecedes(xid, TransactionXmin))
return;
if (TransactionIdEquals(xid, GetTopTransactionIdIfAny()))
return;
/*
* Find sxact or summarized info for the top level xid.
*/
sxidtag.xid = xid;
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
sxid = (SERIALIZABLEXID *)
hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
if (!sxid)
{
/*
* Transaction not found in "normal" SSI structures. Check whether it
* got pushed out to SLRU storage for "old committed" transactions.
*/
SerCommitSeqNo conflictCommitSeqNo;
conflictCommitSeqNo = OldSerXidGetMinConflictCommitSeqNo(xid);
if (conflictCommitSeqNo != 0)
{
if (conflictCommitSeqNo != InvalidSerCommitSeqNo
&& (!SxactIsReadOnly(MySerializableXact)
|| conflictCommitSeqNo
<= MySerializableXact->SeqNo.lastCommitBeforeSnapshot))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on conflict out to old pivot %u.", xid),
errhint("The transaction might succeed if retried.")));
if (SxactHasSummaryConflictIn(MySerializableXact)
|| !SHMQueueEmpty(&MySerializableXact->inConflicts))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, with conflict out to old committed transaction %u.", xid),
errhint("The transaction might succeed if retried.")));
MySerializableXact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
}
/* It's not serializable or otherwise not important. */
LWLockRelease(SerializableXactHashLock);
return;
}
sxact = sxid->myXact;
Assert(TransactionIdEquals(sxact->topXid, xid));
if (sxact == MySerializableXact || SxactIsDoomed(sxact))
{
/* Can't conflict with ourself or a transaction that will roll back. */
LWLockRelease(SerializableXactHashLock);
return;
}
/*
* We have a conflict out to a transaction which has a conflict out to a
* summarized transaction. That summarized transaction must have
* committed first, and we can't tell when it committed in relation to our
* snapshot acquisition, so something needs to be canceled.
*/
if (SxactHasSummaryConflictOut(sxact))
{
if (!SxactIsPrepared(sxact))
{
sxact->flags |= SXACT_FLAG_DOOMED;
LWLockRelease(SerializableXactHashLock);
return;
}
else
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on conflict out to old pivot."),
errhint("The transaction might succeed if retried.")));
}
}
/*
* If this is a read-only transaction and the writing transaction has
* committed, and it doesn't have a rw-conflict to a transaction which
* committed before it, no conflict.
*/
if (SxactIsReadOnly(MySerializableXact)
&& SxactIsCommitted(sxact)
&& !SxactHasSummaryConflictOut(sxact)
&& (!SxactHasConflictOut(sxact)
|| MySerializableXact->SeqNo.lastCommitBeforeSnapshot < sxact->SeqNo.earliestOutConflictCommit))
{
/* Read-only transaction will appear to run first. No conflict. */
LWLockRelease(SerializableXactHashLock);
return;
}
if (!XidIsConcurrent(xid))
{
/* This write was already in our snapshot; no conflict. */
LWLockRelease(SerializableXactHashLock);
return;
}
if (RWConflictExists(MySerializableXact, sxact))
{
/* We don't want duplicate conflict records in the list. */
LWLockRelease(SerializableXactHashLock);
return;
}
/*
* Flag the conflict. But first, if this conflict creates a dangerous
* structure, ereport an error.
*/
FlagRWConflict(MySerializableXact, sxact);
LWLockRelease(SerializableXactHashLock);
}
| void CheckPointPredicate | ( | void | ) |
Definition at line 1026 of file predicate.c.
References OldSerXidControlData::headPage, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), OldSerXidLock, OldSerXidPage, OldSerXidSlruCtl, SimpleLruFlush(), SimpleLruTruncate(), OldSerXidControlData::tailXid, and TransactionIdIsValid.
Referenced by CheckPointGuts().
{
int tailPage;
LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE);
/* Exit quickly if the SLRU is currently not in use. */
if (oldSerXidControl->headPage < 0)
{
LWLockRelease(OldSerXidLock);
return;
}
if (TransactionIdIsValid(oldSerXidControl->tailXid))
{
/* We can truncate the SLRU up to the page containing tailXid */
tailPage = OldSerXidPage(oldSerXidControl->tailXid);
}
else
{
/*
* The SLRU is no longer needed. Truncate to head before we set head
* invalid.
*
* XXX: It's possible that the SLRU is not needed again until XID
* wrap-around has happened, so that the segment containing headPage
* that we leave behind will appear to be new again. In that case it
* won't be removed until XID horizon advances enough to make it
* current again.
*/
tailPage = oldSerXidControl->headPage;
oldSerXidControl->headPage = -1;
}
LWLockRelease(OldSerXidLock);
/* Truncate away pages that are no longer required */
SimpleLruTruncate(OldSerXidSlruCtl, tailPage);
/*
* Flush dirty SLRU pages to disk
*
* This is not actually necessary from a correctness point of view. We do
* it merely as a debugging aid.
*
* We're doing this after the truncation to avoid writing pages right
* before deleting the file in which they sit, which would be completely
* pointless.
*/
SimpleLruFlush(OldSerXidSlruCtl, true);
}
| void CheckTableForSerializableConflictIn | ( | Relation | relation | ) |
Definition at line 4334 of file predicate.c.
References Assert, RelFileNode::dbNode, FirstPredicateLockMgrLock, FlagRWConflict(), GET_PREDICATELOCKTARGETTAG_DB, GET_PREDICATELOCKTARGETTAG_RELATION, hash_seq_init(), hash_seq_search(), i, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myXact, MyXactDidWrite, NULL, offsetof, PREDICATELOCKTARGET::predicateLocks, RelationData::rd_id, RelationData::rd_index, RelationData::rd_node, RWConflictExists(), SerializablePredicateLockListLock, SerializableXactHashLock, SerializationNeededForWrite(), SHMQueueNext(), PredXactListData::SxactGlobalXmin, PREDICATELOCK::tag, PREDICATELOCKTARGET::tag, PREDICATELOCK::targetLink, and TransactionIdIsValid.
Referenced by ExecuteTruncate(), and heap_drop_with_catalog().
{
HASH_SEQ_STATUS seqstat;
PREDICATELOCKTARGET *target;
Oid dbId;
Oid heapId;
int i;
/*
* Bail out quickly if there are no serializable transactions running.
* It's safe to check this without taking locks because the caller is
* holding an ACCESS EXCLUSIVE lock on the relation. No new locks which
* would matter here can be acquired while that is held.
*/
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
return;
if (!SerializationNeededForWrite(relation))
return;
/*
* We're doing a write which might cause rw-conflicts now or later.
* Memorize that fact.
*/
MyXactDidWrite = true;
Assert(relation->rd_index == NULL); /* not an index relation */
dbId = relation->rd_node.dbNode;
heapId = relation->rd_id;
LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
for (i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++)
LWLockAcquire(FirstPredicateLockMgrLock + i, LW_SHARED);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
/* Scan through target list */
hash_seq_init(&seqstat, PredicateLockTargetHash);
while ((target = (PREDICATELOCKTARGET *) hash_seq_search(&seqstat)))
{
PREDICATELOCK *predlock;
/*
* Check whether this is a target which needs attention.
*/
if (GET_PREDICATELOCKTARGETTAG_RELATION(target->tag) != heapId)
continue; /* wrong relation id */
if (GET_PREDICATELOCKTARGETTAG_DB(target->tag) != dbId)
continue; /* wrong database id */
/*
* Loop through locks for this target and flag conflicts.
*/
predlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
&(target->predicateLocks),
offsetof(PREDICATELOCK, targetLink));
while (predlock)
{
PREDICATELOCK *nextpredlock;
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
&(predlock->targetLink),
offsetof(PREDICATELOCK, targetLink));
if (predlock->tag.myXact != MySerializableXact
&& !RWConflictExists(predlock->tag.myXact, MySerializableXact))
{
FlagRWConflict(predlock->tag.myXact, MySerializableXact);
}
predlock = nextpredlock;
}
}
/* Release locks in reverse order */
LWLockRelease(SerializableXactHashLock);
for (i = NUM_PREDICATELOCK_PARTITIONS - 1; i >= 0; i--)
LWLockRelease(FirstPredicateLockMgrLock + i);
LWLockRelease(SerializablePredicateLockListLock);
}
Definition at line 1572 of file predicate.c.
References Assert, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, GetSafeSnapshot(), GetSerializableTransactionSnapshotInt(), InvalidTransactionId, IsolationIsSerializable, RecoveryInProgress(), XactDeferrable, and XactReadOnly.
Referenced by GetTransactionSnapshot().
{
Assert(IsolationIsSerializable());
/*
* Can't use serializable mode while recovery is still active, as it is,
* for example, on a hot standby. We could get here despite the check
* in check_XactIsoLevel() if default_transaction_isolation is set to
* serializable, so phrase the hint accordingly.
*/
if (RecoveryInProgress())
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use serializable mode in a hot standby"),
errdetail("\"default_transaction_isolation\" is set to \"serializable\"."),
errhint("You can use \"SET default_transaction_isolation = 'repeatable read'\" to change the default.")));
/*
* A special optimization is available for SERIALIZABLE READ ONLY
* DEFERRABLE transactions -- we can wait for a suitable snapshot and
* thereby avoid all SSI overhead once it's running.
*/
if (XactReadOnly && XactDeferrable)
return GetSafeSnapshot(snapshot);
return GetSerializableTransactionSnapshotInt(snapshot,
InvalidTransactionId);
}
| void InitPredicateLocks | ( | void | ) |
Definition at line 1091 of file predicate.c.
References PredXactListData::activeList, RWConflictPoolHeaderData::availableList, PredXactListData::availableList, PredXactListData::CanPartialClearThrough, SERIALIZABLEXACT::commitSeqNo, CreatePredXact(), RWConflictPoolHeaderData::element, PredXactListData::element, HASHCTL::entrysize, ereport, errcode(), errmsg(), ERROR, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, FirstNormalSerCommitSeqNo, SERIALIZABLEXACT::flags, HASHCTL::hash, HASH_ELEM, HASH_ENTER, HASH_FUNCTION, HASH_PARTITION, hash_search(), PredXactListData::HavePartialClearedThrough, i, SERIALIZABLEXACT::inConflicts, HASHCTL::keysize, SERIALIZABLEXACT::lastCommitBeforeSnapshot, PredXactListData::LastSxactCommitSeqNo, PredXactListElementData::link, max_prepared_xacts, MaxBackends, MemSet, mul_size(), NPREDICATELOCKTARGETENTS, NULL, HASHCTL::num_partitions, PredXactListData::OldCommittedSxact, OldSerXidInit(), SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, PredicateLockHashPartitionLock, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, PredXactListDataSize, PredXactListElementDataSize, SERIALIZABLEXACT::prepareSeqNo, RWConflictDataSize, RWConflictPoolHeaderDataSize, ScratchPartitionLock, ScratchTargetTagHash, SERIALIZABLEXACT::SeqNo, SetInvalidVirtualTransactionId, ShmemAlloc(), ShmemInitHash(), ShmemInitStruct(), SHMQueueInit(), SHMQueueInsertBefore(), PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SERIALIZABLEXACT::topXid, SERIALIZABLEXACT::vxid, PredXactListData::WritableSxactCount, and SERIALIZABLEXACT::xmin.
Referenced by CreateSharedMemoryAndSemaphores().
{
HASHCTL info;
int hash_flags;
long max_table_size;
Size requestSize;
bool found;
/*
* Compute size of predicate lock target hashtable. Note these
* calculations must agree with PredicateLockShmemSize!
*/
max_table_size = NPREDICATELOCKTARGETENTS();
/*
* Allocate hash table for PREDICATELOCKTARGET structs. This stores
* per-predicate-lock-target information.
*/
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(PREDICATELOCKTARGETTAG);
info.entrysize = sizeof(PREDICATELOCKTARGET);
info.hash = tag_hash;
info.num_partitions = NUM_PREDICATELOCK_PARTITIONS;
hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION | HASH_FIXED_SIZE);
PredicateLockTargetHash = ShmemInitHash("PREDICATELOCKTARGET hash",
max_table_size,
max_table_size,
&info,
hash_flags);
/* Assume an average of 2 xacts per target */
max_table_size *= 2;
/*
* Reserve a dummy entry in the hash table; we use it to make sure there's
* always one entry available when we need to split or combine a page,
* because running out of space there could mean aborting a
* non-serializable transaction.
*/
hash_search(PredicateLockTargetHash, &ScratchTargetTag, HASH_ENTER, NULL);
/*
* Allocate hash table for PREDICATELOCK structs. This stores per
* xact-lock-of-a-target information.
*/
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(PREDICATELOCKTAG);
info.entrysize = sizeof(PREDICATELOCK);
info.hash = predicatelock_hash;
info.num_partitions = NUM_PREDICATELOCK_PARTITIONS;
hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION | HASH_FIXED_SIZE);
PredicateLockHash = ShmemInitHash("PREDICATELOCK hash",
max_table_size,
max_table_size,
&info,
hash_flags);
/*
* Compute size for serializable transaction hashtable. Note these
* calculations must agree with PredicateLockShmemSize!
*/
max_table_size = (MaxBackends + max_prepared_xacts);
/*
* Allocate a list to hold information on transactions participating in
* predicate locking.
*
* Assume an average of 10 predicate locking transactions per backend.
* This allows aggressive cleanup while detail is present before data must
* be summarized for storage in SLRU and the "dummy" transaction.
*/
max_table_size *= 10;
PredXact = ShmemInitStruct("PredXactList",
PredXactListDataSize,
&found);
if (!found)
{
int i;
SHMQueueInit(&PredXact->availableList);
SHMQueueInit(&PredXact->activeList);
PredXact->SxactGlobalXmin = InvalidTransactionId;
PredXact->SxactGlobalXminCount = 0;
PredXact->WritableSxactCount = 0;
PredXact->LastSxactCommitSeqNo = FirstNormalSerCommitSeqNo - 1;
PredXact->CanPartialClearThrough = 0;
PredXact->HavePartialClearedThrough = 0;
requestSize = mul_size((Size) max_table_size,
PredXactListElementDataSize);
PredXact->element = ShmemAlloc(requestSize);
if (PredXact->element == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough shared memory for elements of data structure"
" \"%s\" (%lu bytes requested)",
"PredXactList", (unsigned long) requestSize)));
/* Add all elements to available list, clean. */
memset(PredXact->element, 0, requestSize);
for (i = 0; i < max_table_size; i++)
{
SHMQueueInsertBefore(&(PredXact->availableList),
&(PredXact->element[i].link));
}
PredXact->OldCommittedSxact = CreatePredXact();
SetInvalidVirtualTransactionId(PredXact->OldCommittedSxact->vxid);
PredXact->OldCommittedSxact->prepareSeqNo = 0;
PredXact->OldCommittedSxact->commitSeqNo = 0;
PredXact->OldCommittedSxact->SeqNo.lastCommitBeforeSnapshot = 0;
SHMQueueInit(&PredXact->OldCommittedSxact->outConflicts);
SHMQueueInit(&PredXact->OldCommittedSxact->inConflicts);
SHMQueueInit(&PredXact->OldCommittedSxact->predicateLocks);
SHMQueueInit(&PredXact->OldCommittedSxact->finishedLink);
SHMQueueInit(&PredXact->OldCommittedSxact->possibleUnsafeConflicts);
PredXact->OldCommittedSxact->topXid = InvalidTransactionId;
PredXact->OldCommittedSxact->finishedBefore = InvalidTransactionId;
PredXact->OldCommittedSxact->xmin = InvalidTransactionId;
PredXact->OldCommittedSxact->flags = SXACT_FLAG_COMMITTED;
PredXact->OldCommittedSxact->pid = 0;
}
/* This never changes, so let's keep a local copy. */
OldCommittedSxact = PredXact->OldCommittedSxact;
/*
* Allocate hash table for SERIALIZABLEXID structs. This stores per-xid
* information for serializable transactions which have accessed data.
*/
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(SERIALIZABLEXIDTAG);
info.entrysize = sizeof(SERIALIZABLEXID);
info.hash = tag_hash;
hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
SerializableXidHash = ShmemInitHash("SERIALIZABLEXID hash",
max_table_size,
max_table_size,
&info,
hash_flags);
/*
* Allocate space for tracking rw-conflicts in lists attached to the
* transactions.
*
* Assume an average of 5 conflicts per transaction. Calculations suggest
* that this will prevent resource exhaustion in even the most pessimal
* loads up to max_connections = 200 with all 200 connections pounding the
* database with serializable transactions. Beyond that, there may be
* occasional transactions canceled when trying to flag conflicts. That's
* probably OK.
*/
max_table_size *= 5;
RWConflictPool = ShmemInitStruct("RWConflictPool",
RWConflictPoolHeaderDataSize,
&found);
if (!found)
{
int i;
SHMQueueInit(&RWConflictPool->availableList);
requestSize = mul_size((Size) max_table_size,
RWConflictDataSize);
RWConflictPool->element = ShmemAlloc(requestSize);
if (RWConflictPool->element == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough shared memory for elements of data structure"
" \"%s\" (%lu bytes requested)",
"RWConflictPool", (unsigned long) requestSize)));
/* Add all elements to available list, clean. */
memset(RWConflictPool->element, 0, requestSize);
for (i = 0; i < max_table_size; i++)
{
SHMQueueInsertBefore(&(RWConflictPool->availableList),
&(RWConflictPool->element[i].outLink));
}
}
/*
* Create or attach to the header for the list of finished serializable
* transactions.
*/
FinishedSerializableTransactions = (SHM_QUEUE *)
ShmemInitStruct("FinishedSerializableTransactions",
sizeof(SHM_QUEUE),
&found);
if (!found)
SHMQueueInit(FinishedSerializableTransactions);
/*
* Initialize the SLRU storage for old committed serializable
* transactions.
*/
OldSerXidInit();
/* Pre-calculate the hash and partition lock of the scratch entry */
ScratchTargetTagHash = PredicateLockTargetTagHashCode(&ScratchTargetTag);
ScratchPartitionLock = PredicateLockHashPartitionLock(ScratchTargetTagHash);
}
| bool PageIsPredicateLocked | ( | Relation | relation, | |
| BlockNumber | blkno | |||
| ) |
Definition at line 1855 of file predicate.c.
References RelFileNode::dbNode, HASH_FIND, hash_search_with_hash_value(), LW_SHARED, LWLockAcquire(), LWLockRelease(), NULL, PredicateLockHashPartitionLock, PredicateLockTargetTagHashCode, RelationData::rd_id, RelationData::rd_node, and SET_PREDICATELOCKTARGETTAG_PAGE.
{
PREDICATELOCKTARGETTAG targettag;
uint32 targettaghash;
LWLockId partitionLock;
PREDICATELOCKTARGET *target;
SET_PREDICATELOCKTARGETTAG_PAGE(targettag,
relation->rd_node.dbNode,
relation->rd_id,
blkno);
targettaghash = PredicateLockTargetTagHashCode(&targettag);
partitionLock = PredicateLockHashPartitionLock(targettaghash);
LWLockAcquire(partitionLock, LW_SHARED);
target = (PREDICATELOCKTARGET *)
hash_search_with_hash_value(PredicateLockTargetHash,
&targettag, targettaghash,
HASH_FIND, NULL);
LWLockRelease(partitionLock);
return (target != NULL);
}
| void PostPrepare_PredicateLocks | ( | TransactionId | xid | ) |
Definition at line 4798 of file predicate.c.
References Assert, hash_destroy(), InvalidSerializableXact, MyXactDidWrite, SERIALIZABLEXACT::pid, and SxactIsPrepared.
Referenced by PrepareTransaction().
{
if (MySerializableXact == InvalidSerializableXact)
return;
Assert(SxactIsPrepared(MySerializableXact));
MySerializableXact->pid = 0;
hash_destroy(LocalPredicateLockHash);
LocalPredicateLockHash = NULL;
MySerializableXact = InvalidSerializableXact;
MyXactDidWrite = false;
}
| void PreCommit_CheckForSerializationFailure | ( | void | ) |
Definition at line 4636 of file predicate.c.
References Assert, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, SERIALIZABLEXACT::flags, SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, InvalidSerializableXact, IsolationIsSerializable, PredXactListData::LastSxactCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), offsetof, SERIALIZABLEXACT::prepareSeqNo, SerializableXactHashLock, SHMQueueNext(), SxactIsCommitted, SxactIsDoomed, SxactIsPrepared, SxactIsReadOnly, and RWConflictData::sxactOut.
Referenced by CommitTransaction(), and PrepareTransaction().
{
RWConflict nearConflict;
if (MySerializableXact == InvalidSerializableXact)
return;
Assert(IsolationIsSerializable());
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during commit attempt."),
errhint("The transaction might succeed if retried.")));
}
nearConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&MySerializableXact->inConflicts,
offsetof(RWConflictData, inLink));
while (nearConflict)
{
if (!SxactIsCommitted(nearConflict->sxactOut)
&& !SxactIsDoomed(nearConflict->sxactOut))
{
RWConflict farConflict;
farConflict = (RWConflict)
SHMQueueNext(&nearConflict->sxactOut->inConflicts,
&nearConflict->sxactOut->inConflicts,
offsetof(RWConflictData, inLink));
while (farConflict)
{
if (farConflict->sxactOut == MySerializableXact
|| (!SxactIsCommitted(farConflict->sxactOut)
&& !SxactIsReadOnly(farConflict->sxactOut)
&& !SxactIsDoomed(farConflict->sxactOut)))
{
/*
* Normally, we kill the pivot transaction to make sure we
* make progress if the failing transaction is retried.
* However, we can't kill it if it's already prepared, so
* in that case we commit suicide instead.
*/
if (SxactIsPrepared(nearConflict->sxactOut))
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on commit attempt with conflict in from prepared pivot."),
errhint("The transaction might succeed if retried.")));
}
nearConflict->sxactOut->flags |= SXACT_FLAG_DOOMED;
break;
}
farConflict = (RWConflict)
SHMQueueNext(&nearConflict->sxactOut->inConflicts,
&farConflict->inLink,
offsetof(RWConflictData, inLink));
}
}
nearConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&nearConflict->inLink,
offsetof(RWConflictData, inLink));
}
MySerializableXact->prepareSeqNo = ++(PredXact->LastSxactCommitSeqNo);
MySerializableXact->flags |= SXACT_FLAG_PREPARED;
LWLockRelease(SerializableXactHashLock);
}
| void predicatelock_twophase_recover | ( | TransactionId | xid, | |
| uint16 | info, | |||
| void * | recdata, | |||
| uint32 | len | |||
| ) |
Definition at line 4847 of file predicate.c.
References Assert, VirtualTransactionId::backendId, SERIALIZABLEXACT::commitSeqNo, CreatePredicateLock(), CreatePredXact(), TwoPhasePredicateRecord::data, ereport, errcode(), errmsg(), ERROR, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, TwoPhasePredicateXactRecord::flags, SERIALIZABLEXACT::flags, HASH_ENTER, HASH_FIND, hash_search(), SERIALIZABLEXACT::inConflicts, InvalidSerializableXact, SERIALIZABLEXACT::lastCommitBeforeSnapshot, VirtualTransactionId::localTransactionId, TwoPhasePredicateRecord::lockRecord, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), max_prepared_xacts, MaxBackends, SERIALIZABLEXID::myXact, NULL, OldSerXidSetActiveSerXmin(), SERIALIZABLEXACT::outConflicts, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, SERIALIZABLEXACT::prepareSeqNo, SERIALIZABLEXACT::SeqNo, SerializableXactHashLock, SHMQueueElemInit(), SHMQueueInit(), PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SxactIsPrepared, SxactIsReadOnly, TwoPhasePredicateLockRecord::target, SERIALIZABLEXACT::topXid, TransactionIdEquals, TransactionIdFollows(), TransactionIdIsValid, TWOPHASEPREDICATERECORD_LOCK, TWOPHASEPREDICATERECORD_XACT, TwoPhasePredicateRecord::type, SERIALIZABLEXACT::vxid, PredXactListData::WritableSxactCount, TwoPhasePredicateRecord::xactRecord, SERIALIZABLEXIDTAG::xid, TwoPhasePredicateXactRecord::xmin, and SERIALIZABLEXACT::xmin.
{
TwoPhasePredicateRecord *record;
Assert(len == sizeof(TwoPhasePredicateRecord));
record = (TwoPhasePredicateRecord *) recdata;
Assert((record->type == TWOPHASEPREDICATERECORD_XACT) ||
(record->type == TWOPHASEPREDICATERECORD_LOCK));
if (record->type == TWOPHASEPREDICATERECORD_XACT)
{
/* Per-transaction record. Set up a SERIALIZABLEXACT. */
TwoPhasePredicateXactRecord *xactRecord;
SERIALIZABLEXACT *sxact;
SERIALIZABLEXID *sxid;
SERIALIZABLEXIDTAG sxidtag;
bool found;
xactRecord = (TwoPhasePredicateXactRecord *) &record->data.xactRecord;
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
sxact = CreatePredXact();
if (!sxact)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory")));
/* vxid for a prepared xact is InvalidBackendId/xid; no pid */
sxact->vxid.backendId = InvalidBackendId;
sxact->vxid.localTransactionId = (LocalTransactionId) xid;
sxact->pid = 0;
/* a prepared xact hasn't committed yet */
sxact->prepareSeqNo = RecoverySerCommitSeqNo;
sxact->commitSeqNo = InvalidSerCommitSeqNo;
sxact->finishedBefore = InvalidTransactionId;
sxact->SeqNo.lastCommitBeforeSnapshot = RecoverySerCommitSeqNo;
/*
* Don't need to track this; no transactions running at the time the
* recovered xact started are still active, except possibly other
* prepared xacts and we don't care whether those are RO_SAFE or not.
*/
SHMQueueInit(&(sxact->possibleUnsafeConflicts));
SHMQueueInit(&(sxact->predicateLocks));
SHMQueueElemInit(&(sxact->finishedLink));
sxact->topXid = xid;
sxact->xmin = xactRecord->xmin;
sxact->flags = xactRecord->flags;
Assert(SxactIsPrepared(sxact));
if (!SxactIsReadOnly(sxact))
{
++(PredXact->WritableSxactCount);
Assert(PredXact->WritableSxactCount <=
(MaxBackends + max_prepared_xacts));
}
/*
* We don't know whether the transaction had any conflicts or not, so
* we'll conservatively assume that it had both a conflict in and a
* conflict out, and represent that with the summary conflict flags.
*/
SHMQueueInit(&(sxact->outConflicts));
SHMQueueInit(&(sxact->inConflicts));
sxact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_IN;
sxact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
/* Register the transaction's xid */
sxidtag.xid = xid;
sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
&sxidtag,
HASH_ENTER, &found);
Assert(sxid != NULL);
Assert(!found);
sxid->myXact = (SERIALIZABLEXACT *) sxact;
/*
* Update global xmin. Note that this is a special case compared to
* registering a normal transaction, because the global xmin might go
* backwards. That's OK, because until recovery is over we're not
* going to complete any transactions or create any non-prepared
* transactions, so there's no danger of throwing away.
*/
if ((!TransactionIdIsValid(PredXact->SxactGlobalXmin)) ||
(TransactionIdFollows(PredXact->SxactGlobalXmin, sxact->xmin)))
{
PredXact->SxactGlobalXmin = sxact->xmin;
PredXact->SxactGlobalXminCount = 1;
OldSerXidSetActiveSerXmin(sxact->xmin);
}
else if (TransactionIdEquals(sxact->xmin, PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount > 0);
PredXact->SxactGlobalXminCount++;
}
LWLockRelease(SerializableXactHashLock);
}
else if (record->type == TWOPHASEPREDICATERECORD_LOCK)
{
/* Lock record. Recreate the PREDICATELOCK */
TwoPhasePredicateLockRecord *lockRecord;
SERIALIZABLEXID *sxid;
SERIALIZABLEXACT *sxact;
SERIALIZABLEXIDTAG sxidtag;
uint32 targettaghash;
lockRecord = (TwoPhasePredicateLockRecord *) &record->data.lockRecord;
targettaghash = PredicateLockTargetTagHashCode(&lockRecord->target);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
sxidtag.xid = xid;
sxid = (SERIALIZABLEXID *)
hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
LWLockRelease(SerializableXactHashLock);
Assert(sxid != NULL);
sxact = sxid->myXact;
Assert(sxact != InvalidSerializableXact);
CreatePredicateLock(&lockRecord->target, targettaghash, sxact);
}
}
| void PredicateLockPage | ( | Relation | relation, | |
| BlockNumber | blkno, | |||
| Snapshot | snapshot | |||
| ) |
Definition at line 2440 of file predicate.c.
References RelFileNode::dbNode, PredicateLockAcquire(), RelationData::rd_id, RelationData::rd_node, SerializationNeededForRead(), and SET_PREDICATELOCKTARGETTAG_PAGE.
Referenced by _bt_endpoint(), _bt_first(), _bt_steppage(), and IndexOnlyNext().
{
PREDICATELOCKTARGETTAG tag;
if (!SerializationNeededForRead(relation, snapshot))
return;
SET_PREDICATELOCKTARGETTAG_PAGE(tag,
relation->rd_node.dbNode,
relation->rd_id,
blkno);
PredicateLockAcquire(&tag);
}
| void PredicateLockPageCombine | ( | Relation | relation, | |
| BlockNumber | oldblkno, | |||
| BlockNumber | newblkno | |||
| ) |
Definition at line 3118 of file predicate.c.
References PredicateLockPageSplit().
Referenced by _bt_pagedel().
{
/*
* Page combines differ from page splits in that we ought to be able to
* remove the locks on the old page after transferring them to the new
* page, instead of duplicating them. However, because we can't edit other
* backends' local lock tables, removing the old lock would leave them
* with an entry in their LocalPredicateLockHash for a lock they're not
* holding, which isn't acceptable. So we wind up having to do the same
* work as a page split, acquiring a lock on the new page and keeping the
* old page locked too. That can lead to some false positives, but should
* be rare in practice.
*/
PredicateLockPageSplit(relation, oldblkno, newblkno);
}
| void PredicateLockPageSplit | ( | Relation | relation, | |
| BlockNumber | oldblkno, | |||
| BlockNumber | newblkno | |||
| ) |
Definition at line 3033 of file predicate.c.
References Assert, BlockNumberIsValid, RelFileNode::dbNode, GetParentPredicateLockTag(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), PredicateLockingNeededForRelation(), RelationData::rd_id, RelationData::rd_node, SerializablePredicateLockListLock, SET_PREDICATELOCKTARGETTAG_PAGE, PredXactListData::SxactGlobalXmin, TransactionIdIsValid, and TransferPredicateLocksToNewTarget().
Referenced by _bt_insertonpg(), and PredicateLockPageCombine().
{
PREDICATELOCKTARGETTAG oldtargettag;
PREDICATELOCKTARGETTAG newtargettag;
bool success;
/*
* 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;
if (!PredicateLockingNeededForRelation(relation))
return;
Assert(oldblkno != newblkno);
Assert(BlockNumberIsValid(oldblkno));
Assert(BlockNumberIsValid(newblkno));
SET_PREDICATELOCKTARGETTAG_PAGE(oldtargettag,
relation->rd_node.dbNode,
relation->rd_id,
oldblkno);
SET_PREDICATELOCKTARGETTAG_PAGE(newtargettag,
relation->rd_node.dbNode,
relation->rd_id,
newblkno);
LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
/*
* Try copying the locks over to the new page's tag, creating it if
* necessary.
*/
success = TransferPredicateLocksToNewTarget(oldtargettag,
newtargettag,
false);
if (!success)
{
/*
* No more predicate lock entries are available. Failure isn't an
* option here, so promote the page lock to a relation lock.
*/
/* Get the parent relation lock's lock tag */
success = GetParentPredicateLockTag(&oldtargettag,
&newtargettag);
Assert(success);
/*
* Move the locks to the parent. This shouldn't fail.
*
* Note that here we are removing locks held by other backends,
* leading to a possible inconsistency in their local lock hash table.
* This is OK because we're replacing it with a lock that covers the
* old one.
*/
success = TransferPredicateLocksToNewTarget(oldtargettag,
newtargettag,
true);
Assert(success);
}
LWLockRelease(SerializablePredicateLockListLock);
}
Definition at line 2417 of file predicate.c.
References RelFileNode::dbNode, PredicateLockAcquire(), RelationData::rd_id, RelationData::rd_node, SerializationNeededForRead(), and SET_PREDICATELOCKTARGETTAG_RELATION.
Referenced by _bt_endpoint(), _bt_first(), heap_beginscan_internal(), and index_beginscan_internal().
{
PREDICATELOCKTARGETTAG tag;
if (!SerializationNeededForRead(relation, snapshot))
return;
SET_PREDICATELOCKTARGETTAG_RELATION(tag,
relation->rd_node.dbNode,
relation->rd_id);
PredicateLockAcquire(&tag);
}
| Size PredicateLockShmemSize | ( | void | ) |
Definition at line 1297 of file predicate.c.
References add_size(), hash_estimate_size(), max_prepared_xacts, MaxBackends, mul_size(), NPREDICATELOCKTARGETENTS, NUM_OLDSERXID_BUFFERS, PredXactListDataSize, PredXactListElementDataSize, RWConflictDataSize, RWConflictPoolHeaderDataSize, and SimpleLruShmemSize().
Referenced by CreateSharedMemoryAndSemaphores().
{
Size size = 0;
long max_table_size;
/* predicate lock target hash table */
max_table_size = NPREDICATELOCKTARGETENTS();
size = add_size(size, hash_estimate_size(max_table_size,
sizeof(PREDICATELOCKTARGET)));
/* predicate lock hash table */
max_table_size *= 2;
size = add_size(size, hash_estimate_size(max_table_size,
sizeof(PREDICATELOCK)));
/*
* Since NPREDICATELOCKTARGETENTS is only an estimate, add 10% safety
* margin.
*/
size = add_size(size, size / 10);
/* transaction list */
max_table_size = MaxBackends + max_prepared_xacts;
max_table_size *= 10;
size = add_size(size, PredXactListDataSize);
size = add_size(size, mul_size((Size) max_table_size,
PredXactListElementDataSize));
/* transaction xid table */
size = add_size(size, hash_estimate_size(max_table_size,
sizeof(SERIALIZABLEXID)));
/* rw-conflict pool */
max_table_size *= 5;
size = add_size(size, RWConflictPoolHeaderDataSize);
size = add_size(size, mul_size((Size) max_table_size,
RWConflictDataSize));
/* Head for list of finished serializable transactions. */
size = add_size(size, sizeof(SHM_QUEUE));
/* Shared memory structures for SLRU tracking of old committed xids. */
size = add_size(size, sizeof(OldSerXidControlData));
size = add_size(size, SimpleLruShmemSize(NUM_OLDSERXID_BUFFERS, 0));
return size;
}
Definition at line 2462 of file predicate.c.
References RelFileNode::dbNode, GetTopTransactionIdIfAny(), HeapTupleHeaderGetXmin, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, NULL, PredicateLockAcquire(), PredicateLockExists(), RelationData::rd_id, RelationData::rd_index, RelationData::rd_node, SerializationNeededForRead(), SET_PREDICATELOCKTARGETTAG_RELATION, SET_PREDICATELOCKTARGETTAG_TUPLE, SubTransGetTopmostTransaction(), HeapTupleData::t_data, HeapTupleData::t_self, TransactionIdEquals, TransactionIdFollowsOrEquals(), TransactionIdIsValid, and TransactionXmin.
Referenced by bitgetpage(), heap_fetch(), and heap_hot_search_buffer().
{
PREDICATELOCKTARGETTAG tag;
ItemPointer tid;
TransactionId targetxmin;
if (!SerializationNeededForRead(relation, snapshot))
return;
/*
* If it's a heap tuple, return if this xact wrote it.
*/
if (relation->rd_index == NULL)
{
TransactionId myxid;
targetxmin = HeapTupleHeaderGetXmin(tuple->t_data);
myxid = GetTopTransactionIdIfAny();
if (TransactionIdIsValid(myxid))
{
if (TransactionIdFollowsOrEquals(targetxmin, TransactionXmin))
{
TransactionId xid = SubTransGetTopmostTransaction(targetxmin);
if (TransactionIdEquals(xid, myxid))
{
/* We wrote it; we already have a write lock. */
return;
}
}
}
}
else
targetxmin = InvalidTransactionId;
/*
* Do quick-but-not-definitive test for a relation lock first. This will
* never cause a return when the relation is *not* locked, but will
* occasionally let the check continue when there really *is* a relation
* level lock.
*/
SET_PREDICATELOCKTARGETTAG_RELATION(tag,
relation->rd_node.dbNode,
relation->rd_id);
if (PredicateLockExists(&tag))
return;
tid = &(tuple->t_self);
SET_PREDICATELOCKTARGETTAG_TUPLE(tag,
relation->rd_node.dbNode,
relation->rd_id,
ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid),
targetxmin);
PredicateLockAcquire(&tag);
}
| void PredicateLockTwoPhaseFinish | ( | TransactionId | xid, | |
| bool | isCommit | |||
| ) |
Definition at line 4820 of file predicate.c.
References HASH_FIND, hash_search(), LW_SHARED, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXID::myXact, MyXactDidWrite, NULL, ReleasePredicateLocks(), SerializableXactHashLock, and SERIALIZABLEXIDTAG::xid.
Referenced by FinishPreparedTransaction().
{
SERIALIZABLEXID *sxid;
SERIALIZABLEXIDTAG sxidtag;
sxidtag.xid = xid;
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
sxid = (SERIALIZABLEXID *)
hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
LWLockRelease(SerializableXactHashLock);
/* xid will not be found if it wasn't a serializable transaction */
if (sxid == NULL)
return;
/* Release its locks */
MySerializableXact = sxid->myXact;
MyXactDidWrite = true; /* conservatively assume that we wrote
* something */
ReleasePredicateLocks(isCommit);
}
| void RegisterPredicateLockingXid | ( | TransactionId | xid | ) |
Definition at line 1806 of file predicate.c.
References Assert, HASH_ENTER, hash_search(), InvalidSerializableXact, InvalidTransactionId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXID::myXact, SerializableXactHashLock, SERIALIZABLEXACT::topXid, TransactionIdIsValid, and SERIALIZABLEXIDTAG::xid.
Referenced by AssignTransactionId().
{
SERIALIZABLEXIDTAG sxidtag;
SERIALIZABLEXID *sxid;
bool found;
/*
* If we're not tracking predicate lock data for this transaction, we
* should ignore the request and return quickly.
*/
if (MySerializableXact == InvalidSerializableXact)
return;
/* We should have a valid XID and be at the top level. */
Assert(TransactionIdIsValid(xid));
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/* This should only be done once per transaction. */
Assert(MySerializableXact->topXid == InvalidTransactionId);
MySerializableXact->topXid = xid;
sxidtag.xid = xid;
sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
&sxidtag,
HASH_ENTER, &found);
Assert(!found);
/* Initialize the structure. */
sxid->myXact = MySerializableXact;
LWLockRelease(SerializableXactHashLock);
}
| void ReleasePredicateLocks | ( | bool | isCommit | ) |
Definition at line 3190 of file predicate.c.
References Assert, PredXactListData::CanPartialClearThrough, ClearOldPredicateLocks(), SERIALIZABLEXACT::commitSeqNo, SERIALIZABLEXACT::earliestOutConflictCommit, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, SERIALIZABLEXACT::flags, FlagSxactUnsafe(), hash_destroy(), SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, InvalidSerializableXact, IsolationIsSerializable, SERIALIZABLEXACT::lastCommitBeforeSnapshot, PredXactListData::LastSxactCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyXactDidWrite, VariableCacheData::nextXid, NULL, offsetof, SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, SERIALIZABLEXACT::prepareSeqNo, ProcSendSignal(), ReleaseOneSerializableXact(), ReleaseRWConflict(), SERIALIZABLEXACT::SeqNo, SerializableFinishedListLock, SerializableXactHashLock, SetNewSxactGlobalXmin(), ShmemVariableCache, SHMQueueEmpty(), SHMQueueInsertBefore(), SHMQueueNext(), SXACT_FLAG_CONFLICT_OUT, PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SxactHasConflictOut, SxactHasSummaryConflictOut, RWConflictData::sxactIn, SxactIsCommitted, SxactIsDeferrableWaiting, SxactIsDoomed, SxactIsOnFinishedList, SxactIsPrepared, SxactIsReadOnly, SxactIsRolledBack, SxactIsROSafe, SxactIsROUnsafe, RWConflictData::sxactOut, TransactionIdEquals, PredXactListData::WritableSxactCount, and SERIALIZABLEXACT::xmin.
Referenced by GetSafeSnapshot(), PredicateLockTwoPhaseFinish(), ResourceOwnerReleaseInternal(), and SerializationNeededForRead().
{
bool needToClear;
RWConflict conflict,
nextConflict,
possibleUnsafeConflict;
SERIALIZABLEXACT *roXact;
/*
* We can't trust XactReadOnly here, because a transaction which started
* as READ WRITE can show as READ ONLY later, e.g., within
* substransactions. We want to flag a transaction as READ ONLY if it
* commits without writing so that de facto READ ONLY transactions get the
* benefit of some RO optimizations, so we will use this local variable to
* get some cleanup logic right which is based on whether the transaction
* was declared READ ONLY at the top level.
*/
bool topLevelIsDeclaredReadOnly;
if (MySerializableXact == InvalidSerializableXact)
{
Assert(LocalPredicateLockHash == NULL);
return;
}
Assert(!isCommit || SxactIsPrepared(MySerializableXact));
Assert(!isCommit || !SxactIsDoomed(MySerializableXact));
Assert(!SxactIsCommitted(MySerializableXact));
Assert(!SxactIsRolledBack(MySerializableXact));
/* may not be serializable during COMMIT/ROLLBACK PREPARED */
if (MySerializableXact->pid != 0)
Assert(IsolationIsSerializable());
/* We'd better not already be on the cleanup list. */
Assert(!SxactIsOnFinishedList(MySerializableXact));
topLevelIsDeclaredReadOnly = SxactIsReadOnly(MySerializableXact);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* We don't hold XidGenLock lock here, assuming that TransactionId is
* atomic!
*
* If this value is changing, we don't care that much whether we get the
* old or new value -- it is just used to determine how far
* GlobalSerizableXmin must advance before this transaction can be fully
* cleaned up. The worst that could happen is we wait for one more
* transaction to complete before freeing some RAM; correctness of visible
* behavior is not affected.
*/
MySerializableXact->finishedBefore = ShmemVariableCache->nextXid;
/*
* If it's not a commit it's a rollback, and we can clear our locks
* immediately.
*/
if (isCommit)
{
MySerializableXact->flags |= SXACT_FLAG_COMMITTED;
MySerializableXact->commitSeqNo = ++(PredXact->LastSxactCommitSeqNo);
/* Recognize implicit read-only transaction (commit without write). */
if (!MyXactDidWrite)
MySerializableXact->flags |= SXACT_FLAG_READ_ONLY;
}
else
{
/*
* The DOOMED flag indicates that we intend to roll back this
* transaction and so it should not cause serialization failures for
* other transactions that conflict with it. Note that this flag might
* already be set, if another backend marked this transaction for
* abort.
*
* The ROLLED_BACK flag further indicates that ReleasePredicateLocks
* has been called, and so the SerializableXact is eligible for
* cleanup. This means it should not be considered when calculating
* SxactGlobalXmin.
*/
MySerializableXact->flags |= SXACT_FLAG_DOOMED;
MySerializableXact->flags |= SXACT_FLAG_ROLLED_BACK;
/*
* If the transaction was previously prepared, but is now failing due
* to a ROLLBACK PREPARED or (hopefully very rare) error after the
* prepare, clear the prepared flag. This simplifies conflict
* checking.
*/
MySerializableXact->flags &= ~SXACT_FLAG_PREPARED;
}
if (!topLevelIsDeclaredReadOnly)
{
Assert(PredXact->WritableSxactCount > 0);
if (--(PredXact->WritableSxactCount) == 0)
{
/*
* Release predicate locks and rw-conflicts in for all committed
* transactions. There are no longer any transactions which might
* conflict with the locks and no chance for new transactions to
* overlap. Similarly, existing conflicts in can't cause pivots,
* and any conflicts in which could have completed a dangerous
* structure would already have caused a rollback, so any
* remaining ones must be benign.
*/
PredXact->CanPartialClearThrough = PredXact->LastSxactCommitSeqNo;
}
}
else
{
/*
* Read-only transactions: clear the list of transactions that might
* make us unsafe. Note that we use 'inLink' for the iteration as
* opposed to 'outLink' for the r/w xacts.
*/
possibleUnsafeConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&MySerializableXact->possibleUnsafeConflicts,
offsetof(RWConflictData, inLink));
while (possibleUnsafeConflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&possibleUnsafeConflict->inLink,
offsetof(RWConflictData, inLink));
Assert(!SxactIsReadOnly(possibleUnsafeConflict->sxactOut));
Assert(MySerializableXact == possibleUnsafeConflict->sxactIn);
ReleaseRWConflict(possibleUnsafeConflict);
possibleUnsafeConflict = nextConflict;
}
}
/* Check for conflict out to old committed transactions. */
if (isCommit
&& !SxactIsReadOnly(MySerializableXact)
&& SxactHasSummaryConflictOut(MySerializableXact))
{
/*
* we don't know which old committed transaction we conflicted with,
* so be conservative and use FirstNormalSerCommitSeqNo here
*/
MySerializableXact->SeqNo.earliestOutConflictCommit =
FirstNormalSerCommitSeqNo;
MySerializableXact->flags |= SXACT_FLAG_CONFLICT_OUT;
}
/*
* Release all outConflicts to committed transactions. If we're rolling
* back clear them all. Set SXACT_FLAG_CONFLICT_OUT if any point to
* previously committed transactions.
*/
conflict = (RWConflict)
SHMQueueNext(&MySerializableXact->outConflicts,
&MySerializableXact->outConflicts,
offsetof(RWConflictData, outLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->outConflicts,
&conflict->outLink,
offsetof(RWConflictData, outLink));
if (isCommit
&& !SxactIsReadOnly(MySerializableXact)
&& SxactIsCommitted(conflict->sxactIn))
{
if ((MySerializableXact->flags & SXACT_FLAG_CONFLICT_OUT) == 0
|| conflict->sxactIn->prepareSeqNo < MySerializableXact->SeqNo.earliestOutConflictCommit)
MySerializableXact->SeqNo.earliestOutConflictCommit = conflict->sxactIn->prepareSeqNo;
MySerializableXact->flags |= SXACT_FLAG_CONFLICT_OUT;
}
if (!isCommit
|| SxactIsCommitted(conflict->sxactIn)
|| (conflict->sxactIn->SeqNo.lastCommitBeforeSnapshot >= PredXact->LastSxactCommitSeqNo))
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
/*
* Release all inConflicts from committed and read-only transactions. If
* we're rolling back, clear them all.
*/
conflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&MySerializableXact->inConflicts,
offsetof(RWConflictData, inLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&conflict->inLink,
offsetof(RWConflictData, inLink));
if (!isCommit
|| SxactIsCommitted(conflict->sxactOut)
|| SxactIsReadOnly(conflict->sxactOut))
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
if (!topLevelIsDeclaredReadOnly)
{
/*
* Remove ourselves from the list of possible conflicts for concurrent
* READ ONLY transactions, flagging them as unsafe if we have a
* conflict out. If any are waiting DEFERRABLE transactions, wake them
* up if they are known safe or known unsafe.
*/
possibleUnsafeConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&MySerializableXact->possibleUnsafeConflicts,
offsetof(RWConflictData, outLink));
while (possibleUnsafeConflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&possibleUnsafeConflict->outLink,
offsetof(RWConflictData, outLink));
roXact = possibleUnsafeConflict->sxactIn;
Assert(MySerializableXact == possibleUnsafeConflict->sxactOut);
Assert(SxactIsReadOnly(roXact));
/* Mark conflicted if necessary. */
if (isCommit
&& MyXactDidWrite
&& SxactHasConflictOut(MySerializableXact)
&& (MySerializableXact->SeqNo.earliestOutConflictCommit
<= roXact->SeqNo.lastCommitBeforeSnapshot))
{
/*
* This releases possibleUnsafeConflict (as well as all other
* possible conflicts for roXact)
*/
FlagSxactUnsafe(roXact);
}
else
{
ReleaseRWConflict(possibleUnsafeConflict);
/*
* If we were the last possible conflict, flag it safe. The
* transaction can now safely release its predicate locks (but
* that transaction's backend has to do that itself).
*/
if (SHMQueueEmpty(&roXact->possibleUnsafeConflicts))
roXact->flags |= SXACT_FLAG_RO_SAFE;
}
/*
* Wake up the process for a waiting DEFERRABLE transaction if we
* now know it's either safe or conflicted.
*/
if (SxactIsDeferrableWaiting(roXact) &&
(SxactIsROUnsafe(roXact) || SxactIsROSafe(roXact)))
ProcSendSignal(roXact->pid);
possibleUnsafeConflict = nextConflict;
}
}
/*
* Check whether it's time to clean up old transactions. This can only be
* done when the last serializable transaction with the oldest xmin among
* serializable transactions completes. We then find the "new oldest"
* xmin and purge any transactions which finished before this transaction
* was launched.
*/
needToClear = false;
if (TransactionIdEquals(MySerializableXact->xmin, PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount > 0);
if (--(PredXact->SxactGlobalXminCount) == 0)
{
SetNewSxactGlobalXmin();
needToClear = true;
}
}
LWLockRelease(SerializableXactHashLock);
LWLockAcquire(SerializableFinishedListLock, LW_EXCLUSIVE);
/* Add this to the list of transactions to check for later cleanup. */
if (isCommit)
SHMQueueInsertBefore(FinishedSerializableTransactions,
&MySerializableXact->finishedLink);
if (!isCommit)
ReleaseOneSerializableXact(MySerializableXact, false, false);
LWLockRelease(SerializableFinishedListLock);
if (needToClear)
ClearOldPredicateLocks();
MySerializableXact = InvalidSerializableXact;
MyXactDidWrite = false;
/* Delete per-transaction lock table */
if (LocalPredicateLockHash != NULL)
{
hash_destroy(LocalPredicateLockHash);
LocalPredicateLockHash = NULL;
}
}
| void SetSerializableTransactionSnapshot | ( | Snapshot | snapshot, | |
| TransactionId | sourcexid | |||
| ) |
Definition at line 1612 of file predicate.c.
References Assert, ereport, errcode(), errmsg(), ERROR, GetSerializableTransactionSnapshotInt(), IsolationIsSerializable, XactDeferrable, and XactReadOnly.
Referenced by SetTransactionSnapshot().
{
Assert(IsolationIsSerializable());
/*
* We do not allow SERIALIZABLE READ ONLY DEFERRABLE transactions to
* import snapshots, since there's no way to wait for a safe snapshot when
* we're using the snap we're told to. (XXX instead of throwing an error,
* we could just ignore the XactDeferrable flag?)
*/
if (XactReadOnly && XactDeferrable)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("a snapshot-importing transaction must not be READ ONLY DEFERRABLE")));
(void) GetSerializableTransactionSnapshotInt(snapshot, sourcexid);
}
| void TransferPredicateLocksToHeapRelation | ( | Relation | relation | ) |
Definition at line 3012 of file predicate.c.
References DropAllPredicateLocksFromTable().
Referenced by ATRewriteTable(), cluster_rel(), index_drop(), and reindex_index().
{
DropAllPredicateLocksFromTable(relation, true);
}
Definition at line 351 of file predicate.c.
Referenced by GetSerializableTransactionSnapshotInt(), and PredicateLockPromotionThreshold().
1.7.1