/* * Find a page in a shared buffer, reading it in if necessary. * The page number must correspond to an already-initialized page. * The caller must intend only read-only access to the page. * * The passed-in xid is used only for error reporting, and may be * InvalidTransactionId if no specific xid is associated with the action. * * Return value is the shared-buffer slot number now holding the page. * The buffer's LRU access info is updated. * * Control lock must NOT be held at entry, but will be held at exit. * It is unspecified whether the lock will be shared or exclusive. */ int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid) { SlruShared shared = ctl->shared; int slotno; /* Try to find the page while holding only shared lock */ LWLockAcquire(shared->ControlLock, LW_SHARED); /* See if page is already in a buffer */ for (slotno = 0; slotno < shared->num_slots; slotno++) { if (shared->page_number[slotno] == pageno && shared->page_status[slotno] != SLRU_PAGE_EMPTY && shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS) { /* See comments for SlruRecentlyUsed macro */ SlruRecentlyUsed(shared, slotno); return slotno; } } /* No luck, so switch to normal exclusive lock and do regular read */ LWLockRelease(shared->ControlLock); LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE); return SimpleLruReadPage(ctl, pageno, true, xid); }
/* * Record the parent of a subtransaction in the subtrans log. */ void SubTransSetParent(TransactionId xid, TransactionId parent) { int pageno = TransactionIdToPage(xid); int entryno = TransactionIdToEntry(xid); int slotno; TransactionId *ptr; Assert(TransactionIdIsValid(parent)); Assert(TransactionIdFollows(xid, parent)); LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid); ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno]; ptr += entryno; /* * It's possible we'll try to set the parent xid multiple times but we * shouldn't ever be changing the xid from one valid xid to another valid * xid, which would corrupt the data structure. */ if (*ptr != parent) { Assert(*ptr == InvalidTransactionId); *ptr = parent; SubTransCtl->shared->page_dirty[slotno] = true; } LWLockRelease(SubtransControlLock); }
/* * Record the parent of a subtransaction in the subtrans log. * * In some cases we may need to overwrite an existing value. */ void SubTransSetParent(TransactionId xid, TransactionId parent, bool overwriteOK) { int pageno = TransactionIdToPage(xid); int entryno = TransactionIdToEntry(xid); int slotno; TransactionId *ptr; Assert(TransactionIdIsValid(parent)); LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid); ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno]; ptr += entryno; /* Current state should be 0 */ Assert(*ptr == InvalidTransactionId || (*ptr == parent && overwriteOK)); *ptr = parent; SubTransCtl->shared->page_dirty[slotno] = true; LWLockRelease(SubtransControlLock); }
/* * Add local XID for any new distributed transactions. */ void PreallocLocalXidsForOpenDistributedTransactions(DistributedTransactionId *gxidArray, uint32 count) { int i; DistributedTransactionId gxid; int pageno; int entryno; int slotno; DISTRIBUTEDXIDMAP_ENTRY *ptr; LWLockAcquire(DistributedXidMapControlLock, LW_EXCLUSIVE); for (i = 0; i < count; i++) { gxid = gxidArray[i]; pageno = DistributedTransactionIdToPage(gxid); entryno = DistributedTransactionIdToEntry(gxid); elog((Debug_print_full_dtm ? LOG : DEBUG5), "PreallocLocalXidsForOpenDistributedTransactions: gxidArray[%d] is %u (pageno %d, entryno %d)", i, gxid, pageno, entryno); if (pageno > *shmDistributedXidMapHighestPageNo) { /* * Zero out the new page(s). */ DistributedXidMapMakeMorePages(pageno); } if (*shmMaxDistributedXid < gxid) { *shmMaxDistributedXid = gxid; } slotno = SimpleLruReadPage(DistributedXidMapCtl, pageno, InvalidTransactionId); ptr = (DISTRIBUTEDXIDMAP_ENTRY *) DistributedXidMapCtl->shared->page_buffer[slotno]; ptr += entryno; if (ptr->state == DISTRIBUTEDXIDMAP_STATE_NONE) { ptr->state = DISTRIBUTEDXIDMAP_STATE_PREALLOC_FOR_OPEN_TRANS; ptr->pid = MyProcPid; ptr->xid = GetNewTransactionId(false, false); // NOT subtrans, DO NOT Set PROC struct xid DistributedXidMapCtl->shared->page_dirty[slotno] = true; elog((Debug_print_full_dtm ? LOG : DEBUG5), "PreallocLocalXidsForOpenDistributedTransactions: Allocated local XID = %u for global XID = %u", ptr->xid, gxid); } } LWLockRelease(DistributedXidMapControlLock); }
void UpdateDistributedXidMapState(DistributedTransactionId gxid, DistributedMapState newState) { int pageno = DistributedTransactionIdToPage(gxid); int entryno = DistributedTransactionIdToEntry(gxid); int slotno; DISTRIBUTEDXIDMAP_ENTRY *ptr; DistributedMapState oldState; TransactionId xid; Assert(newState == DISTRIBUTEDXIDMAP_STATE_PREPARED || newState == DISTRIBUTEDXIDMAP_STATE_COMMITTED || newState == DISTRIBUTEDXIDMAP_STATE_ABORTED); elog((Debug_print_full_dtm ? LOG : DEBUG5), "Entering UpdateDistributedXidMapState with distributed xid = %d (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", gxid, pageno, entryno, *shmDistributedXidMapHighestPageNo); LWLockAcquire(DistributedXidMapControlLock, LW_EXCLUSIVE); if (pageno > *shmDistributedXidMapHighestPageNo) { LWLockRelease(DistributedXidMapControlLock); elog(ERROR, "UpdateDistributedXidMapState distributed xid = %d invalid (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", gxid, pageno, entryno, *shmDistributedXidMapHighestPageNo); } slotno = SimpleLruReadPage(DistributedXidMapCtl, pageno, InvalidTransactionId); ptr = (DISTRIBUTEDXIDMAP_ENTRY *) DistributedXidMapCtl->shared->page_buffer[slotno]; ptr += entryno; oldState = ptr->state; if (oldState == DISTRIBUTEDXIDMAP_STATE_IN_PROGRESS) { ptr->state = newState; } else if (oldState == DISTRIBUTEDXIDMAP_STATE_PREPARED) { Assert(newState != DISTRIBUTEDXIDMAP_STATE_PREPARED); ptr->state = newState; } xid = ptr->xid; DistributedXidMapCtl->shared->page_dirty[slotno] = true; LWLockRelease(DistributedXidMapControlLock); elog((Debug_print_full_dtm ? LOG : DEBUG5), "UpdateDistributedXidMapState state for local xid = %u and distributed xid = %d from state %s to new state %s (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", xid, gxid, DistributedMapStateToString(oldState), DistributedMapStateToString(newState),pageno, entryno, *shmDistributedXidMapHighestPageNo); }
/* * Record the parent of a subtransaction in the subtrans log. */ void SubTransSetParent(TransactionId xid, TransactionId parent) { MIRRORED_LOCK_DECLARE; int pageno = TransactionIdToPage(xid); int entryno = TransactionIdToEntry(xid); int slotno; SubTransData *ptr; SubTransData subData; /* * Main Xact has parent and topMostParent as InvalidTransactionId */ if ( parent != InvalidTransactionId ) { /* Get the topMostParent for Parent */ SubTransGetData(parent, &subData); } else { subData.topMostParent = InvalidTransactionId; } MIRRORED_LOCK; LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); slotno = SimpleLruReadPage(SubTransCtl, pageno, xid); ptr = (SubTransData *) SubTransCtl->shared->page_buffer[slotno]; ptr += entryno; /* Current state should be 0 */ Assert(ptr->parent == InvalidTransactionId); Assert(ptr->topMostParent == InvalidTransactionId); ptr->parent = parent; ptr->topMostParent = subData.topMostParent; SubTransCtl->shared->page_dirty[slotno] = true; LWLockRelease(SubtransControlLock); MIRRORED_UNLOCK; }
/* * Record the commit timestamp of transaction entries in the commit log for all * entries on a single page. Atomic only on this page. */ static void SetXidCommitTsInPage(TransactionId xid, int nsubxids, TransactionId *subxids, TimestampTz ts, RepOriginId nodeid, int pageno) { int slotno; int i; LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE); slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, xid); TransactionIdSetCommitTs(xid, ts, nodeid, slotno); for (i = 0; i < nsubxids; i++) TransactionIdSetCommitTs(subxids[i], ts, nodeid, slotno); CommitTsCtl->shared->page_dirty[slotno] = true; LWLockRelease(CommitTsControlLock); }
/* * Get the local XID associated with a distributed transaction. */ TransactionId GetLocalXidForDistributedTransaction(DistributedTransactionId gxid) { int pageno = DistributedTransactionIdToPage(gxid); int entryno = DistributedTransactionIdToEntry(gxid); int slotno; DISTRIBUTEDXIDMAP_ENTRY *ptr; TransactionId xid; DistributedMapState state; elog((Debug_print_full_dtm ? LOG : DEBUG5), "Entering GetLocalXidForDistributedTransaction with distributed xid = %d (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", gxid, pageno, entryno, *shmDistributedXidMapHighestPageNo); LWLockAcquire(DistributedXidMapControlLock, LW_EXCLUSIVE); if (pageno > *shmDistributedXidMapHighestPageNo) { LWLockRelease(DistributedXidMapControlLock); elog((Debug_print_full_dtm ? LOG : DEBUG5), "GetLocalXidForDistributedTransaction returning InvalidTransactionId for local xid for distributed xid = %d (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", gxid, pageno, entryno, *shmDistributedXidMapHighestPageNo); return InvalidTransactionId; } slotno = SimpleLruReadPage(DistributedXidMapCtl, pageno, InvalidTransactionId); ptr = (DISTRIBUTEDXIDMAP_ENTRY *) DistributedXidMapCtl->shared->page_buffer[slotno]; ptr += entryno; xid = ptr->xid; state = ptr->state; LWLockRelease(DistributedXidMapControlLock); elog((Debug_print_full_dtm ? LOG : DEBUG5), "GetLocalXidForDistributedTransaction found local xid = %u for distributed xid = %d in state %s (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", xid, gxid, DistributedMapStateToString(state), pageno, entryno, *shmDistributedXidMapHighestPageNo); return xid; }
/* * Get the local XID associated with a distributed transaction. We will * allocate the local XID and assign its value in the map if necessary. */ void AllocOrGetLocalXidForStartDistributedTransaction(DistributedTransactionId gxid, TransactionId *xid) { int pageno = DistributedTransactionIdToPage(gxid); int entryno = DistributedTransactionIdToEntry(gxid); int slotno; DISTRIBUTEDXIDMAP_ENTRY *ptr; Assert(gxid != InvalidDistributedTransactionId); Assert(xid != NULL); elog((Debug_print_full_dtm ? LOG : DEBUG5), "Entering AllocOrGetLocalXidForStartDistributedTransaction with distributed xid = %d (pageno = %d, entryno = %d, DistributedXidMapHighestPageNo = %d)", gxid, pageno, entryno, *shmDistributedXidMapHighestPageNo); LWLockAcquire(DistributedXidMapControlLock, LW_EXCLUSIVE); if (pageno > *shmDistributedXidMapHighestPageNo) { /* * Zero out the new page(s). */ DistributedXidMapMakeMorePages(pageno); } if (*shmMaxDistributedXid < gxid) { *shmMaxDistributedXid = gxid; } slotno = SimpleLruReadPage(DistributedXidMapCtl, pageno, InvalidTransactionId); ptr = (DISTRIBUTEDXIDMAP_ENTRY *) DistributedXidMapCtl->shared->page_buffer[slotno]; ptr += entryno; if (ptr->state == DISTRIBUTEDXIDMAP_STATE_NONE) { /* * Need to allocate a local XID and assign the map entry. */ *xid = GetNewTransactionId(false, false); // NOT subtrans, DO NOT Set PROC struct xid ptr->state = DISTRIBUTEDXIDMAP_STATE_IN_PROGRESS; ptr->pid = MyProcPid; ptr->xid = *xid; elog((Debug_print_full_dtm ? LOG : DEBUG5), "AllocOrGetLocalXidForStartDistributedTransaction allocated local XID = %d", *xid); DistributedXidMapCtl->shared->page_dirty[slotno] = true; } else if (ptr->state == DISTRIBUTEDXIDMAP_STATE_PREALLOC_FOR_OPEN_TRANS) { /* * The local XID was pre-allocated by another QE when it * received a distributed snapshot that listed the distributed transaction * in its in-doubt list. */ ptr->state = DISTRIBUTEDXIDMAP_STATE_IN_PROGRESS; ptr->pid = MyProcPid; *xid = ptr->xid; elog((Debug_print_full_dtm ? LOG : DEBUG5), "AllocOrGetLocalXidForStartDistributedTransaction found pre-allocated local XID = %d", *xid); DistributedXidMapCtl->shared->page_dirty[slotno] = true; } else { int pid = ptr->pid; TransactionId reuseXid = ptr->xid; DistributedMapState state = ptr->state; LWLockRelease(DistributedXidMapControlLock); elog(ERROR,"Attempting re-use local xid %u and distributed xid %u again (original start pid was %d, state = %s, pageno %d, entryno %d, slotno %d)", reuseXid, gxid, pid, DistributedMapStateToString(state), pageno, entryno, slotno); } SetProcXid(*xid, gxid); LWLockRelease(DistributedXidMapControlLock); }
/* * Record that a distributed transaction committed in the distributed log. * */ void DistributedLog_SetCommitted( TransactionId localXid, DistributedTransactionTimeStamp distribTimeStamp, DistributedTransactionId distribXid, bool isRedo) { MIRRORED_LOCK_DECLARE; int page = TransactionIdToPage(localXid); int entryno = TransactionIdToEntry(localXid); int slotno; DistributedLogEntry *ptr; bool alreadyThere = false; MIRRORED_LOCK; LWLockAcquire(DistributedLogControlLock, LW_EXCLUSIVE); if (isRedo) { elog((Debug_print_full_dtm ? LOG : DEBUG5), "DistributedLog_SetCommitted check if page %d is present", page); if (!SimpleLruPageExists(DistributedLogCtl, page)) { DistributedLog_ZeroPage(page, /* writeXLog */ false); elog((Debug_print_full_dtm ? LOG : DEBUG5), "DistributedLog_SetCommitted zeroed page %d", page); } } slotno = SimpleLruReadPage(DistributedLogCtl, page, localXid); ptr = (DistributedLogEntry *) DistributedLogCtl->shared->page_buffer[slotno]; ptr += entryno; if (ptr->distribTimeStamp != 0 || ptr->distribXid != 0) { if (ptr->distribTimeStamp != distribTimeStamp) elog(ERROR, "Current distributed timestamp = %u does not match input timestamp = %u for local xid = %u in distributed log (page = %d, entryno = %d)", ptr->distribTimeStamp, distribTimeStamp, localXid, page, entryno); if (ptr->distribXid != distribXid) elog(ERROR, "Current distributed xid = %u does not match input distributed xid = %u for local xid = %u in distributed log (page = %d, entryno = %d)", ptr->distribXid, distribXid, localXid, page, entryno); alreadyThere = true; } else { ptr->distribTimeStamp = distribTimeStamp; ptr->distribXid = distribXid; DistributedLogCtl->shared->page_dirty[slotno] = true; } LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; elog((Debug_print_full_dtm ? LOG : DEBUG5), "DistributedLog_SetCommitted with local xid = %d (page = %d, entryno = %d) and distributed transaction xid = %u (timestamp = %u) status = %s", localXid, page, entryno, distribXid, distribTimeStamp, (alreadyThere ? "already there" : "set")); }
/* * This must be called ONCE during postmaster or standalone-backend startup, * after StartupXLOG has initialized ShmemVariableCache->nextXid. */ void DistributedLog_Startup( TransactionId oldestActiveXid, TransactionId nextXid) { MIRRORED_LOCK_DECLARE; int startPage; int endPage; /* * UNDONE: We really need oldest frozen xid. If we can't get it, then * we will need to tolerate not finiding a page in * DistributedLog_SetCommitted and DistributedLog_IsCommitted. */ startPage = TransactionIdToPage(oldestActiveXid); endPage = TransactionIdToPage(nextXid); MIRRORED_LOCK; LWLockAcquire(DistributedLogControlLock, LW_EXCLUSIVE); elog((Debug_print_full_dtm ? LOG : DEBUG5), "DistributedLog_Startup startPage %d, endPage %d", startPage, endPage); /* * Initialize our idea of the latest page number. */ DistributedLogCtl->shared->latest_page_number = endPage; /* * Zero out the remainder of the current DistributedLog page. Under normal * circumstances it should be zeroes already, but it seems at least * theoretically possible that XLOG replay will have settled on a nextXID * value that is less than the last XID actually used and marked by the * previous database lifecycle (since subtransaction commit writes clog * but makes no WAL entry). Let's just be safe. (We need not worry about * pages beyond the current one, since those will be zeroed when first * used. For the same reason, there is no need to do anything when * nextXid is exactly at a page boundary; and it's likely that the * "current" page doesn't exist yet in that case.) */ if (TransactionIdToEntry(nextXid) != 0) { int entryno = TransactionIdToEntry(nextXid); int slotno; DistributedLogEntry *ptr; int remainingEntries; slotno = SimpleLruReadPage(DistributedLogCtl, endPage, nextXid); ptr = (DistributedLogEntry *) DistributedLogCtl->shared->page_buffer[slotno]; ptr += entryno; /* Zero the rest of the page */ remainingEntries = ENTRIES_PER_PAGE - entryno; MemSet(ptr, 0, remainingEntries * sizeof(DistributedLogEntry)); DistributedLogCtl->shared->page_dirty[slotno] = true; } LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; }
/* * Find the next lowest transaction with a logged or recorded status. * Currently on distributed commits are recorded. */ bool DistributedLog_ScanForPrevCommitted( TransactionId *indexXid, DistributedTransactionTimeStamp *distribTimeStamp, DistributedTransactionId *distribXid) { MIRRORED_LOCK_DECLARE; TransactionId highXid; int pageno; TransactionId lowXid; int slotno; TransactionId xid; *distribTimeStamp = 0; // Set it to something. *distribXid = 0; if ((*indexXid) == InvalidTransactionId) return false; highXid = (*indexXid) - 1; if (highXid < FirstNormalTransactionId) return false; MIRRORED_LOCK; while (true) { pageno = TransactionIdToPage(highXid); /* * Compute the xid floor for the page. */ lowXid = pageno * (TransactionId) ENTRIES_PER_PAGE; if (lowXid == InvalidTransactionId) lowXid = FirstNormalTransactionId; LWLockAcquire(DistributedLogControlLock, LW_EXCLUSIVE); /* * Peek to see if page exists. */ if (!SimpleLruPageExists(DistributedLogCtl, pageno)) { LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; *indexXid = InvalidTransactionId; *distribTimeStamp = 0; // Set it to something. *distribXid = 0; return false; } slotno = SimpleLruReadPage(DistributedLogCtl, pageno, highXid); for (xid = highXid; xid >= lowXid; xid--) { int entryno = TransactionIdToEntry(xid); DistributedLogEntry *ptr; ptr = (DistributedLogEntry *) DistributedLogCtl->shared->page_buffer[slotno]; ptr += entryno; if (ptr->distribTimeStamp != 0 && ptr->distribXid != 0) { *indexXid = xid; *distribTimeStamp = ptr->distribTimeStamp; *distribXid = ptr->distribXid; LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; return true; } } LWLockRelease(DistributedLogControlLock); if (lowXid == FirstNormalTransactionId) { MIRRORED_UNLOCK; *indexXid = InvalidTransactionId; *distribTimeStamp = 0; // Set it to something. *distribXid = 0; return false; } highXid = lowXid - 1; // Go to last xid of previous page. } MIRRORED_UNLOCK; return false; // We'll never reach this. }
/* * Determine if a distributed transaction committed in the distributed log. */ bool DistributedLog_CommittedCheck( TransactionId localXid, DistributedTransactionTimeStamp *distribTimeStamp, DistributedTransactionId *distribXid) { MIRRORED_LOCK_DECLARE; int page = TransactionIdToPage(localXid); int entryno = TransactionIdToEntry(localXid); int slotno; DistributedLogEntry *ptr; MIRRORED_LOCK; LWLockAcquire(DistributedLogControlLock, LW_EXCLUSIVE); if (DistributedLogShared->knowHighestUnusedPage && page <= DistributedLogShared->highestUnusedPage) { /* * We prevously discovered we didn't have the page... */ LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; *distribTimeStamp = 0; // Set it to something. *distribXid = 0; return false; } /* * Peek to see if page exists. */ if (!SimpleLruPageExists(DistributedLogCtl, page)) { if (DistributedLogShared->knowHighestUnusedPage) { if (DistributedLogShared->highestUnusedPage > page) DistributedLogShared->highestUnusedPage = page; } else { DistributedLogShared->knowHighestUnusedPage = true; DistributedLogShared->highestUnusedPage = page; } LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; *distribTimeStamp = 0; // Set it to something. *distribXid = 0; return false; } slotno = SimpleLruReadPage(DistributedLogCtl, page, localXid); ptr = (DistributedLogEntry *) DistributedLogCtl->shared->page_buffer[slotno]; ptr += entryno; *distribTimeStamp = ptr->distribTimeStamp; *distribXid = ptr->distribXid; ptr = NULL; LWLockRelease(DistributedLogControlLock); MIRRORED_UNLOCK; if (*distribTimeStamp != 0 && *distribXid != 0) { return true; } else if (*distribTimeStamp == 0 && *distribXid == 0) { // Not found. return false; } else { if (*distribTimeStamp == 0) elog(ERROR, "Found zero timestamp for local xid = %u in distributed log (distributed xid = %u, page = %d, entryno = %d)", localXid, *distribXid, page, entryno); elog(ERROR, "Found zero distributed xid for local xid = %u in distributed log (dtx start time = %u, page = %d, entryno = %d)", localXid, *distribTimeStamp, page, entryno); return false; // We'll never reach here. } }