📄 multixact.c
字号:
slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset); xidptr = (TransactionId *) MultiXactMemberCtl->shared->page_buffer[slotno]; xidptr += entryno; MemSet(xidptr, 0, BLCKSZ - (entryno * sizeof(TransactionId))); MultiXactMemberCtl->shared->page_dirty[slotno] = true; } LWLockRelease(MultiXactMemberControlLock); /* * Initialize lastTruncationPoint to invalid, ensuring that the first * checkpoint will try to do truncation. */ MultiXactState->lastTruncationPoint = InvalidMultiXactId;}/* * This must be called ONCE during postmaster or standalone-backend shutdown */voidShutdownMultiXact(void){ /* Flush dirty MultiXact pages to disk */ SimpleLruFlush(MultiXactOffsetCtl, false); SimpleLruFlush(MultiXactMemberCtl, false);}/* * Get the next MultiXactId and offset to save in a checkpoint record */voidMultiXactGetCheckptMulti(bool is_shutdown, MultiXactId *nextMulti, MultiXactOffset *nextMultiOffset){ LWLockAcquire(MultiXactGenLock, LW_SHARED); *nextMulti = MultiXactState->nextMXact; *nextMultiOffset = MultiXactState->nextOffset; LWLockRelease(MultiXactGenLock); debug_elog4(DEBUG2, "MultiXact: checkpoint is nextMulti %u, nextOffset %u", *nextMulti, *nextMultiOffset);}/* * Perform a checkpoint --- either during shutdown, or on-the-fly */voidCheckPointMultiXact(void){ /* Flush dirty MultiXact pages to disk */ SimpleLruFlush(MultiXactOffsetCtl, true); SimpleLruFlush(MultiXactMemberCtl, true); /* * Truncate the SLRU files. This could be done at any time, but * checkpoint seems a reasonable place for it. There is one exception: if * we are called during xlog recovery, then shared->latest_page_number * isn't valid (because StartupMultiXact hasn't been called yet) and so * SimpleLruTruncate would get confused. It seems best not to risk * removing any data during recovery anyway, so don't truncate. */ if (!InRecovery) TruncateMultiXact();}/* * Set the next-to-be-assigned MultiXactId and offset * * This is used when we can determine the correct next ID/offset exactly * from a checkpoint record. We need no locking since it is only called * during bootstrap and XLog replay. */voidMultiXactSetNextMXact(MultiXactId nextMulti, MultiXactOffset nextMultiOffset){ debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %u", nextMulti, nextMultiOffset); MultiXactState->nextMXact = nextMulti; MultiXactState->nextOffset = nextMultiOffset;}/* * Ensure the next-to-be-assigned MultiXactId is at least minMulti, * and similarly nextOffset is at least minMultiOffset * * This is used when we can determine minimum safe values from an XLog * record (either an on-line checkpoint or an mxact creation log entry). * We need no locking since it is only called during XLog replay. */voidMultiXactAdvanceNextMXact(MultiXactId minMulti, MultiXactOffset minMultiOffset){ if (MultiXactIdPrecedes(MultiXactState->nextMXact, minMulti)) { debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti); MultiXactState->nextMXact = minMulti; } if (MultiXactOffsetPrecedes(MultiXactState->nextOffset, minMultiOffset)) { debug_elog3(DEBUG2, "MultiXact: setting next offset to %u", minMultiOffset); MultiXactState->nextOffset = minMultiOffset; }}/* * Make sure that MultiXactOffset has room for a newly-allocated MultiXactId. * * NB: this is called while holding MultiXactGenLock. We want it to be very * fast most of the time; even when it's not so fast, no actual I/O need * happen unless we're forced to write out a dirty log or xlog page to make * room in shared memory. */static voidExtendMultiXactOffset(MultiXactId multi){ int pageno; /* * No work except at first MultiXactId of a page. But beware: just after * wraparound, the first MultiXactId of page zero is FirstMultiXactId. */ if (MultiXactIdToOffsetEntry(multi) != 0 && multi != FirstMultiXactId) return; pageno = MultiXactIdToOffsetPage(multi); LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE); /* Zero the page and make an XLOG entry about it */ ZeroMultiXactOffsetPage(pageno, true); LWLockRelease(MultiXactOffsetControlLock);}/* * Make sure that MultiXactMember has room for the members of a newly- * allocated MultiXactId. * * Like the above routine, this is called while holding MultiXactGenLock; * same comments apply. */static voidExtendMultiXactMember(MultiXactOffset offset, int nmembers){ /* * It's possible that the members span more than one page of the members * file, so we loop to ensure we consider each page. The coding is not * optimal if the members span several pages, but that seems unusual * enough to not worry much about. */ while (nmembers > 0) { int entryno; /* * Only zero when at first entry of a page. */ entryno = MXOffsetToMemberEntry(offset); if (entryno == 0) { int pageno; pageno = MXOffsetToMemberPage(offset); LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE); /* Zero the page and make an XLOG entry about it */ ZeroMultiXactMemberPage(pageno, true); LWLockRelease(MultiXactMemberControlLock); } /* Advance to next page (OK if nmembers goes negative) */ offset += (MULTIXACT_MEMBERS_PER_PAGE - entryno); nmembers -= (MULTIXACT_MEMBERS_PER_PAGE - entryno); }}/* * Remove all MultiXactOffset and MultiXactMember segments before the oldest * ones still of interest. * * This is called only during checkpoints. We assume no more than one * backend does this at a time. * * XXX do we have any issues with needing to checkpoint here? */static voidTruncateMultiXact(void){ MultiXactId nextMXact; MultiXactOffset nextOffset; MultiXactId oldestMXact; MultiXactOffset oldestOffset; int cutoffPage; int i; /* * First, compute where we can safely truncate. Per notes above, this is * the oldest valid value among all the OldestMemberMXactId[] and * OldestVisibleMXactId[] entries, or nextMXact if none are valid. */ LWLockAcquire(MultiXactGenLock, LW_SHARED); /* * We have to beware of the possibility that nextMXact is in the * wrapped-around state. We don't fix the counter itself here, but we * must be sure to use a valid value in our calculation. */ nextMXact = MultiXactState->nextMXact; if (nextMXact < FirstMultiXactId) nextMXact = FirstMultiXactId; oldestMXact = nextMXact; for (i = 1; i <= MaxBackends; i++) { MultiXactId thisoldest; thisoldest = OldestMemberMXactId[i]; if (MultiXactIdIsValid(thisoldest) && MultiXactIdPrecedes(thisoldest, oldestMXact)) oldestMXact = thisoldest; thisoldest = OldestVisibleMXactId[i]; if (MultiXactIdIsValid(thisoldest) && MultiXactIdPrecedes(thisoldest, oldestMXact)) oldestMXact = thisoldest; } /* Save the current nextOffset too */ nextOffset = MultiXactState->nextOffset; LWLockRelease(MultiXactGenLock); debug_elog3(DEBUG2, "MultiXact: truncation point = %u", oldestMXact); /* * If we already truncated at this point, do nothing. This saves time * when no MultiXacts are getting used, which is probably not uncommon. */ if (MultiXactState->lastTruncationPoint == oldestMXact) return; /* * We need to determine where to truncate MultiXactMember. If we found a * valid oldest MultiXactId, read its starting offset; otherwise we use * the nextOffset value we saved above. */ if (oldestMXact == nextMXact) oldestOffset = nextOffset; else { int pageno; int slotno; int entryno; MultiXactOffset *offptr; /* lock is acquired by SimpleLruReadPage_ReadOnly */ pageno = MultiXactIdToOffsetPage(oldestMXact); entryno = MultiXactIdToOffsetEntry(oldestMXact); slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, oldestMXact); offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; offptr += entryno; oldestOffset = *offptr; LWLockRelease(MultiXactOffsetControlLock); } /* * The cutoff point is the start of the segment containing oldestMXact. We * pass the *page* containing oldestMXact to SimpleLruTruncate. */ cutoffPage = MultiXactIdToOffsetPage(oldestMXact); SimpleLruTruncate(MultiXactOffsetCtl, cutoffPage); /* * Also truncate MultiXactMember at the previously determined offset. */ cutoffPage = MXOffsetToMemberPage(oldestOffset); SimpleLruTruncate(MultiXactMemberCtl, cutoffPage); /* * Set the last known truncation point. We don't need a lock for this * since only one backend does checkpoints at a time. */ MultiXactState->lastTruncationPoint = oldestMXact;}/* * Decide which of two MultiXactOffset page numbers is "older" for truncation * purposes. * * We need to use comparison of MultiXactId here in order to do the right * thing with wraparound. However, if we are asked about page number zero, we * don't want to hand InvalidMultiXactId to MultiXactIdPrecedes: it'll get * weird. So, offset both multis by FirstMultiXactId to avoid that. * (Actually, the current implementation doesn't do anything weird with * InvalidMultiXactId, but there's no harm in leaving this code like this.) */static boolMultiXactOffsetPagePrecedes(int page1, int page2){ MultiXactId multi1; MultiXactId multi2; multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE; multi1 += FirstMultiXactId; multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE; multi2 += FirstMultiXactId; return MultiXactIdPrecedes(multi1, multi2);}/* * Decide which of two MultiXactMember page numbers is "older" for truncation * purposes. There is no "invalid offset number" so use the numbers verbatim. */static boolMultiXactMemberPagePrecedes(int page1, int page2){ MultiXactOffset offset1; MultiXactOffset offset2; offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE; offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE; return MultiXactOffsetPrecedes(offset1, offset2);}/* * Decide which of two MultiXactIds is earlier. * * XXX do we need to do something special for InvalidMultiXactId? * (Doesn't look like it.) */static boolMultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2){ int32 diff = (int32) (multi1 - multi2); return (diff < 0);}/* * Decide which of two offsets is earlier. */static boolMultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2){ int32 diff = (int32) (offset1 - offset2); return (diff < 0);}/* * Write an xlog record reflecting the zeroing of either a MEMBERs or * OFFSETs page (info shows which) */static voidWriteMZeroPageXlogRec(int pageno, uint8 info){ XLogRecData rdata; rdata.data = (char *) (&pageno); rdata.len = sizeof(int); rdata.buffer = InvalidBuffer; rdata.next = NULL; (void) XLogInsert(RM_MULTIXACT_ID, info, &rdata);}/* * MULTIXACT resource manager's routines */voidmultixact_redo(XLogRecPtr lsn, XLogRecord *record){ uint8 info = record->xl_info & ~XLR_INFO_MASK; if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE) { int pageno; int slotno; memcpy(&pageno, XLogRecGetData(record), sizeof(int)); LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE); slotno = ZeroMultiXactOffsetPage(pageno, false); SimpleLruWritePage(MultiXactOffsetCtl, slotno, NULL); Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]); LWLockRelease(MultiXactOffsetControlLock); } else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE) { int pageno; int slotno; memcpy(&pageno, XLogRecGetData(record), sizeof(int)); LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE); slotno = ZeroMultiXactMemberPage(pageno, false); SimpleLruWritePage(MultiXactMemberCtl, slotno, NULL); Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]); LWLockRelease(MultiXactMemberControlLock); } else if (info == XLOG_MULTIXACT_CREATE_ID) { xl_multixact_create *xlrec = (xl_multixact_create *) XLogRecGetData(record); TransactionId *xids = xlrec->xids; TransactionId max_xid; int i; /* Store the data back into the SLRU files */ RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nxids, xids); /* Make sure nextMXact/nextOffset are beyond what this record has */ MultiXactAdvanceNextMXact(xlrec->mid + 1, xlrec->moff + xlrec->nxids); /* * Make sure nextXid is beyond any XID mentioned in the record. This * should be unnecessary, since any XID found here ought to have other * evidence in the XLOG, but let's be safe. */ max_xid = record->xl_xid; for (i = 0; i < xlrec->nxids; i++) { if (TransactionIdPrecedes(max_xid, xids[i])) max_xid = xids[i]; } if (TransactionIdFollowsOrEquals(max_xid, ShmemVariableCache->nextXid)) { ShmemVariableCache->nextXid = max_xid; TransactionIdAdvance(ShmemVariableCache->nextXid); } } else elog(PANIC, "multixact_redo: unknown op code %u", info);}voidmultixact_desc(StringInfo buf, uint8 xl_info, char *rec){ uint8 info = xl_info & ~XLR_INFO_MASK; if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE) { int pageno; memcpy(&pageno, rec, sizeof(int)); appendStringInfo(buf, "zero offsets page: %d", pageno); } else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE) { int pageno; memcpy(&pageno, rec, sizeof(int)); appendStringInfo(buf, "zero members page: %d", pageno); } else if (info == XLOG_MULTIXACT_CREATE_ID) { xl_multixact_create *xlrec = (xl_multixact_create *) rec; int i; appendStringInfo(buf, "create multixact %u offset %u:", xlrec->mid, xlrec->moff); for (i = 0; i < xlrec->nxids; i++) appendStringInfo(buf, " %u", xlrec->xids[i]); } else appendStringInfo(buf, "UNKNOWN");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -