#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().