PostgreSQL Source Code git master
multixact.c File Reference
#include "postgres.h"
#include "access/multixact.h"
#include "access/multixact_internal.h"
#include "access/slru.h"
#include "access/twophase.h"
#include "access/twophase_rmgr.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "pg_trace.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/guc_hooks.h"
#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
Include dependency graph for multixact.c:

Go to the source code of this file.

Data Structures

struct  MultiXactStateData
 
struct  mXactCacheEnt
 
struct  mxtruncinfo
 

Macros

#define MULTIXACT_MEMBER_LOW_THRESHOLD   UINT64CONST(2000000000)
 
#define MULTIXACT_MEMBER_HIGH_THRESHOLD   UINT64CONST(4000000000)
 
#define MultiXactOffsetCtl   (&MultiXactOffsetCtlData)
 
#define MultiXactMemberCtl   (&MultiXactMemberCtlData)
 
#define MaxOldestSlot   (MaxBackends + max_prepared_xacts)
 
#define MAX_CACHE_ENTRIES   256
 
#define debug_elog2(a, b)
 
#define debug_elog3(a, b, c)
 
#define debug_elog4(a, b, c, d)
 
#define debug_elog5(a, b, c, d, e)
 
#define debug_elog6(a, b, c, d, e, f)
 
#define SHARED_MULTIXACT_STATE_SIZE
 

Typedefs

typedef struct MultiXactStateData MultiXactStateData
 
typedef struct mXactCacheEnt mXactCacheEnt
 
typedef struct mxtruncinfo mxtruncinfo
 

Functions

static MultiXactId NextMultiXactId (MultiXactId multi)
 
static MultiXactId PreviousMultiXactId (MultiXactId multi)
 
static void MultiXactIdSetOldestVisible (void)
 
static void RecordNewMultiXact (MultiXactId multi, MultiXactOffset offset, int nmembers, MultiXactMember *members)
 
static MultiXactId GetNewMultiXactId (int nmembers, MultiXactOffset *offset)
 
static int mxactMemberComparator (const void *arg1, const void *arg2)
 
static MultiXactId mXactCacheGetBySet (int nmembers, MultiXactMember *members)
 
static int mXactCacheGetById (MultiXactId multi, MultiXactMember **members)
 
static void mXactCachePut (MultiXactId multi, int nmembers, MultiXactMember *members)
 
static bool MultiXactOffsetPagePrecedes (int64 page1, int64 page2)
 
static bool MultiXactMemberPagePrecedes (int64 page1, int64 page2)
 
static void ExtendMultiXactOffset (MultiXactId multi)
 
static void ExtendMultiXactMember (MultiXactOffset offset, int nmembers)
 
static void SetOldestOffset (void)
 
static bool find_multixact_start (MultiXactId multi, MultiXactOffset *result)
 
static void WriteMTruncateXlogRec (Oid oldestMultiDB, MultiXactId startTruncOff, MultiXactId endTruncOff, MultiXactOffset startTruncMemb, MultiXactOffset endTruncMemb)
 
MultiXactId MultiXactIdCreate (TransactionId xid1, MultiXactStatus status1, TransactionId xid2, MultiXactStatus status2)
 
MultiXactId MultiXactIdExpand (MultiXactId multi, TransactionId xid, MultiXactStatus status)
 
bool MultiXactIdIsRunning (MultiXactId multi, bool isLockOnly)
 
void MultiXactIdSetOldestMember (void)
 
MultiXactId ReadNextMultiXactId (void)
 
void ReadMultiXactIdRange (MultiXactId *oldest, MultiXactId *next)
 
MultiXactId MultiXactIdCreateFromMembers (int nmembers, MultiXactMember *members)
 
int GetMultiXactIdMembers (MultiXactId multi, MultiXactMember **members, bool from_pgupgrade, bool isLockOnly)
 
char * mxstatus_to_string (MultiXactStatus status)
 
char * mxid_to_string (MultiXactId multi, int nmembers, MultiXactMember *members)
 
void AtEOXact_MultiXact (void)
 
void AtPrepare_MultiXact (void)
 
void PostPrepare_MultiXact (FullTransactionId fxid)
 
void multixact_twophase_recover (FullTransactionId fxid, uint16 info, void *recdata, uint32 len)
 
void multixact_twophase_postcommit (FullTransactionId fxid, uint16 info, void *recdata, uint32 len)
 
void multixact_twophase_postabort (FullTransactionId fxid, uint16 info, void *recdata, uint32 len)
 
Size MultiXactShmemSize (void)
 
void MultiXactShmemInit (void)
 
bool check_multixact_offset_buffers (int *newval, void **extra, GucSource source)
 
bool check_multixact_member_buffers (int *newval, void **extra, GucSource source)
 
void BootStrapMultiXact (void)
 
void StartupMultiXact (void)
 
void TrimMultiXact (void)
 
void MultiXactGetCheckptMulti (bool is_shutdown, MultiXactId *nextMulti, MultiXactOffset *nextMultiOffset, MultiXactId *oldestMulti, Oid *oldestMultiDB)
 
void CheckPointMultiXact (void)
 
void MultiXactSetNextMXact (MultiXactId nextMulti, MultiXactOffset nextMultiOffset)
 
void SetMultiXactIdLimit (MultiXactId oldest_datminmxid, Oid oldest_datoid)
 
void MultiXactAdvanceNextMXact (MultiXactId minMulti, MultiXactOffset minMultiOffset)
 
void MultiXactAdvanceOldest (MultiXactId oldestMulti, Oid oldestMultiDB)
 
MultiXactId GetOldestMultiXactId (void)
 
void GetMultiXactInfo (uint32 *multixacts, MultiXactOffset *members, MultiXactId *oldestMultiXactId, MultiXactOffset *oldestOffset)
 
int MultiXactMemberFreezeThreshold (void)
 
static bool SlruScanDirCbFindEarliest (SlruCtl ctl, char *filename, int64 segpage, void *data)
 
static void PerformMembersTruncation (MultiXactOffset oldestOffset, MultiXactOffset newOldestOffset)
 
static void PerformOffsetsTruncation (MultiXactId oldestMulti, MultiXactId newOldestMulti)
 
void TruncateMultiXact (MultiXactId newOldestMulti, Oid newOldestMultiDB)
 
bool MultiXactIdPrecedes (MultiXactId multi1, MultiXactId multi2)
 
bool MultiXactIdPrecedesOrEquals (MultiXactId multi1, MultiXactId multi2)
 
void multixact_redo (XLogReaderState *record)
 
int multixactoffsetssyncfiletag (const FileTag *ftag, char *path)
 
int multixactmemberssyncfiletag (const FileTag *ftag, char *path)
 

Variables

static SlruCtlData MultiXactOffsetCtlData
 
static SlruCtlData MultiXactMemberCtlData
 
static MultiXactStateDataMultiXactState
 
static MultiXactIdOldestMemberMXactId
 
static MultiXactIdOldestVisibleMXactId
 
static dclist_head MXactCache = DCLIST_STATIC_INIT(MXactCache)
 
static MemoryContext MXactContext = NULL
 

Macro Definition Documentation

◆ debug_elog2

#define debug_elog2 (   a,
  b 
)

Definition at line 257 of file multixact.c.

◆ debug_elog3

#define debug_elog3 (   a,
  b,
  c 
)

Definition at line 258 of file multixact.c.

◆ debug_elog4

#define debug_elog4 (   a,
  b,
  c,
 
)

Definition at line 259 of file multixact.c.

◆ debug_elog5

#define debug_elog5 (   a,
  b,
  c,
  d,
  e 
)

Definition at line 260 of file multixact.c.

◆ debug_elog6

#define debug_elog6 (   a,
  b,
  c,
  d,
  e,
 
)

Definition at line 261 of file multixact.c.

◆ MAX_CACHE_ENTRIES

#define MAX_CACHE_ENTRIES   256

Definition at line 246 of file multixact.c.

◆ MaxOldestSlot

#define MaxOldestSlot   (MaxBackends + max_prepared_xacts)

Definition at line 213 of file multixact.c.

◆ MULTIXACT_MEMBER_HIGH_THRESHOLD

#define MULTIXACT_MEMBER_HIGH_THRESHOLD   UINT64CONST(4000000000)

Definition at line 99 of file multixact.c.

◆ MULTIXACT_MEMBER_LOW_THRESHOLD

#define MULTIXACT_MEMBER_LOW_THRESHOLD   UINT64CONST(2000000000)

Definition at line 98 of file multixact.c.

◆ MultiXactMemberCtl

#define MultiXactMemberCtl   (&MultiXactMemberCtlData)

Definition at line 120 of file multixact.c.

◆ MultiXactOffsetCtl

#define MultiXactOffsetCtl   (&MultiXactOffsetCtlData)

Definition at line 119 of file multixact.c.

◆ SHARED_MULTIXACT_STATE_SIZE

#define SHARED_MULTIXACT_STATE_SIZE
Value:
add_size(offsetof(MultiXactStateData, perBackendXactIds), \
TransactionId MultiXactId
Definition: c.h:681
#define MaxOldestSlot
Definition: multixact.c:213
Size add_size(Size s1, Size s2)
Definition: shmem.c:495
Size mul_size(Size s1, Size s2)
Definition: shmem.c:510

Typedef Documentation

◆ MultiXactStateData

◆ mXactCacheEnt

typedef struct mXactCacheEnt mXactCacheEnt

◆ mxtruncinfo

typedef struct mxtruncinfo mxtruncinfo

Function Documentation

◆ AtEOXact_MultiXact()

void AtEOXact_MultiXact ( void  )

Definition at line 1565 of file multixact.c.

1566{
1567 /*
1568 * Reset our OldestMemberMXactId and OldestVisibleMXactId values, both of
1569 * which should only be valid while within a transaction.
1570 *
1571 * We assume that storing a MultiXactId is atomic and so we need not take
1572 * MultiXactGenLock to do this.
1573 */
1576
1577 /*
1578 * Discard the local MultiXactId cache. Since MXactContext was created as
1579 * a child of TopTransactionContext, we needn't delete it explicitly.
1580 */
1581 MXactContext = NULL;
1583}
ProcNumber MyProcNumber
Definition: globals.c:90
static void dclist_init(dclist_head *head)
Definition: ilist.h:671
static MemoryContext MXactContext
Definition: multixact.c:248
static MultiXactId * OldestVisibleMXactId
Definition: multixact.c:218
static dclist_head MXactCache
Definition: multixact.c:247
static MultiXactId * OldestMemberMXactId
Definition: multixact.c:217
#define InvalidMultiXactId
Definition: multixact.h:25

References dclist_init(), InvalidMultiXactId, MXactCache, MXactContext, MyProcNumber, OldestMemberMXactId, and OldestVisibleMXactId.

Referenced by AbortTransaction(), CommitTransaction(), and test_read_multixact().

◆ AtPrepare_MultiXact()

void AtPrepare_MultiXact ( void  )

Definition at line 1593 of file multixact.c.

1594{
1596
1597 if (MultiXactIdIsValid(myOldestMember))
1599 &myOldestMember, sizeof(MultiXactId));
1600}
#define MultiXactIdIsValid(multi)
Definition: multixact.h:29
void RegisterTwoPhaseRecord(TwoPhaseRmgrId rmid, uint16 info, const void *data, uint32 len)
Definition: twophase.c:1271
#define TWOPHASE_RM_MULTIXACT_ID
Definition: twophase_rmgr.h:29

References MultiXactIdIsValid, MyProcNumber, OldestMemberMXactId, RegisterTwoPhaseRecord(), and TWOPHASE_RM_MULTIXACT_ID.

Referenced by PrepareTransaction().

◆ BootStrapMultiXact()

void BootStrapMultiXact ( void  )

Definition at line 1790 of file multixact.c.

1791{
1792 /* Zero the initial pages and flush them to disk */
1795}
#define MultiXactMemberCtl
Definition: multixact.c:120
#define MultiXactOffsetCtl
Definition: multixact.c:119
void SimpleLruZeroAndWritePage(SlruCtl ctl, int64 pageno)
Definition: slru.c:444

References MultiXactMemberCtl, MultiXactOffsetCtl, and SimpleLruZeroAndWritePage().

Referenced by BootStrapXLOG().

◆ check_multixact_member_buffers()

bool check_multixact_member_buffers ( int *  newval,
void **  extra,
GucSource  source 
)

Definition at line 1779 of file multixact.c.

1780{
1781 return check_slru_buffers("multixact_member_buffers", newval);
1782}
#define newval
bool check_slru_buffers(const char *name, int *newval)
Definition: slru.c:355

References check_slru_buffers(), and newval.

◆ check_multixact_offset_buffers()

bool check_multixact_offset_buffers ( int *  newval,
void **  extra,
GucSource  source 
)

Definition at line 1770 of file multixact.c.

1771{
1772 return check_slru_buffers("multixact_offset_buffers", newval);
1773}

References check_slru_buffers(), and newval.

◆ CheckPointMultiXact()

void CheckPointMultiXact ( void  )

Definition at line 1965 of file multixact.c.

1966{
1967 TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(true);
1968
1969 /*
1970 * Write dirty MultiXact pages to disk. This may result in sync requests
1971 * queued for later handling by ProcessSyncRequests(), as part of the
1972 * checkpoint.
1973 */
1976
1977 TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true);
1978}
void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
Definition: slru.c:1347

References MultiXactMemberCtl, MultiXactOffsetCtl, and SimpleLruWriteAll().

Referenced by CheckPointGuts().

◆ ExtendMultiXactMember()

static void ExtendMultiXactMember ( MultiXactOffset  offset,
int  nmembers 
)
static

Definition at line 2239 of file multixact.c.

2240{
2241 /*
2242 * It's possible that the members span more than one page of the members
2243 * file, so we loop to ensure we consider each page. The coding is not
2244 * optimal if the members span several pages, but that seems unusual
2245 * enough to not worry much about.
2246 */
2247 while (nmembers > 0)
2248 {
2249 int flagsoff;
2250 int flagsbit;
2252
2253 /*
2254 * Only zero when at first entry of a page.
2255 */
2256 flagsoff = MXOffsetToFlagsOffset(offset);
2257 flagsbit = MXOffsetToFlagsBitShift(offset);
2258 if (flagsoff == 0 && flagsbit == 0)
2259 {
2260 int64 pageno;
2261 LWLock *lock;
2262
2263 pageno = MXOffsetToMemberPage(offset);
2265
2267
2268 /* Zero the page and make a WAL entry about it */
2270 XLogSimpleInsertInt64(RM_MULTIXACT_ID,
2272
2273 LWLockRelease(lock);
2274 }
2275
2276 /* Compute the number of items till end of current page. */
2278
2279 /*
2280 * Advance to next page. OK if nmembers goes negative.
2281 */
2282 nmembers -= difference;
2283 offset += difference;
2284 }
2285}
int64_t int64
Definition: c.h:549
uint32_t uint32
Definition: c.h:552
Datum difference(PG_FUNCTION_ARGS)
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1174
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1894
@ LW_EXCLUSIVE
Definition: lwlock.h:112
#define XLOG_MULTIXACT_ZERO_MEM_PAGE
Definition: multixact.h:68
static int MXOffsetToFlagsBitShift(MultiXactOffset32 offset)
static int64 MXOffsetToMemberPage(MultiXactOffset32 offset)
#define MULTIXACT_MEMBERS_PER_PAGE
static int MXOffsetToFlagsOffset(MultiXactOffset32 offset)
int SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
Definition: slru.c:375
static LWLock * SimpleLruGetBankLock(SlruCtl ctl, int64 pageno)
Definition: slru.h:160
Definition: lwlock.h:42
XLogRecPtr XLogSimpleInsertInt64(RmgrId rmid, uint8 info, int64 value)
Definition: xloginsert.c:543

References difference(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MULTIXACT_MEMBERS_PER_PAGE, MultiXactMemberCtl, MXOffsetToFlagsBitShift(), MXOffsetToFlagsOffset(), MXOffsetToMemberPage(), SimpleLruGetBankLock(), SimpleLruZeroPage(), XLOG_MULTIXACT_ZERO_MEM_PAGE, and XLogSimpleInsertInt64().

Referenced by GetNewMultiXactId().

◆ ExtendMultiXactOffset()

static void ExtendMultiXactOffset ( MultiXactId  multi)
static

Definition at line 2205 of file multixact.c.

2206{
2207 int64 pageno;
2208 LWLock *lock;
2209
2210 /*
2211 * No work except at first MultiXactId of a page. But beware: just after
2212 * wraparound, the first MultiXactId of page zero is FirstMultiXactId.
2213 */
2214 if (MultiXactIdToOffsetEntry(multi) != 0 &&
2215 multi != FirstMultiXactId)
2216 return;
2217
2218 pageno = MultiXactIdToOffsetPage(multi);
2220
2222
2223 /* Zero the page and make a WAL entry about it */
2226 pageno);
2227
2228 LWLockRelease(lock);
2229}
#define XLOG_MULTIXACT_ZERO_OFF_PAGE
Definition: multixact.h:67
#define FirstMultiXactId
Definition: multixact.h:26
static int MultiXactIdToOffsetEntry(MultiXactId multi)
static int64 MultiXactIdToOffsetPage(MultiXactId multi)

References FirstMultiXactId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdToOffsetEntry(), MultiXactIdToOffsetPage(), MultiXactOffsetCtl, SimpleLruGetBankLock(), SimpleLruZeroPage(), XLOG_MULTIXACT_ZERO_OFF_PAGE, and XLogSimpleInsertInt64().

Referenced by GetNewMultiXactId().

◆ find_multixact_start()

static bool find_multixact_start ( MultiXactId  multi,
MultiXactOffset result 
)
static

Definition at line 2421 of file multixact.c.

2422{
2423 MultiXactOffset offset;
2424 int64 pageno;
2425 int entryno;
2426 int slotno;
2427 MultiXactOffset *offptr;
2428
2430
2431 pageno = MultiXactIdToOffsetPage(multi);
2432 entryno = MultiXactIdToOffsetEntry(multi);
2433
2434 /*
2435 * Write out dirty data, so PhysicalPageExists can work correctly.
2436 */
2439
2441 return false;
2442
2443 /* lock is acquired by SimpleLruReadPage_ReadOnly */
2444 slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, multi);
2445 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
2446 offptr += entryno;
2447 offset = *offptr;
2449
2450 *result = offset;
2451 return true;
2452}
uint64 MultiXactOffset
Definition: c.h:683
Assert(PointerIsAligned(start, uint64))
static MultiXactStateData * MultiXactState
Definition: multixact.c:216
int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
Definition: slru.c:630
bool SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int64 pageno)
Definition: slru.c:771

References Assert(), MultiXactStateData::finishedStartup, LWLockRelease(), MultiXactIdToOffsetEntry(), MultiXactIdToOffsetPage(), MultiXactMemberCtl, MultiXactOffsetCtl, MultiXactState, SimpleLruDoesPhysicalPageExist(), SimpleLruGetBankLock(), SimpleLruReadPage_ReadOnly(), and SimpleLruWriteAll().

Referenced by SetOldestOffset(), and TruncateMultiXact().

◆ GetMultiXactIdMembers()

int GetMultiXactIdMembers ( MultiXactId  multi,
MultiXactMember **  members,
bool  from_pgupgrade,
bool  isLockOnly 
)

Definition at line 1115 of file multixact.c.

1117{
1118 int64 pageno;
1119 int64 prev_pageno;
1120 int entryno;
1121 int slotno;
1122 MultiXactOffset *offptr;
1123 MultiXactOffset offset;
1124 MultiXactOffset nextMXOffset;
1125 int length;
1126 MultiXactId oldestMXact;
1127 MultiXactId nextMXact;
1128 MultiXactMember *ptr;
1129 LWLock *lock;
1130
1131 debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
1132
1133 if (!MultiXactIdIsValid(multi) || from_pgupgrade)
1134 {
1135 *members = NULL;
1136 return -1;
1137 }
1138
1139 /* See if the MultiXactId is in the local cache */
1140 length = mXactCacheGetById(multi, members);
1141 if (length >= 0)
1142 {
1143 debug_elog3(DEBUG2, "GetMembers: found %s in the cache",
1144 mxid_to_string(multi, length, *members));
1145 return length;
1146 }
1147
1148 /* Set our OldestVisibleMXactId[] entry if we didn't already */
1150
1151 /*
1152 * If we know the multi is used only for locking and not for updates, then
1153 * we can skip checking if the value is older than our oldest visible
1154 * multi. It cannot possibly still be running.
1155 */
1156 if (isLockOnly &&
1158 {
1159 debug_elog2(DEBUG2, "GetMembers: a locker-only multi is too old");
1160 *members = NULL;
1161 return -1;
1162 }
1163
1164 /*
1165 * We check known limits on MultiXact before resorting to the SLRU area.
1166 *
1167 * An ID older than MultiXactState->oldestMultiXactId cannot possibly be
1168 * useful; it has already been removed, or will be removed shortly, by
1169 * truncation. If one is passed, an error is raised.
1170 *
1171 * Also, an ID >= nextMXact shouldn't ever be seen here; if it is seen, it
1172 * implies undetected ID wraparound has occurred. This raises a hard
1173 * error.
1174 *
1175 * Shared lock is enough here since we aren't modifying any global state.
1176 * Acquire it just long enough to grab the current counter values.
1177 */
1178 LWLockAcquire(MultiXactGenLock, LW_SHARED);
1179
1180 oldestMXact = MultiXactState->oldestMultiXactId;
1181 nextMXact = MultiXactState->nextMXact;
1182
1183 LWLockRelease(MultiXactGenLock);
1184
1185 if (MultiXactIdPrecedes(multi, oldestMXact))
1186 ereport(ERROR,
1187 (errcode(ERRCODE_INTERNAL_ERROR),
1188 errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
1189 multi)));
1190
1191 if (!MultiXactIdPrecedes(multi, nextMXact))
1192 ereport(ERROR,
1193 (errcode(ERRCODE_INTERNAL_ERROR),
1194 errmsg("MultiXactId %u has not been created yet -- apparent wraparound",
1195 multi)));
1196
1197 /*
1198 * Find out the offset at which we need to start reading MultiXactMembers
1199 * and the number of members in the multixact. We determine the latter as
1200 * the difference between this multixact's starting offset and the next
1201 * one's.
1202 */
1203 pageno = MultiXactIdToOffsetPage(multi);
1204 entryno = MultiXactIdToOffsetEntry(multi);
1205
1206 /* Acquire the bank lock for the page we need. */
1209
1210 /* read this multi's offset */
1211 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
1212 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
1213 offptr += entryno;
1214 offset = *offptr;
1215
1216 if (offset == 0)
1217 ereport(ERROR,
1219 errmsg("MultiXact %u has invalid offset", multi)));
1220
1221 /* read next multi's offset */
1222 {
1223 MultiXactId tmpMXact;
1224
1225 /* handle wraparound if needed */
1226 tmpMXact = NextMultiXactId(multi);
1227
1228 prev_pageno = pageno;
1229
1230 pageno = MultiXactIdToOffsetPage(tmpMXact);
1231 entryno = MultiXactIdToOffsetEntry(tmpMXact);
1232
1233 if (pageno != prev_pageno)
1234 {
1235 LWLock *newlock;
1236
1237 /*
1238 * Since we're going to access a different SLRU page, if this page
1239 * falls under a different bank, release the old bank's lock and
1240 * acquire the lock of the new bank.
1241 */
1242 newlock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno);
1243 if (newlock != lock)
1244 {
1245 LWLockRelease(lock);
1246 LWLockAcquire(newlock, LW_EXCLUSIVE);
1247 lock = newlock;
1248 }
1249 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);
1250 }
1251
1252 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
1253 offptr += entryno;
1254 nextMXOffset = *offptr;
1255 }
1256
1257 LWLockRelease(lock);
1258 lock = NULL;
1259
1260 /* Sanity check the next offset */
1261 if (nextMXOffset == 0)
1262 ereport(ERROR,
1264 errmsg("MultiXact %u has invalid next offset", multi)));
1265 if (nextMXOffset < offset)
1266 ereport(ERROR,
1268 errmsg("MultiXact %u has offset (%" PRIu64 ") greater than its next offset (%" PRIu64 ")",
1269 multi, offset, nextMXOffset)));
1270 if (nextMXOffset - offset > INT32_MAX)
1271 ereport(ERROR,
1273 errmsg("MultiXact %u has too many members (%" PRIu64 ")",
1274 multi, nextMXOffset - offset)));
1275 length = nextMXOffset - offset;
1276
1277 /* read the members */
1278 ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember));
1279 prev_pageno = -1;
1280 for (int i = 0; i < length; i++, offset++)
1281 {
1282 TransactionId *xactptr;
1283 uint32 *flagsptr;
1284 int flagsoff;
1285 int bshift;
1286 int memberoff;
1287
1288 pageno = MXOffsetToMemberPage(offset);
1289 memberoff = MXOffsetToMemberOffset(offset);
1290
1291 if (pageno != prev_pageno)
1292 {
1293 LWLock *newlock;
1294
1295 /*
1296 * Since we're going to access a different SLRU page, if this page
1297 * falls under a different bank, release the old bank's lock and
1298 * acquire the lock of the new bank.
1299 */
1300 newlock = SimpleLruGetBankLock(MultiXactMemberCtl, pageno);
1301 if (newlock != lock)
1302 {
1303 if (lock)
1304 LWLockRelease(lock);
1305 LWLockAcquire(newlock, LW_EXCLUSIVE);
1306 lock = newlock;
1307 }
1308
1309 slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
1310 prev_pageno = pageno;
1311 }
1312
1313 xactptr = (TransactionId *)
1314 (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
1315 Assert(TransactionIdIsValid(*xactptr));
1316
1317 flagsoff = MXOffsetToFlagsOffset(offset);
1318 bshift = MXOffsetToFlagsBitShift(offset);
1319 flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
1320
1321 ptr[i].xid = *xactptr;
1322 ptr[i].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK;
1323 }
1324
1325 LWLockRelease(lock);
1326
1327 /*
1328 * Copy the result into the local cache.
1329 */
1330 mXactCachePut(multi, length, ptr);
1331
1332 debug_elog3(DEBUG2, "GetMembers: no cache for %s",
1333 mxid_to_string(multi, length, ptr));
1334 *members = ptr;
1335 return length;
1336}
uint32 TransactionId
Definition: c.h:671
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define DEBUG2
Definition: elog.h:29
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
@ LW_SHARED
Definition: lwlock.h:113
void * palloc(Size size)
Definition: mcxt.c:1365
static int mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
Definition: multixact.c:1419
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:2828
static void MultiXactIdSetOldestVisible(void)
Definition: multixact.c:589
static void mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
Definition: multixact.c:1466
static MultiXactId NextMultiXactId(MultiXactId multi)
Definition: multixact.c:102
#define debug_elog3(a, b, c)
Definition: multixact.c:258
char * mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
Definition: multixact.c:1534
#define debug_elog2(a, b)
Definition: multixact.c:257
#define MXACT_MEMBER_XACT_BITMASK
static int MXOffsetToMemberOffset(MultiXactOffset32 offset)
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:42
int SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok, TransactionId xid)
Definition: slru.c:527
TransactionId xid
Definition: multixact.h:57
MultiXactStatus status
Definition: multixact.h:58
MultiXactId nextMXact
Definition: multixact.c:132
MultiXactId oldestMultiXactId
Definition: multixact.c:145
#define TransactionIdIsValid(xid)
Definition: transam.h:41

References Assert(), DEBUG2, debug_elog2, debug_elog3, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg(), ERROR, i, if(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdSetOldestVisible(), MultiXactIdToOffsetEntry(), MultiXactIdToOffsetPage(), MultiXactMemberCtl, MultiXactOffsetCtl, MultiXactState, MXACT_MEMBER_XACT_BITMASK, mXactCacheGetById(), mXactCachePut(), mxid_to_string(), MXOffsetToFlagsBitShift(), MXOffsetToFlagsOffset(), MXOffsetToMemberOffset(), MXOffsetToMemberPage(), MyProcNumber, NextMultiXactId(), MultiXactStateData::nextMXact, MultiXactStateData::oldestMultiXactId, OldestVisibleMXactId, palloc(), SimpleLruGetBankLock(), SimpleLruReadPage(), MultiXactMember::status, TransactionIdIsValid, and MultiXactMember::xid.

Referenced by Do_MultiXactIdWait(), DoesMultiXactIdConflict(), FreezeMultiXactId(), GetMultiXactIdHintBits(), heap_lock_tuple(), heap_lock_updated_tuple_rec(), heap_tuple_should_freeze(), MultiXactIdExpand(), MultiXactIdGetUpdateXid(), MultiXactIdIsRunning(), pg_get_multixact_members(), pgrowlocks(), and test_read_multixact().

◆ GetMultiXactInfo()

void GetMultiXactInfo ( uint32 multixacts,
MultiXactOffset members,
MultiXactId oldestMultiXactId,
MultiXactOffset oldestOffset 
)

Definition at line 2464 of file multixact.c.

2466{
2467 MultiXactOffset nextOffset;
2468 MultiXactId nextMultiXactId;
2469
2470 LWLockAcquire(MultiXactGenLock, LW_SHARED);
2471 nextOffset = MultiXactState->nextOffset;
2472 *oldestMultiXactId = MultiXactState->oldestMultiXactId;
2473 nextMultiXactId = MultiXactState->nextMXact;
2474 *oldestOffset = MultiXactState->oldestOffset;
2475 LWLockRelease(MultiXactGenLock);
2476
2477 *members = nextOffset - *oldestOffset;
2478 *multixacts = nextMultiXactId - *oldestMultiXactId;
2479}
MultiXactOffset nextOffset
Definition: multixact.c:135
MultiXactOffset oldestOffset
Definition: multixact.c:152

References LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactState, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactId, and MultiXactStateData::oldestOffset.

Referenced by MultiXactMemberFreezeThreshold().

◆ GetNewMultiXactId()

static MultiXactId GetNewMultiXactId ( int  nmembers,
MultiXactOffset offset 
)
static

Definition at line 926 of file multixact.c.

927{
928 MultiXactId result;
929 MultiXactOffset nextOffset;
930
931 debug_elog3(DEBUG2, "GetNew: for %d xids", nmembers);
932
933 /* safety check, we should never get this far in a HS standby */
934 if (RecoveryInProgress())
935 elog(ERROR, "cannot assign MultiXactIds during recovery");
936
937 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
938
939 /* Assign the MXID */
940 result = MultiXactState->nextMXact;
941
942 /*----------
943 * Check to see if it's safe to assign another MultiXactId. This protects
944 * against catastrophic data loss due to multixact wraparound. The basic
945 * rules are:
946 *
947 * If we're past multiVacLimit or the safe threshold for member storage
948 * space, or we don't know what the safe threshold for member storage is,
949 * start trying to force autovacuum cycles.
950 * If we're past multiWarnLimit, start issuing warnings.
951 * If we're past multiStopLimit, refuse to create new MultiXactIds.
952 *
953 * Note these are pretty much the same protections in GetNewTransactionId.
954 *----------
955 */
957 {
958 /*
959 * For safety's sake, we release MultiXactGenLock while sending
960 * signals, warnings, etc. This is not so much because we care about
961 * preserving concurrency in this situation, as to avoid any
962 * possibility of deadlock while doing get_database_name(). First,
963 * copy all the shared values we'll need in this path.
964 */
965 MultiXactId multiWarnLimit = MultiXactState->multiWarnLimit;
966 MultiXactId multiStopLimit = MultiXactState->multiStopLimit;
967 MultiXactId multiWrapLimit = MultiXactState->multiWrapLimit;
968 Oid oldest_datoid = MultiXactState->oldestMultiXactDB;
969
970 LWLockRelease(MultiXactGenLock);
971
972 if (IsUnderPostmaster &&
973 !MultiXactIdPrecedes(result, multiStopLimit))
974 {
975 char *oldest_datname = get_database_name(oldest_datoid);
976
977 /*
978 * Immediately kick autovacuum into action as we're already in
979 * ERROR territory.
980 */
982
983 /* complain even if that DB has disappeared */
984 if (oldest_datname)
986 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
987 errmsg("database is not accepting commands that assign new MultiXactIds to avoid wraparound data loss in database \"%s\"",
988 oldest_datname),
989 errhint("Execute a database-wide VACUUM in that database.\n"
990 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
991 else
993 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
994 errmsg("database is not accepting commands that assign new MultiXactIds to avoid wraparound data loss in database with OID %u",
995 oldest_datoid),
996 errhint("Execute a database-wide VACUUM in that database.\n"
997 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
998 }
999
1000 /*
1001 * To avoid swamping the postmaster with signals, we issue the autovac
1002 * request only once per 64K multis generated. This still gives
1003 * plenty of chances before we get into real trouble.
1004 */
1005 if (IsUnderPostmaster && ((result % 65536) == 0 || result == FirstMultiXactId))
1007
1008 if (!MultiXactIdPrecedes(result, multiWarnLimit))
1009 {
1010 char *oldest_datname = get_database_name(oldest_datoid);
1011
1012 /* complain even if that DB has disappeared */
1013 if (oldest_datname)
1015 (errmsg_plural("database \"%s\" must be vacuumed before %u more MultiXactId is used",
1016 "database \"%s\" must be vacuumed before %u more MultiXactIds are used",
1017 multiWrapLimit - result,
1018 oldest_datname,
1019 multiWrapLimit - result),
1020 errhint("Execute a database-wide VACUUM in that database.\n"
1021 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1022 else
1024 (errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used",
1025 "database with OID %u must be vacuumed before %u more MultiXactIds are used",
1026 multiWrapLimit - result,
1027 oldest_datoid,
1028 multiWrapLimit - result),
1029 errhint("Execute a database-wide VACUUM in that database.\n"
1030 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1031 }
1032
1033 /* Re-acquire lock and start over */
1034 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
1035 result = MultiXactState->nextMXact;
1036 }
1037
1038 /*
1039 * Make sure there is room for the next MXID in the file. Assigning this
1040 * MXID sets the next MXID's offset already.
1041 */
1043
1044 /*
1045 * Reserve the members space, similarly to above.
1046 */
1047 nextOffset = MultiXactState->nextOffset;
1048
1049 /*
1050 * Offsets are 64-bit integers and will never wrap around. Firstly, it
1051 * would take an unrealistic amount of time and resources to consume 2^64
1052 * offsets. Secondly, multixid creation is WAL-logged, so you would run
1053 * out of LSNs before reaching offset wraparound. Nevertheless, check for
1054 * wraparound as a sanity check.
1055 */
1056 if (nextOffset + nmembers < nextOffset)
1057 ereport(ERROR,
1058 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1059 errmsg("MultiXact members would wrap around")));
1060 *offset = nextOffset;
1061
1062 ExtendMultiXactMember(nextOffset, nmembers);
1063
1064 /*
1065 * Critical section from here until caller has written the data into the
1066 * just-reserved SLRU space; we don't want to error out with a partly
1067 * written MultiXact structure. (In particular, failing to write our
1068 * start offset after advancing nextMXact would effectively corrupt the
1069 * previous MultiXact.)
1070 */
1072
1073 /*
1074 * Advance counters. As in GetNewTransactionId(), this must not happen
1075 * until after file extension has succeeded!
1076 */
1078 MultiXactState->nextOffset += nmembers;
1079
1080 LWLockRelease(MultiXactGenLock);
1081
1082 debug_elog4(DEBUG2, "GetNew: returning %u offset %" PRIu64,
1083 result, *offset);
1084 return result;
1085}
int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: elog.c:1193
int errhint(const char *fmt,...)
Definition: elog.c:1330
#define WARNING
Definition: elog.h:36
#define elog(elevel,...)
Definition: elog.h:226
bool IsUnderPostmaster
Definition: globals.c:120
char * get_database_name(Oid dbid)
Definition: lsyscache.c:1257
#define START_CRIT_SECTION()
Definition: miscadmin.h:150
static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
Definition: multixact.c:2239
static void ExtendMultiXactOffset(MultiXactId multi)
Definition: multixact.c:2205
#define debug_elog4(a, b, c, d)
Definition: multixact.c:259
void SendPostmasterSignal(PMSignalReason reason)
Definition: pmsignal.c:165
@ PMSIGNAL_START_AUTOVAC_LAUNCHER
Definition: pmsignal.h:39
unsigned int Oid
Definition: postgres_ext.h:32
MultiXactId multiWrapLimit
Definition: multixact.c:158
MultiXactId multiStopLimit
Definition: multixact.c:157
MultiXactId multiWarnLimit
Definition: multixact.c:156
MultiXactId multiVacLimit
Definition: multixact.c:155
bool RecoveryInProgress(void)
Definition: xlog.c:6404

References DEBUG2, debug_elog3, debug_elog4, elog, ereport, errcode(), errhint(), errmsg(), errmsg_plural(), ERROR, ExtendMultiXactMember(), ExtendMultiXactOffset(), FirstMultiXactId, get_database_name(), IsUnderPostmaster, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactStateData::multiStopLimit, MultiXactStateData::multiVacLimit, MultiXactStateData::multiWarnLimit, MultiXactStateData::multiWrapLimit, MultiXactIdPrecedes(), MultiXactState, NextMultiXactId(), MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactDB, PMSIGNAL_START_AUTOVAC_LAUNCHER, RecoveryInProgress(), SendPostmasterSignal(), START_CRIT_SECTION, and WARNING.

Referenced by MultiXactIdCreateFromMembers().

◆ GetOldestMultiXactId()

MultiXactId GetOldestMultiXactId ( void  )

Definition at line 2300 of file multixact.c.

2301{
2302 MultiXactId oldestMXact;
2303 int i;
2304
2305 /*
2306 * This is the oldest valid value among all the OldestMemberMXactId[] and
2307 * OldestVisibleMXactId[] entries, or nextMXact if none are valid.
2308 */
2309 LWLockAcquire(MultiXactGenLock, LW_SHARED);
2310 oldestMXact = MultiXactState->nextMXact;
2311 for (i = 0; i < MaxOldestSlot; i++)
2312 {
2313 MultiXactId thisoldest;
2314
2315 thisoldest = OldestMemberMXactId[i];
2316 if (MultiXactIdIsValid(thisoldest) &&
2317 MultiXactIdPrecedes(thisoldest, oldestMXact))
2318 oldestMXact = thisoldest;
2319 thisoldest = OldestVisibleMXactId[i];
2320 if (MultiXactIdIsValid(thisoldest) &&
2321 MultiXactIdPrecedes(thisoldest, oldestMXact))
2322 oldestMXact = thisoldest;
2323 }
2324
2325 LWLockRelease(MultiXactGenLock);
2326
2327 return oldestMXact;
2328}

References i, LW_SHARED, LWLockAcquire(), LWLockRelease(), MaxOldestSlot, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactState, MultiXactStateData::nextMXact, OldestMemberMXactId, and OldestVisibleMXactId.

Referenced by heapam_relation_set_new_filelocator(), vac_update_datfrozenxid(), and vacuum_get_cutoffs().

◆ multixact_redo()

void multixact_redo ( XLogReaderState record)

Definition at line 2882 of file multixact.c.

2883{
2884 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
2885
2886 /* Backup blocks are not used in multixact records */
2888
2889 if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
2890 {
2891 int64 pageno;
2892
2893 memcpy(&pageno, XLogRecGetData(record), sizeof(pageno));
2895 }
2896 else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
2897 {
2898 int64 pageno;
2899
2900 memcpy(&pageno, XLogRecGetData(record), sizeof(pageno));
2902 }
2903 else if (info == XLOG_MULTIXACT_CREATE_ID)
2904 {
2905 xl_multixact_create *xlrec =
2907 TransactionId max_xid;
2908 int i;
2909
2910 /* Store the data back into the SLRU files */
2911 RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers,
2912 xlrec->members);
2913
2914 /* Make sure nextMXact/nextOffset are beyond what this record has */
2916 xlrec->moff + xlrec->nmembers);
2917
2918 /*
2919 * Make sure nextXid is beyond any XID mentioned in the record. This
2920 * should be unnecessary, since any XID found here ought to have other
2921 * evidence in the XLOG, but let's be safe.
2922 */
2923 max_xid = XLogRecGetXid(record);
2924 for (i = 0; i < xlrec->nmembers; i++)
2925 {
2926 if (TransactionIdPrecedes(max_xid, xlrec->members[i].xid))
2927 max_xid = xlrec->members[i].xid;
2928 }
2929
2931 }
2932 else if (info == XLOG_MULTIXACT_TRUNCATE_ID)
2933 {
2935 int64 pageno;
2936
2937 memcpy(&xlrec, XLogRecGetData(record),
2939
2940 elog(DEBUG1, "replaying multixact truncation: "
2941 "offsets [%u, %u), offsets segments [%" PRIx64 ", %" PRIx64 "), "
2942 "members [%" PRIu64 ", %" PRIu64 "), members segments [%" PRIx64 ", %" PRIx64 ")",
2943 xlrec.startTruncOff, xlrec.endTruncOff,
2946 xlrec.startTruncMemb, xlrec.endTruncMemb,
2949
2950 /* should not be required, but more than cheap enough */
2951 LWLockAcquire(MultiXactTruncationLock, LW_EXCLUSIVE);
2952
2953 /*
2954 * Advance the horizon values, so they're current at the end of
2955 * recovery.
2956 */
2958
2960
2961 /*
2962 * During XLOG replay, latest_page_number isn't necessarily set up
2963 * yet; insert a suitable value to bypass the sanity test in
2964 * SimpleLruTruncate.
2965 */
2966 pageno = MultiXactIdToOffsetPage(xlrec.endTruncOff);
2967 pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number,
2968 pageno);
2970
2971 LWLockRelease(MultiXactTruncationLock);
2972 }
2973 else
2974 elog(PANIC, "multixact_redo: unknown op code %u", info);
2975}
static void pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition: atomics.h:485
uint8_t uint8
Definition: c.h:550
#define PANIC
Definition: elog.h:42
#define DEBUG1
Definition: elog.h:30
static void PerformOffsetsTruncation(MultiXactId oldestMulti, MultiXactId newOldestMulti)
Definition: multixact.c:2594
static void PerformMembersTruncation(MultiXactOffset oldestOffset, MultiXactOffset newOldestOffset)
Definition: multixact.c:2584
static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset, int nmembers, MultiXactMember *members)
Definition: multixact.c:759
void MultiXactAdvanceNextMXact(MultiXactId minMulti, MultiXactOffset minMultiOffset)
Definition: multixact.c:2161
void SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
Definition: multixact.c:2011
#define XLOG_MULTIXACT_TRUNCATE_ID
Definition: multixact.h:70
#define SizeOfMultiXactTruncate
Definition: multixact.h:95
#define XLOG_MULTIXACT_CREATE_ID
Definition: multixact.h:69
static int64 MultiXactIdToOffsetSegment(MultiXactId multi)
static int64 MXOffsetToMemberSegment(MultiXactOffset offset)
MultiXactId mid
Definition: multixact.h:74
MultiXactMember members[FLEXIBLE_ARRAY_MEMBER]
Definition: multixact.h:77
MultiXactOffset moff
Definition: multixact.h:75
MultiXactId endTruncOff
Definition: multixact.h:88
MultiXactOffset startTruncMemb
Definition: multixact.h:91
MultiXactOffset endTruncMemb
Definition: multixact.h:92
MultiXactId startTruncOff
Definition: multixact.h:87
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.h:263
void AdvanceNextFullTransactionIdPastXid(TransactionId xid)
Definition: varsup.c:304
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:409
#define XLogRecGetData(decoder)
Definition: xlogreader.h:414
#define XLogRecGetXid(decoder)
Definition: xlogreader.h:411
#define XLogRecHasAnyBlockRefs(decoder)
Definition: xlogreader.h:416

References AdvanceNextFullTransactionIdPastXid(), Assert(), DEBUG1, elog, xl_multixact_truncate::endTruncMemb, xl_multixact_truncate::endTruncOff, i, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), xl_multixact_create::members, xl_multixact_create::mid, xl_multixact_create::moff, MultiXactAdvanceNextMXact(), MultiXactIdToOffsetPage(), MultiXactIdToOffsetSegment(), MultiXactMemberCtl, MultiXactOffsetCtl, MXOffsetToMemberSegment(), NextMultiXactId(), xl_multixact_create::nmembers, xl_multixact_truncate::oldestMultiDB, PANIC, PerformMembersTruncation(), PerformOffsetsTruncation(), pg_atomic_write_u64(), RecordNewMultiXact(), SetMultiXactIdLimit(), SimpleLruZeroAndWritePage(), SizeOfMultiXactTruncate, xl_multixact_truncate::startTruncMemb, xl_multixact_truncate::startTruncOff, TransactionIdPrecedes(), MultiXactMember::xid, XLOG_MULTIXACT_CREATE_ID, XLOG_MULTIXACT_TRUNCATE_ID, XLOG_MULTIXACT_ZERO_MEM_PAGE, XLOG_MULTIXACT_ZERO_OFF_PAGE, XLogRecGetData, XLogRecGetInfo, XLogRecGetXid, and XLogRecHasAnyBlockRefs.

◆ multixact_twophase_postabort()

void multixact_twophase_postabort ( FullTransactionId  fxid,
uint16  info,
void *  recdata,
uint32  len 
)

Definition at line 1692 of file multixact.c.

1694{
1695 multixact_twophase_postcommit(fxid, info, recdata, len);
1696}
void multixact_twophase_postcommit(FullTransactionId fxid, uint16 info, void *recdata, uint32 len)
Definition: multixact.c:1677
const void size_t len

References len, and multixact_twophase_postcommit().

◆ multixact_twophase_postcommit()

void multixact_twophase_postcommit ( FullTransactionId  fxid,
uint16  info,
void *  recdata,
uint32  len 
)

Definition at line 1677 of file multixact.c.

1679{
1680 ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, true);
1681
1682 Assert(len == sizeof(MultiXactId));
1683
1684 OldestMemberMXactId[dummyProcNumber] = InvalidMultiXactId;
1685}
int ProcNumber
Definition: procnumber.h:24
ProcNumber TwoPhaseGetDummyProcNumber(FullTransactionId fxid, bool lock_held)
Definition: twophase.c:908

References Assert(), InvalidMultiXactId, len, OldestMemberMXactId, and TwoPhaseGetDummyProcNumber().

Referenced by multixact_twophase_postabort().

◆ multixact_twophase_recover()

void multixact_twophase_recover ( FullTransactionId  fxid,
uint16  info,
void *  recdata,
uint32  len 
)

Definition at line 1656 of file multixact.c.

1658{
1659 ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, false);
1660 MultiXactId oldestMember;
1661
1662 /*
1663 * Get the oldest member XID from the state file record, and set it in the
1664 * OldestMemberMXactId slot reserved for this prepared transaction.
1665 */
1666 Assert(len == sizeof(MultiXactId));
1667 oldestMember = *((MultiXactId *) recdata);
1668
1669 OldestMemberMXactId[dummyProcNumber] = oldestMember;
1670}

References Assert(), len, OldestMemberMXactId, and TwoPhaseGetDummyProcNumber().

◆ MultiXactAdvanceNextMXact()

void MultiXactAdvanceNextMXact ( MultiXactId  minMulti,
MultiXactOffset  minMultiOffset 
)

Definition at line 2161 of file multixact.c.

2163{
2164 Assert(MultiXactIdIsValid(minMulti));
2165
2166 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
2168 {
2169 debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti);
2170 MultiXactState->nextMXact = minMulti;
2171 }
2172 if (MultiXactState->nextOffset < minMultiOffset)
2173 {
2174 debug_elog3(DEBUG2, "MultiXact: setting next offset to %" PRIu64,
2175 minMultiOffset);
2176 MultiXactState->nextOffset = minMultiOffset;
2177 }
2178 LWLockRelease(MultiXactGenLock);
2179}

References Assert(), DEBUG2, debug_elog3, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactState, MultiXactStateData::nextMXact, and MultiXactStateData::nextOffset.

Referenced by multixact_redo(), and xlog_redo().

◆ MultiXactAdvanceOldest()

void MultiXactAdvanceOldest ( MultiXactId  oldestMulti,
Oid  oldestMultiDB 
)

Definition at line 2188 of file multixact.c.

2189{
2191
2193 SetMultiXactIdLimit(oldestMulti, oldestMultiDB);
2194}
bool InRecovery
Definition: xlogutils.c:50

References Assert(), InRecovery, MultiXactIdPrecedes(), MultiXactState, MultiXactStateData::oldestMultiXactId, and SetMultiXactIdLimit().

Referenced by xlog_redo().

◆ MultiXactGetCheckptMulti()

void MultiXactGetCheckptMulti ( bool  is_shutdown,
MultiXactId nextMulti,
MultiXactOffset nextMultiOffset,
MultiXactId oldestMulti,
Oid oldestMultiDB 
)

Definition at line 1943 of file multixact.c.

1948{
1949 LWLockAcquire(MultiXactGenLock, LW_SHARED);
1950 *nextMulti = MultiXactState->nextMXact;
1951 *nextMultiOffset = MultiXactState->nextOffset;
1952 *oldestMulti = MultiXactState->oldestMultiXactId;
1953 *oldestMultiDB = MultiXactState->oldestMultiXactDB;
1954 LWLockRelease(MultiXactGenLock);
1955
1957 "MultiXact: checkpoint is nextMulti %u, nextOffset %" PRIu64 ", oldestMulti %u in DB %u",
1958 *nextMulti, *nextMultiOffset, *oldestMulti, *oldestMultiDB);
1959}
#define debug_elog6(a, b, c, d, e, f)
Definition: multixact.c:261

References DEBUG2, debug_elog6, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactState, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactDB, and MultiXactStateData::oldestMultiXactId.

Referenced by CreateCheckPoint().

◆ MultiXactIdCreate()

MultiXactId MultiXactIdCreate ( TransactionId  xid1,
MultiXactStatus  status1,
TransactionId  xid2,
MultiXactStatus  status2 
)

Definition at line 301 of file multixact.c.

303{
304 MultiXactId newMulti;
305 MultiXactMember members[2];
306
309
310 Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));
311
312 /* MultiXactIdSetOldestMember() must have been called already. */
314
315 /*
316 * Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
317 * are still running. In typical usage, xid2 will be our own XID and the
318 * caller just did a check on xid1, so it'd be wasted effort.
319 */
320
321 members[0].xid = xid1;
322 members[0].status = status1;
323 members[1].xid = xid2;
324 members[1].status = status2;
325
326 newMulti = MultiXactIdCreateFromMembers(2, members);
327
328 debug_elog3(DEBUG2, "Create: %s",
329 mxid_to_string(newMulti, 2, members));
330
331 return newMulti;
332}
MultiXactId MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
Definition: multixact.c:658
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43

References Assert(), DEBUG2, debug_elog3, MultiXactIdCreateFromMembers(), MultiXactIdIsValid, mxid_to_string(), MyProcNumber, OldestMemberMXactId, MultiXactMember::status, TransactionIdEquals, TransactionIdIsValid, and MultiXactMember::xid.

Referenced by compute_new_xmax_infomask(), and test_create_multixact().

◆ MultiXactIdCreateFromMembers()

MultiXactId MultiXactIdCreateFromMembers ( int  nmembers,
MultiXactMember members 
)

Definition at line 658 of file multixact.c.

659{
660 MultiXactId multi;
661 MultiXactOffset offset;
663
664 debug_elog3(DEBUG2, "Create: %s",
665 mxid_to_string(InvalidMultiXactId, nmembers, members));
666
667 /*
668 * See if the same set of members already exists in our cache; if so, just
669 * re-use that MultiXactId. (Note: it might seem that looking in our
670 * cache is insufficient, and we ought to search disk to see if a
671 * duplicate definition already exists. But since we only ever create
672 * MultiXacts containing our own XID, in most cases any such MultiXacts
673 * were in fact created by us, and so will be in our cache. There are
674 * corner cases where someone else added us to a MultiXact without our
675 * knowledge, but it's not worth checking for.)
676 */
677 multi = mXactCacheGetBySet(nmembers, members);
678 if (MultiXactIdIsValid(multi))
679 {
680 debug_elog2(DEBUG2, "Create: in cache!");
681 return multi;
682 }
683
684 /* Verify that there is a single update Xid among the given members. */
685 {
686 int i;
687 bool has_update = false;
688
689 for (i = 0; i < nmembers; i++)
690 {
691 if (ISUPDATE_from_mxstatus(members[i].status))
692 {
693 if (has_update)
694 elog(ERROR, "new multixact has more than one updating member: %s",
695 mxid_to_string(InvalidMultiXactId, nmembers, members));
696 has_update = true;
697 }
698 }
699 }
700
701 /* Load the injection point before entering the critical section */
702 INJECTION_POINT_LOAD("multixact-create-from-members");
703
704 /*
705 * Assign the MXID and offsets range to use, and make sure there is space
706 * in the OFFSETs and MEMBERs files. NB: this routine does
707 * START_CRIT_SECTION().
708 *
709 * Note: unlike MultiXactIdCreate and MultiXactIdExpand, we do not check
710 * that we've called MultiXactIdSetOldestMember here. This is because
711 * this routine is used in some places to create new MultiXactIds of which
712 * the current backend is not a member, notably during freezing of multis
713 * in vacuum. During vacuum, in particular, it would be unacceptable to
714 * keep OldestMulti set, in case it runs for long.
715 */
716 multi = GetNewMultiXactId(nmembers, &offset);
717
718 INJECTION_POINT_CACHED("multixact-create-from-members", NULL);
719
720 /* Make an XLOG entry describing the new MXID. */
721 xlrec.mid = multi;
722 xlrec.moff = offset;
723 xlrec.nmembers = nmembers;
724
725 /*
726 * XXX Note: there's a lot of padding space in MultiXactMember. We could
727 * find a more compact representation of this Xlog record -- perhaps all
728 * the status flags in one XLogRecData, then all the xids in another one?
729 * Not clear that it's worth the trouble though.
730 */
733 XLogRegisterData(members, nmembers * sizeof(MultiXactMember));
734
735 (void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID);
736
737 /* Now enter the information into the OFFSETs and MEMBERs logs */
738 RecordNewMultiXact(multi, offset, nmembers, members);
739
740 /* Done with critical section */
742
743 /* Store the new MultiXactId in the local cache, too */
744 mXactCachePut(multi, nmembers, members);
745
746 debug_elog2(DEBUG2, "Create: all done");
747
748 return multi;
749}
#define INJECTION_POINT_CACHED(name, arg)
#define INJECTION_POINT_LOAD(name)
#define END_CRIT_SECTION()
Definition: miscadmin.h:152
static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
Definition: multixact.c:926
static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members)
Definition: multixact.c:1376
#define ISUPDATE_from_mxstatus(status)
Definition: multixact.h:51
#define SizeOfMultiXactCreate
Definition: multixact.h:80
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:478
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:368
void XLogBeginInsert(void)
Definition: xloginsert.c:152

References DEBUG2, debug_elog2, debug_elog3, elog, END_CRIT_SECTION, ERROR, GetNewMultiXactId(), i, INJECTION_POINT_CACHED, INJECTION_POINT_LOAD, InvalidMultiXactId, ISUPDATE_from_mxstatus, xl_multixact_create::mid, xl_multixact_create::moff, MultiXactIdIsValid, mXactCacheGetBySet(), mXactCachePut(), mxid_to_string(), xl_multixact_create::nmembers, RecordNewMultiXact(), SizeOfMultiXactCreate, XLOG_MULTIXACT_CREATE_ID, XLogBeginInsert(), XLogInsert(), and XLogRegisterData().

Referenced by FreezeMultiXactId(), MultiXactIdCreate(), and MultiXactIdExpand().

◆ MultiXactIdExpand()

MultiXactId MultiXactIdExpand ( MultiXactId  multi,
TransactionId  xid,
MultiXactStatus  status 
)

Definition at line 354 of file multixact.c.

355{
356 MultiXactId newMulti;
357 MultiXactMember *members;
358 MultiXactMember *newMembers;
359 int nmembers;
360 int i;
361 int j;
362
365
366 /* MultiXactIdSetOldestMember() must have been called already. */
368
369 debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
370 multi, xid, mxstatus_to_string(status));
371
372 /*
373 * Note: we don't allow for old multis here. The reason is that the only
374 * caller of this function does a check that the multixact is no longer
375 * running.
376 */
377 nmembers = GetMultiXactIdMembers(multi, &members, false, false);
378
379 if (nmembers < 0)
380 {
381 MultiXactMember member;
382
383 /*
384 * The MultiXactId is obsolete. This can only happen if all the
385 * MultiXactId members stop running between the caller checking and
386 * passing it to us. It would be better to return that fact to the
387 * caller, but it would complicate the API and it's unlikely to happen
388 * too often, so just deal with it by creating a singleton MultiXact.
389 */
390 member.xid = xid;
391 member.status = status;
392 newMulti = MultiXactIdCreateFromMembers(1, &member);
393
394 debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u",
395 multi, newMulti);
396 return newMulti;
397 }
398
399 /*
400 * If the TransactionId is already a member of the MultiXactId with the
401 * same status, just return the existing MultiXactId.
402 */
403 for (i = 0; i < nmembers; i++)
404 {
405 if (TransactionIdEquals(members[i].xid, xid) &&
406 (members[i].status == status))
407 {
408 debug_elog4(DEBUG2, "Expand: %u is already a member of %u",
409 xid, multi);
410 pfree(members);
411 return multi;
412 }
413 }
414
415 /*
416 * Determine which of the members of the MultiXactId are still of
417 * interest. This is any running transaction, and also any transaction
418 * that grabbed something stronger than just a lock and was committed. (An
419 * update that aborted is of no interest here; and having more than one
420 * update Xid in a multixact would cause errors elsewhere.)
421 *
422 * Removing dead members is not just an optimization: freezing of tuples
423 * whose Xmax are multis depends on this behavior.
424 *
425 * Note we have the same race condition here as above: j could be 0 at the
426 * end of the loop.
427 */
428 newMembers = palloc_array(MultiXactMember, nmembers + 1);
429
430 for (i = 0, j = 0; i < nmembers; i++)
431 {
432 if (TransactionIdIsInProgress(members[i].xid) ||
433 (ISUPDATE_from_mxstatus(members[i].status) &&
434 TransactionIdDidCommit(members[i].xid)))
435 {
436 newMembers[j].xid = members[i].xid;
437 newMembers[j++].status = members[i].status;
438 }
439 }
440
441 newMembers[j].xid = xid;
442 newMembers[j++].status = status;
443 newMulti = MultiXactIdCreateFromMembers(j, newMembers);
444
445 pfree(members);
446 pfree(newMembers);
447
448 debug_elog3(DEBUG2, "Expand: returning new multi %u", newMulti);
449
450 return newMulti;
451}
#define palloc_array(type, count)
Definition: fe_memutils.h:76
int j
Definition: isn.c:78
void pfree(void *pointer)
Definition: mcxt.c:1594
char * mxstatus_to_string(MultiXactStatus status)
Definition: multixact.c:1511
#define debug_elog5(a, b, c, d, e)
Definition: multixact.c:260
int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, bool from_pgupgrade, bool isLockOnly)
Definition: multixact.c:1115
bool TransactionIdIsInProgress(TransactionId xid)
Definition: procarray.c:1402
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:126

References Assert(), DEBUG2, debug_elog3, debug_elog4, debug_elog5, GetMultiXactIdMembers(), i, ISUPDATE_from_mxstatus, j, MultiXactIdCreateFromMembers(), MultiXactIdIsValid, mxstatus_to_string(), MyProcNumber, OldestMemberMXactId, palloc_array, pfree(), MultiXactMember::status, TransactionIdDidCommit(), TransactionIdEquals, TransactionIdIsInProgress(), TransactionIdIsValid, and MultiXactMember::xid.

Referenced by compute_new_xmax_infomask().

◆ MultiXactIdIsRunning()

bool MultiXactIdIsRunning ( MultiXactId  multi,
bool  isLockOnly 
)

Definition at line 465 of file multixact.c.

466{
467 MultiXactMember *members;
468 int nmembers;
469 int i;
470
471 debug_elog3(DEBUG2, "IsRunning %u?", multi);
472
473 /*
474 * "false" here means we assume our callers have checked that the given
475 * multi cannot possibly come from a pg_upgraded database.
476 */
477 nmembers = GetMultiXactIdMembers(multi, &members, false, isLockOnly);
478
479 if (nmembers <= 0)
480 {
481 debug_elog2(DEBUG2, "IsRunning: no members");
482 return false;
483 }
484
485 /*
486 * Checking for myself is cheap compared to looking in shared memory;
487 * return true if any live subtransaction of the current top-level
488 * transaction is a member.
489 *
490 * This is not needed for correctness, it's just a fast path.
491 */
492 for (i = 0; i < nmembers; i++)
493 {
494 if (TransactionIdIsCurrentTransactionId(members[i].xid))
495 {
496 debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
497 pfree(members);
498 return true;
499 }
500 }
501
502 /*
503 * This could be made faster by having another entry point in procarray.c,
504 * walking the PGPROC array only once for all the members. But in most
505 * cases nmembers should be small enough that it doesn't much matter.
506 */
507 for (i = 0; i < nmembers; i++)
508 {
509 if (TransactionIdIsInProgress(members[i].xid))
510 {
511 debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running",
512 i, members[i].xid);
513 pfree(members);
514 return true;
515 }
516 }
517
518 pfree(members);
519
520 debug_elog3(DEBUG2, "IsRunning: %u is not running", multi);
521
522 return false;
523}
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:942

References DEBUG2, debug_elog2, debug_elog3, debug_elog4, GetMultiXactIdMembers(), i, pfree(), TransactionIdIsCurrentTransactionId(), and TransactionIdIsInProgress().

Referenced by compute_new_xmax_infomask(), FreezeMultiXactId(), HeapTupleSatisfiesUpdate(), and HeapTupleSatisfiesVacuumHorizon().

◆ MultiXactIdPrecedes()

◆ MultiXactIdPrecedesOrEquals()

bool MultiXactIdPrecedesOrEquals ( MultiXactId  multi1,
MultiXactId  multi2 
)

Definition at line 2842 of file multixact.c.

2843{
2844 int32 diff = (int32) (multi1 - multi2);
2845
2846 return (diff <= 0);
2847}

Referenced by check_mxid_in_range(), heap_tuple_should_freeze(), heap_vacuum_rel(), TruncateMultiXact(), and vacuum_get_cutoffs().

◆ MultiXactIdSetOldestMember()

void MultiXactIdSetOldestMember ( void  )

Definition at line 539 of file multixact.c.

540{
542 {
543 MultiXactId nextMXact;
544
545 /*
546 * You might think we don't need to acquire a lock here, since
547 * fetching and storing of TransactionIds is probably atomic, but in
548 * fact we do: suppose we pick up nextMXact and then lose the CPU for
549 * a long time. Someone else could advance nextMXact, and then
550 * another someone else could compute an OldestVisibleMXactId that
551 * would be after the value we are going to store when we get control
552 * back. Which would be wrong.
553 *
554 * Note that a shared lock is sufficient, because it's enough to stop
555 * someone from advancing nextMXact; and nobody else could be trying
556 * to write to our OldestMember entry, only reading (and we assume
557 * storing it is atomic.)
558 */
559 LWLockAcquire(MultiXactGenLock, LW_SHARED);
560
561 nextMXact = MultiXactState->nextMXact;
562
564
565 LWLockRelease(MultiXactGenLock);
566
567 debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",
568 MyProcNumber, nextMXact);
569 }
570}

References DEBUG2, debug_elog4, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MultiXactState, MyProcNumber, MultiXactStateData::nextMXact, and OldestMemberMXactId.

Referenced by heap_delete(), heap_lock_tuple(), heap_lock_updated_tuple(), heap_update(), and test_create_multixact().

◆ MultiXactIdSetOldestVisible()

static void MultiXactIdSetOldestVisible ( void  )
static

Definition at line 589 of file multixact.c.

590{
592 {
593 MultiXactId oldestMXact;
594 int i;
595
596 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
597
598 oldestMXact = MultiXactState->nextMXact;
599 for (i = 0; i < MaxOldestSlot; i++)
600 {
601 MultiXactId thisoldest = OldestMemberMXactId[i];
602
603 if (MultiXactIdIsValid(thisoldest) &&
604 MultiXactIdPrecedes(thisoldest, oldestMXact))
605 oldestMXact = thisoldest;
606 }
607
608 OldestVisibleMXactId[MyProcNumber] = oldestMXact;
609
610 LWLockRelease(MultiXactGenLock);
611
612 debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u",
613 MyProcNumber, oldestMXact);
614 }
615}

References DEBUG2, debug_elog4, i, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MaxOldestSlot, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactState, MyProcNumber, MultiXactStateData::nextMXact, OldestMemberMXactId, and OldestVisibleMXactId.

Referenced by GetMultiXactIdMembers().

◆ MultiXactMemberFreezeThreshold()

int MultiXactMemberFreezeThreshold ( void  )

Definition at line 2510 of file multixact.c.

2511{
2512 MultiXactOffset members;
2513 uint32 multixacts;
2514 uint32 victim_multixacts;
2515 double fraction;
2516 int result;
2517 MultiXactId oldestMultiXactId;
2518 MultiXactOffset oldestOffset;
2519
2520 /* Read the current offsets and members usage. */
2521 GetMultiXactInfo(&multixacts, &members, &oldestMultiXactId, &oldestOffset);
2522
2523 /* If member space utilization is low, no special action is required. */
2524 if (members <= MULTIXACT_MEMBER_LOW_THRESHOLD)
2526
2527 /*
2528 * Compute a target for relminmxid advancement. The number of multixacts
2529 * we try to eliminate from the system is based on how far we are past
2530 * MULTIXACT_MEMBER_LOW_THRESHOLD.
2531 *
2532 * The way this formula works is that when members is exactly at the low
2533 * threshold, fraction = 0.0, and we set freeze_max_age equal to
2534 * mxid_age(oldestMultiXactId). As members grows further, towards the
2535 * high threshold, fraction grows linearly from 0.0 to 1.0, and the result
2536 * shrinks from mxid_age(oldestMultiXactId) to 0. Beyond the high
2537 * threshold, fraction > 1.0 and the result is clamped to 0.
2538 */
2539 fraction = (double) (members - MULTIXACT_MEMBER_LOW_THRESHOLD) /
2541
2542 /* fraction could be > 1.0, but lowest possible freeze age is zero */
2543 if (fraction >= 1.0)
2544 return 0;
2545
2546 victim_multixacts = multixacts * fraction;
2547 result = multixacts - victim_multixacts;
2548
2549 /*
2550 * Clamp to autovacuum_multixact_freeze_max_age, so that we never make
2551 * autovacuum less aggressive than it would otherwise be.
2552 */
2554}
int autovacuum_multixact_freeze_max_age
Definition: autovacuum.c:130
#define Min(x, y)
Definition: c.h:1016
void GetMultiXactInfo(uint32 *multixacts, MultiXactOffset *members, MultiXactId *oldestMultiXactId, MultiXactOffset *oldestOffset)
Definition: multixact.c:2464
#define MULTIXACT_MEMBER_LOW_THRESHOLD
Definition: multixact.c:98
#define MULTIXACT_MEMBER_HIGH_THRESHOLD
Definition: multixact.c:99

References autovacuum_multixact_freeze_max_age, GetMultiXactInfo(), Min, MULTIXACT_MEMBER_HIGH_THRESHOLD, and MULTIXACT_MEMBER_LOW_THRESHOLD.

Referenced by do_autovacuum(), do_start_worker(), and vacuum_get_cutoffs().

◆ MultiXactMemberPagePrecedes()

static bool MultiXactMemberPagePrecedes ( int64  page1,
int64  page2 
)
static

Definition at line 2816 of file multixact.c.

2817{
2818 return page1 < page2;
2819}

Referenced by MultiXactShmemInit().

◆ multixactmemberssyncfiletag()

int multixactmemberssyncfiletag ( const FileTag ftag,
char *  path 
)

Definition at line 2990 of file multixact.c.

2991{
2992 return SlruSyncFileTag(MultiXactMemberCtl, ftag, path);
2993}
int SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path)
Definition: slru.c:1856

References MultiXactMemberCtl, and SlruSyncFileTag().

◆ MultiXactOffsetPagePrecedes()

static bool MultiXactOffsetPagePrecedes ( int64  page1,
int64  page2 
)
static

Definition at line 2795 of file multixact.c.

2796{
2797 MultiXactId multi1;
2798 MultiXactId multi2;
2799
2800 multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
2801 multi1 += FirstMultiXactId + 1;
2802 multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
2803 multi2 += FirstMultiXactId + 1;
2804
2805 return (MultiXactIdPrecedes(multi1, multi2) &&
2806 MultiXactIdPrecedes(multi1,
2807 multi2 + MULTIXACT_OFFSETS_PER_PAGE - 1));
2808}
#define MULTIXACT_OFFSETS_PER_PAGE

References FirstMultiXactId, MULTIXACT_OFFSETS_PER_PAGE, and MultiXactIdPrecedes().

Referenced by MultiXactShmemInit().

◆ multixactoffsetssyncfiletag()

int multixactoffsetssyncfiletag ( const FileTag ftag,
char *  path 
)

Definition at line 2981 of file multixact.c.

2982{
2983 return SlruSyncFileTag(MultiXactOffsetCtl, ftag, path);
2984}

References MultiXactOffsetCtl, and SlruSyncFileTag().

◆ MultiXactSetNextMXact()

void MultiXactSetNextMXact ( MultiXactId  nextMulti,
MultiXactOffset  nextMultiOffset 
)

Definition at line 1989 of file multixact.c.

1991{
1992 Assert(MultiXactIdIsValid(nextMulti));
1993 debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %" PRIu64,
1994 nextMulti, nextMultiOffset);
1995
1996 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
1997 MultiXactState->nextMXact = nextMulti;
1998 MultiXactState->nextOffset = nextMultiOffset;
1999 LWLockRelease(MultiXactGenLock);
2000}

References Assert(), DEBUG2, debug_elog4, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MultiXactState, MultiXactStateData::nextMXact, and MultiXactStateData::nextOffset.

Referenced by BootStrapXLOG(), StartupXLOG(), and xlog_redo().

◆ MultiXactShmemInit()

void MultiXactShmemInit ( void  )

Definition at line 1721 of file multixact.c.

1722{
1723 bool found;
1724
1725 debug_elog2(DEBUG2, "Shared Memory Init for MultiXact");
1726
1729
1731 "multixact_offset", multixact_offset_buffers, 0,
1732 "pg_multixact/offsets", LWTRANCHE_MULTIXACTOFFSET_BUFFER,
1733 LWTRANCHE_MULTIXACTOFFSET_SLRU,
1735 false);
1738 "multixact_member", multixact_member_buffers, 0,
1739 "pg_multixact/members", LWTRANCHE_MULTIXACTMEMBER_BUFFER,
1740 LWTRANCHE_MULTIXACTMEMBER_SLRU,
1742 true);
1743 /* doesn't call SimpleLruTruncate() or meet criteria for unit tests */
1744
1745 /* Initialize our shared state struct */
1746 MultiXactState = ShmemInitStruct("Shared MultiXact State",
1748 &found);
1749 if (!IsUnderPostmaster)
1750 {
1751 Assert(!found);
1752
1753 /* Make sure we zero out the per-backend state */
1755 }
1756 else
1757 Assert(found);
1758
1759 /*
1760 * Set up array pointers.
1761 */
1764}
#define MemSet(start, val, len)
Definition: c.h:1032
int multixact_offset_buffers
Definition: globals.c:163
int multixact_member_buffers
Definition: globals.c:162
static bool MultiXactMemberPagePrecedes(int64 page1, int64 page2)
Definition: multixact.c:2816
#define SHARED_MULTIXACT_STATE_SIZE
static bool MultiXactOffsetPagePrecedes(int64 page1, int64 page2)
Definition: multixact.c:2795
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:389
void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, const char *subdir, int buffer_tranche_id, int bank_tranche_id, SyncRequestHandler sync_handler, bool long_segment_names)
Definition: slru.c:252
#define SlruPagePrecedesUnitTests(ctl, per_page)
Definition: slru.h:185
MultiXactId perBackendXactIds[FLEXIBLE_ARRAY_MEMBER]
Definition: multixact.c:207
@ SYNC_HANDLER_MULTIXACT_MEMBER
Definition: sync.h:41
@ SYNC_HANDLER_MULTIXACT_OFFSET
Definition: sync.h:40

References Assert(), DEBUG2, debug_elog2, IsUnderPostmaster, MaxOldestSlot, MemSet, multixact_member_buffers, multixact_offset_buffers, MULTIXACT_OFFSETS_PER_PAGE, MultiXactMemberCtl, MultiXactMemberPagePrecedes(), MultiXactOffsetCtl, MultiXactOffsetPagePrecedes(), MultiXactState, OldestMemberMXactId, OldestVisibleMXactId, MultiXactStateData::perBackendXactIds, SHARED_MULTIXACT_STATE_SIZE, ShmemInitStruct(), SimpleLruInit(), SlruPagePrecedesUnitTests, SYNC_HANDLER_MULTIXACT_MEMBER, and SYNC_HANDLER_MULTIXACT_OFFSET.

Referenced by CreateOrAttachShmemStructs().

◆ MultiXactShmemSize()

Size MultiXactShmemSize ( void  )

Definition at line 1704 of file multixact.c.

1705{
1706 Size size;
1707
1708 /* We need 2*MaxOldestSlot perBackendXactIds[] entries */
1709#define SHARED_MULTIXACT_STATE_SIZE \
1710 add_size(offsetof(MultiXactStateData, perBackendXactIds), \
1711 mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))
1712
1716
1717 return size;
1718}
size_t Size
Definition: c.h:624
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition: slru.c:198

References add_size(), multixact_member_buffers, multixact_offset_buffers, SHARED_MULTIXACT_STATE_SIZE, and SimpleLruShmemSize().

Referenced by CalculateShmemSize().

◆ mXactCacheGetById()

static int mXactCacheGetById ( MultiXactId  multi,
MultiXactMember **  members 
)
static

Definition at line 1419 of file multixact.c.

1420{
1421 dlist_iter iter;
1422
1423 debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);
1424
1426 {
1428 iter.cur);
1429
1430 if (entry->multi == multi)
1431 {
1432 MultiXactMember *ptr;
1433 Size size;
1434
1435 size = sizeof(MultiXactMember) * entry->nmembers;
1436 ptr = (MultiXactMember *) palloc(size);
1437
1438 memcpy(ptr, entry->members, size);
1439
1440 debug_elog3(DEBUG2, "CacheGet: found %s",
1441 mxid_to_string(multi,
1442 entry->nmembers,
1443 entry->members));
1444
1445 /*
1446 * Note we modify the list while not using a modifiable iterator.
1447 * This is acceptable only because we exit the iteration
1448 * immediately afterwards.
1449 */
1451
1452 *members = ptr;
1453 return entry->nmembers;
1454 }
1455 }
1456
1457 debug_elog2(DEBUG2, "CacheGet: not found");
1458 return -1;
1459}
#define dclist_container(type, membername, ptr)
Definition: ilist.h:947
static void dclist_move_head(dclist_head *head, dlist_node *node)
Definition: ilist.h:808
#define dclist_foreach(iter, lhead)
Definition: ilist.h:970
struct MultiXactMember MultiXactMember
dlist_node * cur
Definition: ilist.h:179
MultiXactId multi
Definition: multixact.c:240
MultiXactMember members[FLEXIBLE_ARRAY_MEMBER]
Definition: multixact.c:243

References dlist_iter::cur, dclist_container, dclist_foreach, dclist_move_head(), DEBUG2, debug_elog2, debug_elog3, mXactCacheEnt::members, mXactCacheEnt::multi, MXactCache, mxid_to_string(), mXactCacheEnt::nmembers, and palloc().

Referenced by GetMultiXactIdMembers().

◆ mXactCacheGetBySet()

static MultiXactId mXactCacheGetBySet ( int  nmembers,
MultiXactMember members 
)
static

Definition at line 1376 of file multixact.c.

1377{
1378 dlist_iter iter;
1379
1380 debug_elog3(DEBUG2, "CacheGet: looking for %s",
1381 mxid_to_string(InvalidMultiXactId, nmembers, members));
1382
1383 /* sort the array so comparison is easy */
1384 qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
1385
1387 {
1389 iter.cur);
1390
1391 if (entry->nmembers != nmembers)
1392 continue;
1393
1394 /*
1395 * We assume the cache entries are sorted, and that the unused bits in
1396 * "status" are zeroed.
1397 */
1398 if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
1399 {
1400 debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
1402 return entry->multi;
1403 }
1404 }
1405
1406 debug_elog2(DEBUG2, "CacheGet: not found :-(");
1407 return InvalidMultiXactId;
1408}
static int mxactMemberComparator(const void *arg1, const void *arg2)
Definition: multixact.c:1346
#define qsort(a, b, c, d)
Definition: port.h:499

References dlist_iter::cur, dclist_container, dclist_foreach, dclist_move_head(), DEBUG2, debug_elog2, debug_elog3, InvalidMultiXactId, mXactCacheEnt::members, mXactCacheEnt::multi, MXactCache, mxactMemberComparator(), mxid_to_string(), mXactCacheEnt::nmembers, and qsort.

Referenced by MultiXactIdCreateFromMembers().

◆ mXactCachePut()

static void mXactCachePut ( MultiXactId  multi,
int  nmembers,
MultiXactMember members 
)
static

Definition at line 1466 of file multixact.c.

1467{
1468 mXactCacheEnt *entry;
1469
1470 debug_elog3(DEBUG2, "CachePut: storing %s",
1471 mxid_to_string(multi, nmembers, members));
1472
1473 if (MXactContext == NULL)
1474 {
1475 /* The cache only lives as long as the current transaction */
1476 debug_elog2(DEBUG2, "CachePut: initializing memory context");
1478 "MultiXact cache context",
1480 }
1481
1482 entry = (mXactCacheEnt *)
1484 offsetof(mXactCacheEnt, members) +
1485 nmembers * sizeof(MultiXactMember));
1486
1487 entry->multi = multi;
1488 entry->nmembers = nmembers;
1489 memcpy(entry->members, members, nmembers * sizeof(MultiXactMember));
1490
1491 /* mXactCacheGetBySet assumes the entries are sorted, so sort them */
1492 qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
1493
1494 dclist_push_head(&MXactCache, &entry->node);
1496 {
1497 dlist_node *node;
1498
1501
1502 entry = dclist_container(mXactCacheEnt, node, node);
1503 debug_elog3(DEBUG2, "CachePut: pruning cached multi %u",
1504 entry->multi);
1505
1506 pfree(entry);
1507 }
1508}
static uint32 dclist_count(const dclist_head *head)
Definition: ilist.h:932
static dlist_node * dclist_tail_node(dclist_head *head)
Definition: ilist.h:920
static void dclist_delete_from(dclist_head *head, dlist_node *node)
Definition: ilist.h:763
static void dclist_push_head(dclist_head *head, dlist_node *node)
Definition: ilist.h:693
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1229
MemoryContext TopTransactionContext
Definition: mcxt.c:171
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
#define MAX_CACHE_ENTRIES
Definition: multixact.c:246
dlist_node node
Definition: multixact.c:242

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, dclist_container, dclist_count(), dclist_delete_from(), dclist_push_head(), dclist_tail_node(), DEBUG2, debug_elog2, debug_elog3, MAX_CACHE_ENTRIES, mXactCacheEnt::members, MemoryContextAlloc(), mXactCacheEnt::multi, MXactCache, MXactContext, mxactMemberComparator(), mxid_to_string(), mXactCacheEnt::nmembers, mXactCacheEnt::node, pfree(), qsort, and TopTransactionContext.

Referenced by GetMultiXactIdMembers(), and MultiXactIdCreateFromMembers().

◆ mxactMemberComparator()

static int mxactMemberComparator ( const void *  arg1,
const void *  arg2 
)
static

Definition at line 1346 of file multixact.c.

1347{
1348 MultiXactMember member1 = *(const MultiXactMember *) arg1;
1349 MultiXactMember member2 = *(const MultiXactMember *) arg2;
1350
1351 if (member1.xid > member2.xid)
1352 return 1;
1353 if (member1.xid < member2.xid)
1354 return -1;
1355 if (member1.status > member2.status)
1356 return 1;
1357 if (member1.status < member2.status)
1358 return -1;
1359 return 0;
1360}

References MultiXactMember::status, and MultiXactMember::xid.

Referenced by mXactCacheGetBySet(), and mXactCachePut().

◆ mxid_to_string()

char * mxid_to_string ( MultiXactId  multi,
int  nmembers,
MultiXactMember members 
)

Definition at line 1534 of file multixact.c.

1535{
1536 static char *str = NULL;
1538 int i;
1539
1540 if (str != NULL)
1541 pfree(str);
1542
1544
1545 appendStringInfo(&buf, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
1546 mxstatus_to_string(members[0].status));
1547
1548 for (i = 1; i < nmembers; i++)
1549 appendStringInfo(&buf, ", %u (%s)", members[i].xid,
1550 mxstatus_to_string(members[i].status));
1551
1554 pfree(buf.data);
1555 return str;
1556}
const char * str
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1746
MemoryContext TopMemoryContext
Definition: mcxt.c:166
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97

References appendStringInfo(), appendStringInfoChar(), buf, i, initStringInfo(), MemoryContextStrdup(), mxstatus_to_string(), pfree(), str, and TopMemoryContext.

Referenced by GetMultiXactIdMembers(), MultiXactIdCreate(), MultiXactIdCreateFromMembers(), mXactCacheGetById(), mXactCacheGetBySet(), and mXactCachePut().

◆ mxstatus_to_string()

char * mxstatus_to_string ( MultiXactStatus  status)

Definition at line 1511 of file multixact.c.

1512{
1513 switch (status)
1514 {
1516 return "keysh";
1518 return "sh";
1520 return "fornokeyupd";
1522 return "forupd";
1524 return "nokeyupd";
1526 return "upd";
1527 default:
1528 elog(ERROR, "unrecognized multixact status %d", status);
1529 return "";
1530 }
1531}
@ MultiXactStatusForShare
Definition: multixact.h:39
@ MultiXactStatusForNoKeyUpdate
Definition: multixact.h:40
@ MultiXactStatusNoKeyUpdate
Definition: multixact.h:43
@ MultiXactStatusUpdate
Definition: multixact.h:45
@ MultiXactStatusForUpdate
Definition: multixact.h:41
@ MultiXactStatusForKeyShare
Definition: multixact.h:38

References elog, ERROR, MultiXactStatusForKeyShare, MultiXactStatusForNoKeyUpdate, MultiXactStatusForShare, MultiXactStatusForUpdate, MultiXactStatusNoKeyUpdate, and MultiXactStatusUpdate.

Referenced by MultiXactIdExpand(), mxid_to_string(), and pg_get_multixact_members().

◆ NextMultiXactId()

static MultiXactId NextMultiXactId ( MultiXactId  multi)
inlinestatic

Definition at line 102 of file multixact.c.

103{
104 return multi == MaxMultiXactId ? FirstMultiXactId : multi + 1;
105}
#define MaxMultiXactId
Definition: multixact.h:27

References FirstMultiXactId, and MaxMultiXactId.

Referenced by GetMultiXactIdMembers(), GetNewMultiXactId(), multixact_redo(), and RecordNewMultiXact().

◆ PerformMembersTruncation()

static void PerformMembersTruncation ( MultiXactOffset  oldestOffset,
MultiXactOffset  newOldestOffset 
)
static

Definition at line 2584 of file multixact.c.

2585{
2587 MXOffsetToMemberPage(newOldestOffset));
2588}
void SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
Definition: slru.c:1433

References MultiXactMemberCtl, MXOffsetToMemberPage(), and SimpleLruTruncate().

Referenced by multixact_redo(), and TruncateMultiXact().

◆ PerformOffsetsTruncation()

static void PerformOffsetsTruncation ( MultiXactId  oldestMulti,
MultiXactId  newOldestMulti 
)
static

Definition at line 2594 of file multixact.c.

2595{
2596 /*
2597 * We step back one multixact to avoid passing a cutoff page that hasn't
2598 * been created yet in the rare case that oldestMulti would be the first
2599 * item on a page and oldestMulti == nextMulti. In that case, if we
2600 * didn't subtract one, we'd trigger SimpleLruTruncate's wraparound
2601 * detection.
2602 */
2605}
static MultiXactId PreviousMultiXactId(MultiXactId multi)
Definition: multixact.c:108

References MultiXactIdToOffsetPage(), MultiXactOffsetCtl, PreviousMultiXactId(), and SimpleLruTruncate().

Referenced by multixact_redo(), and TruncateMultiXact().

◆ PostPrepare_MultiXact()

void PostPrepare_MultiXact ( FullTransactionId  fxid)

Definition at line 1607 of file multixact.c.

1608{
1609 MultiXactId myOldestMember;
1610
1611 /*
1612 * Transfer our OldestMemberMXactId value to the slot reserved for the
1613 * prepared transaction.
1614 */
1615 myOldestMember = OldestMemberMXactId[MyProcNumber];
1616 if (MultiXactIdIsValid(myOldestMember))
1617 {
1618 ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, false);
1619
1620 /*
1621 * Even though storing MultiXactId is atomic, acquire lock to make
1622 * sure others see both changes, not just the reset of the slot of the
1623 * current backend. Using a volatile pointer might suffice, but this
1624 * isn't a hot spot.
1625 */
1626 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
1627
1628 OldestMemberMXactId[dummyProcNumber] = myOldestMember;
1630
1631 LWLockRelease(MultiXactGenLock);
1632 }
1633
1634 /*
1635 * We don't need to transfer OldestVisibleMXactId value, because the
1636 * transaction is not going to be looking at any more multixacts once it's
1637 * prepared.
1638 *
1639 * We assume that storing a MultiXactId is atomic and so we need not take
1640 * MultiXactGenLock to do this.
1641 */
1643
1644 /*
1645 * Discard the local MultiXactId cache like in AtEOXact_MultiXact.
1646 */
1647 MXactContext = NULL;
1649}

References dclist_init(), InvalidMultiXactId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdIsValid, MXactCache, MXactContext, MyProcNumber, OldestMemberMXactId, OldestVisibleMXactId, and TwoPhaseGetDummyProcNumber().

Referenced by PrepareTransaction().

◆ PreviousMultiXactId()

static MultiXactId PreviousMultiXactId ( MultiXactId  multi)
inlinestatic

Definition at line 108 of file multixact.c.

109{
110 return multi == FirstMultiXactId ? MaxMultiXactId : multi - 1;
111}

References FirstMultiXactId, and MaxMultiXactId.

Referenced by PerformOffsetsTruncation().

◆ ReadMultiXactIdRange()

void ReadMultiXactIdRange ( MultiXactId oldest,
MultiXactId next 
)

Definition at line 639 of file multixact.c.

640{
641 LWLockAcquire(MultiXactGenLock, LW_SHARED);
644 LWLockRelease(MultiXactGenLock);
645}
static int32 next
Definition: blutils.c:224

References LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactState, next, MultiXactStateData::nextMXact, and MultiXactStateData::oldestMultiXactId.

Referenced by update_cached_mxid_range().

◆ ReadNextMultiXactId()

MultiXactId ReadNextMultiXactId ( void  )

Definition at line 622 of file multixact.c.

623{
624 MultiXactId mxid;
625
626 /* XXX we could presumably do this without a lock. */
627 LWLockAcquire(MultiXactGenLock, LW_SHARED);
629 LWLockRelease(MultiXactGenLock);
630
631 return mxid;
632}

References LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactState, and MultiXactStateData::nextMXact.

Referenced by ATRewriteTables(), AutoVacWorkerMain(), do_start_worker(), mxid_age(), refresh_by_heap_swap(), vac_update_datfrozenxid(), vac_update_relstats(), vacuum_get_cutoffs(), and vacuum_xid_failsafe_check().

◆ RecordNewMultiXact()

static void RecordNewMultiXact ( MultiXactId  multi,
MultiXactOffset  offset,
int  nmembers,
MultiXactMember members 
)
static

Definition at line 759 of file multixact.c.

761{
762 int64 pageno;
763 int64 prev_pageno;
764 int entryno;
765 int slotno;
766 MultiXactOffset *offptr;
768 int64 next_pageno;
769 int next_entryno;
770 MultiXactOffset *next_offptr;
771 MultiXactOffset next_offset;
772 LWLock *lock;
773 LWLock *prevlock = NULL;
774
775 /* position of this multixid in the offsets SLRU area */
776 pageno = MultiXactIdToOffsetPage(multi);
777 entryno = MultiXactIdToOffsetEntry(multi);
778
779 /* position of the next multixid */
780 next = NextMultiXactId(multi);
781 next_pageno = MultiXactIdToOffsetPage(next);
782 next_entryno = MultiXactIdToOffsetEntry(next);
783
784 /*
785 * Set the starting offset of this multixid's members.
786 *
787 * In the common case, it was already be set by the previous
788 * RecordNewMultiXact call, as this was the next multixid of the previous
789 * multixid. But if multiple backends are generating multixids
790 * concurrently, we might race ahead and get called before the previous
791 * multixid.
792 */
795
796 /*
797 * Note: we pass the MultiXactId to SimpleLruReadPage as the "transaction"
798 * to complain about if there's any I/O error. This is kinda bogus, but
799 * since the errors will always give the full pathname, it should be clear
800 * enough that a MultiXactId is really involved. Perhaps someday we'll
801 * take the trouble to generalize the slru.c error reporting code.
802 */
803 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
804 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
805 offptr += entryno;
806
807 if (*offptr != offset)
808 {
809 /* should already be set to the correct value, or not at all */
810 Assert(*offptr == 0);
811 *offptr = offset;
812 MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
813 }
814
815 /*
816 * Set the next multixid's offset to the end of this multixid's members.
817 */
818 if (next_pageno == pageno)
819 {
820 next_offptr = offptr + 1;
821 }
822 else
823 {
824 /* must be the first entry on the page */
825 Assert(next_entryno == 0 || next == FirstMultiXactId);
826
827 /* Swap the lock for a lock on the next page */
828 LWLockRelease(lock);
829 lock = SimpleLruGetBankLock(MultiXactOffsetCtl, next_pageno);
831
832 slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, next);
833 next_offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
834 next_offptr += next_entryno;
835 }
836
837 /* Like in GetNewMultiXactId(), skip over offset 0 */
838 next_offset = offset + nmembers;
839 if (next_offset == 0)
840 next_offset = 1;
841 if (*next_offptr != next_offset)
842 {
843 /* should already be set to the correct value, or not at all */
844 Assert(*next_offptr == 0);
845 *next_offptr = next_offset;
846 MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
847 }
848
849 /* Release MultiXactOffset SLRU lock. */
850 LWLockRelease(lock);
851
852 prev_pageno = -1;
853
854 for (int i = 0; i < nmembers; i++, offset++)
855 {
856 TransactionId *memberptr;
857 uint32 *flagsptr;
858 uint32 flagsval;
859 int bshift;
860 int flagsoff;
861 int memberoff;
862
863 Assert(members[i].status <= MultiXactStatusUpdate);
864
865 pageno = MXOffsetToMemberPage(offset);
866 memberoff = MXOffsetToMemberOffset(offset);
867 flagsoff = MXOffsetToFlagsOffset(offset);
868 bshift = MXOffsetToFlagsBitShift(offset);
869
870 if (pageno != prev_pageno)
871 {
872 /*
873 * MultiXactMember SLRU page is changed so check if this new page
874 * fall into the different SLRU bank then release the old bank's
875 * lock and acquire lock on the new bank.
876 */
878 if (lock != prevlock)
879 {
880 if (prevlock != NULL)
881 LWLockRelease(prevlock);
882
884 prevlock = lock;
885 }
886 slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
887 prev_pageno = pageno;
888 }
889
890 memberptr = (TransactionId *)
891 (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
892
893 *memberptr = members[i].xid;
894
895 flagsptr = (uint32 *)
896 (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
897
898 flagsval = *flagsptr;
899 flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
900 flagsval |= (members[i].status << bshift);
901 *flagsptr = flagsval;
902
903 MultiXactMemberCtl->shared->page_dirty[slotno] = true;
904 }
905
906 if (prevlock != NULL)
907 LWLockRelease(prevlock);
908}
#define MXACT_MEMBER_BITS_PER_XACT

References Assert(), FirstMultiXactId, i, if(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MultiXactIdToOffsetEntry(), MultiXactIdToOffsetPage(), MultiXactMemberCtl, MultiXactOffsetCtl, MultiXactStatusUpdate, MXACT_MEMBER_BITS_PER_XACT, MXOffsetToFlagsBitShift(), MXOffsetToFlagsOffset(), MXOffsetToMemberOffset(), MXOffsetToMemberPage(), next, NextMultiXactId(), SimpleLruGetBankLock(), SimpleLruReadPage(), MultiXactMember::status, and MultiXactMember::xid.

Referenced by multixact_redo(), and MultiXactIdCreateFromMembers().

◆ SetMultiXactIdLimit()

void SetMultiXactIdLimit ( MultiXactId  oldest_datminmxid,
Oid  oldest_datoid 
)

Definition at line 2011 of file multixact.c.

2012{
2013 MultiXactId multiVacLimit;
2014 MultiXactId multiWarnLimit;
2015 MultiXactId multiStopLimit;
2016 MultiXactId multiWrapLimit;
2017 MultiXactId curMulti;
2018
2019 Assert(MultiXactIdIsValid(oldest_datminmxid));
2020
2021 /*
2022 * We pretend that a wrap will happen halfway through the multixact ID
2023 * space, but that's not really true, because multixacts wrap differently
2024 * from transaction IDs.
2025 */
2026 multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
2027 if (multiWrapLimit < FirstMultiXactId)
2028 multiWrapLimit += FirstMultiXactId;
2029
2030 /*
2031 * We'll refuse to continue assigning MultiXactIds once we get within 3M
2032 * multi of data loss. See SetTransactionIdLimit.
2033 */
2034 multiStopLimit = multiWrapLimit - 3000000;
2035 if (multiStopLimit < FirstMultiXactId)
2036 multiStopLimit -= FirstMultiXactId;
2037
2038 /*
2039 * We'll start complaining loudly when we get within 40M multis of data
2040 * loss. This is kind of arbitrary, but if you let your gas gauge get
2041 * down to 2% of full, would you be looking for the next gas station? We
2042 * need to be fairly liberal about this number because there are lots of
2043 * scenarios where most transactions are done by automatic clients that
2044 * won't pay attention to warnings. (No, we're not gonna make this
2045 * configurable. If you know enough to configure it, you know enough to
2046 * not get in this kind of trouble in the first place.)
2047 */
2048 multiWarnLimit = multiWrapLimit - 40000000;
2049 if (multiWarnLimit < FirstMultiXactId)
2050 multiWarnLimit -= FirstMultiXactId;
2051
2052 /*
2053 * We'll start trying to force autovacuums when oldest_datminmxid gets to
2054 * be more than autovacuum_multixact_freeze_max_age mxids old.
2055 *
2056 * Note: autovacuum_multixact_freeze_max_age is a PGC_POSTMASTER parameter
2057 * so that we don't have to worry about dealing with on-the-fly changes in
2058 * its value. See SetTransactionIdLimit.
2059 */
2060 multiVacLimit = oldest_datminmxid + autovacuum_multixact_freeze_max_age;
2061 if (multiVacLimit < FirstMultiXactId)
2062 multiVacLimit += FirstMultiXactId;
2063
2064 /* Grab lock for just long enough to set the new limit values */
2065 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
2066 MultiXactState->oldestMultiXactId = oldest_datminmxid;
2067 MultiXactState->oldestMultiXactDB = oldest_datoid;
2068 MultiXactState->multiVacLimit = multiVacLimit;
2069 MultiXactState->multiWarnLimit = multiWarnLimit;
2070 MultiXactState->multiStopLimit = multiStopLimit;
2071 MultiXactState->multiWrapLimit = multiWrapLimit;
2072 curMulti = MultiXactState->nextMXact;
2073 LWLockRelease(MultiXactGenLock);
2074
2075 /* Log the info */
2077 (errmsg_internal("MultiXactId wrap limit is %u, limited by database with OID %u",
2078 multiWrapLimit, oldest_datoid)));
2079
2080 /*
2081 * Computing the actual limits is only possible once the data directory is
2082 * in a consistent state. There's no need to compute the limits while
2083 * still replaying WAL - no decisions about new multis are made even
2084 * though multixact creations might be replayed. So we'll only do further
2085 * checks after TrimMultiXact() has been called.
2086 */
2088 return;
2089
2091
2092 /*
2093 * Offsets are 64-bits wide and never wrap around, so we don't need to
2094 * consider them for emergency autovacuum purposes. But now that we're in
2095 * a consistent state, determine MultiXactState->oldestOffset. It will be
2096 * used to adjust the freezing cutoff, to keep the offsets disk usage in
2097 * check.
2098 */
2100
2101 /*
2102 * If past the autovacuum force point, immediately signal an autovac
2103 * request. The reason for this is that autovac only processes one
2104 * database per invocation. Once it's finished cleaning up the oldest
2105 * database, it'll call here, and we'll signal the postmaster to start
2106 * another iteration immediately if there are still any old databases.
2107 */
2108 if (MultiXactIdPrecedes(multiVacLimit, curMulti) && IsUnderPostmaster)
2110
2111 /* Give an immediate warning if past the wrap warn point */
2112 if (MultiXactIdPrecedes(multiWarnLimit, curMulti))
2113 {
2114 char *oldest_datname;
2115
2116 /*
2117 * We can be called when not inside a transaction, for example during
2118 * StartupXLOG(). In such a case we cannot do database access, so we
2119 * must just report the oldest DB's OID.
2120 *
2121 * Note: it's also possible that get_database_name fails and returns
2122 * NULL, for example because the database just got dropped. We'll
2123 * still warn, even though the warning might now be unnecessary.
2124 */
2125 if (IsTransactionState())
2126 oldest_datname = get_database_name(oldest_datoid);
2127 else
2128 oldest_datname = NULL;
2129
2130 if (oldest_datname)
2132 (errmsg_plural("database \"%s\" must be vacuumed before %u more MultiXactId is used",
2133 "database \"%s\" must be vacuumed before %u more MultiXactIds are used",
2134 multiWrapLimit - curMulti,
2135 oldest_datname,
2136 multiWrapLimit - curMulti),
2137 errhint("To avoid MultiXactId assignment failures, execute a database-wide VACUUM in that database.\n"
2138 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
2139 else
2141 (errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used",
2142 "database with OID %u must be vacuumed before %u more MultiXactIds are used",
2143 multiWrapLimit - curMulti,
2144 oldest_datoid,
2145 multiWrapLimit - curMulti),
2146 errhint("To avoid MultiXactId assignment failures, execute a database-wide VACUUM in that database.\n"
2147 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
2148 }
2149}
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1170
static void SetOldestOffset(void)
Definition: multixact.c:2335
bool IsTransactionState(void)
Definition: xact.c:388

References Assert(), autovacuum_multixact_freeze_max_age, DEBUG1, ereport, errhint(), errmsg_internal(), errmsg_plural(), MultiXactStateData::finishedStartup, FirstMultiXactId, get_database_name(), InRecovery, IsTransactionState(), IsUnderPostmaster, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MaxMultiXactId, MultiXactStateData::multiStopLimit, MultiXactStateData::multiVacLimit, MultiXactStateData::multiWarnLimit, MultiXactStateData::multiWrapLimit, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactState, MultiXactStateData::nextMXact, MultiXactStateData::oldestMultiXactDB, MultiXactStateData::oldestMultiXactId, PMSIGNAL_START_AUTOVAC_LAUNCHER, SendPostmasterSignal(), SetOldestOffset(), and WARNING.

Referenced by BootStrapXLOG(), multixact_redo(), MultiXactAdvanceOldest(), StartupXLOG(), TrimMultiXact(), and vac_truncate_clog().

◆ SetOldestOffset()

static void SetOldestOffset ( void  )
static

Definition at line 2335 of file multixact.c.

2336{
2337 MultiXactId oldestMultiXactId;
2338 MultiXactId nextMXact;
2339 MultiXactOffset oldestOffset = 0; /* placate compiler */
2340 MultiXactOffset nextOffset;
2341 bool oldestOffsetKnown = false;
2342
2343 /*
2344 * NB: Have to prevent concurrent truncation, we might otherwise try to
2345 * lookup an oldestMulti that's concurrently getting truncated away.
2346 */
2347 LWLockAcquire(MultiXactTruncationLock, LW_SHARED);
2348
2349 /* Read relevant fields from shared memory. */
2350 LWLockAcquire(MultiXactGenLock, LW_SHARED);
2351 oldestMultiXactId = MultiXactState->oldestMultiXactId;
2352 nextMXact = MultiXactState->nextMXact;
2353 nextOffset = MultiXactState->nextOffset;
2355 LWLockRelease(MultiXactGenLock);
2356
2357 /*
2358 * Determine the offset of the oldest multixact. Normally, we can read
2359 * the offset from the multixact itself, but there's an important special
2360 * case: if there are no multixacts in existence at all, oldestMXact
2361 * obviously can't point to one. It will instead point to the multixact
2362 * ID that will be assigned the next time one is needed.
2363 */
2364 if (oldestMultiXactId == nextMXact)
2365 {
2366 /*
2367 * When the next multixact gets created, it will be stored at the next
2368 * offset.
2369 */
2370 oldestOffset = nextOffset;
2371 oldestOffsetKnown = true;
2372 }
2373 else
2374 {
2375 /*
2376 * Look up the offset at which the oldest existing multixact's members
2377 * are stored. If we cannot find it, be careful not to fail, and
2378 * leave oldestOffset unchanged. oldestOffset is initialized to zero
2379 * at system startup, which prevents truncating members until a proper
2380 * value is calculated.
2381 *
2382 * (We had bugs in early releases of PostgreSQL 9.3.X and 9.4.X where
2383 * the supposedly-earliest multixact might not really exist. Those
2384 * should be long gone by now, so this should not fail, but let's
2385 * still be defensive.)
2386 */
2387 oldestOffsetKnown =
2388 find_multixact_start(oldestMultiXactId, &oldestOffset);
2389
2390 if (oldestOffsetKnown)
2392 (errmsg_internal("oldest MultiXactId member is at offset %" PRIu64,
2393 oldestOffset)));
2394 else
2395 ereport(LOG,
2396 (errmsg("MultiXact member truncation is disabled because oldest checkpointed MultiXact %u does not exist on disk",
2397 oldestMultiXactId)));
2398 }
2399
2400 LWLockRelease(MultiXactTruncationLock);
2401
2402 /* Install the computed value */
2403 if (oldestOffsetKnown)
2404 {
2405 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
2406 MultiXactState->oldestOffset = oldestOffset;
2407 LWLockRelease(MultiXactGenLock);
2408 }
2409}
#define LOG
Definition: elog.h:31
static bool find_multixact_start(MultiXactId multi, MultiXactOffset *result)
Definition: multixact.c:2421

References Assert(), DEBUG1, ereport, errmsg(), errmsg_internal(), find_multixact_start(), MultiXactStateData::finishedStartup, LOG, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MultiXactState, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactId, and MultiXactStateData::oldestOffset.

Referenced by SetMultiXactIdLimit().

◆ SlruScanDirCbFindEarliest()

static bool SlruScanDirCbFindEarliest ( SlruCtl  ctl,
char *  filename,
int64  segpage,
void *  data 
)
static

Definition at line 2566 of file multixact.c.

2567{
2568 mxtruncinfo *trunc = (mxtruncinfo *) data;
2569
2570 if (trunc->earliestExistingPage == -1 ||
2571 ctl->PagePrecedes(segpage, trunc->earliestExistingPage))
2572 {
2573 trunc->earliestExistingPage = segpage;
2574 }
2575
2576 return false; /* keep going */
2577}
const void * data
tree ctl
Definition: radixtree.h:1838
int64 earliestExistingPage
Definition: multixact.c:2558

References ctl, data, and mxtruncinfo::earliestExistingPage.

Referenced by TruncateMultiXact().

◆ StartupMultiXact()

void StartupMultiXact ( void  )

Definition at line 1806 of file multixact.c.

1807{
1810 int64 pageno;
1811
1812 /*
1813 * Initialize offset's idea of the latest page number.
1814 */
1815 pageno = MultiXactIdToOffsetPage(multi);
1816 pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number,
1817 pageno);
1818
1819 /*
1820 * Initialize member's idea of the latest page number.
1821 */
1822 pageno = MXOffsetToMemberPage(offset);
1823 pg_atomic_write_u64(&MultiXactMemberCtl->shared->latest_page_number,
1824 pageno);
1825}

References MultiXactIdToOffsetPage(), MultiXactMemberCtl, MultiXactOffsetCtl, MultiXactState, MXOffsetToMemberPage(), MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, and pg_atomic_write_u64().

Referenced by StartupXLOG().

◆ TrimMultiXact()

void TrimMultiXact ( void  )

Definition at line 1831 of file multixact.c.

1832{
1833 MultiXactId nextMXact;
1834 MultiXactOffset offset;
1835 MultiXactId oldestMXact;
1836 Oid oldestMXactDB;
1837 int64 pageno;
1838 int entryno;
1839 int flagsoff;
1840
1841 LWLockAcquire(MultiXactGenLock, LW_SHARED);
1842 nextMXact = MultiXactState->nextMXact;
1843 offset = MultiXactState->nextOffset;
1844 oldestMXact = MultiXactState->oldestMultiXactId;
1845 oldestMXactDB = MultiXactState->oldestMultiXactDB;
1846 LWLockRelease(MultiXactGenLock);
1847
1848 /* Clean up offsets state */
1849
1850 /*
1851 * (Re-)Initialize our idea of the latest page number for offsets.
1852 */
1853 pageno = MultiXactIdToOffsetPage(nextMXact);
1854 pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number,
1855 pageno);
1856
1857 /*
1858 * Set the offset of nextMXact on the offsets page. This is normally done
1859 * in RecordNewMultiXact() of the previous multixact, but let's be sure
1860 * the next page exists, if the nextMXact was reset with pg_resetwal for
1861 * example.
1862 *
1863 * Zero out the remainder of the page. See notes in TrimCLOG() for
1864 * background. Unlike CLOG, some WAL record covers every pg_multixact
1865 * SLRU mutation. Since, also unlike CLOG, we ignore the WAL rule "write
1866 * xlog before data," nextMXact successors may carry obsolete, nonzero
1867 * offset values.
1868 */
1869 entryno = MultiXactIdToOffsetEntry(nextMXact);
1870 {
1871 int slotno;
1872 MultiXactOffset *offptr;
1874
1876 if (entryno == 0 || nextMXact == FirstMultiXactId)
1877 slotno = SimpleLruZeroPage(MultiXactOffsetCtl, pageno);
1878 else
1879 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, nextMXact);
1880 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
1881 offptr += entryno;
1882
1883 *offptr = offset;
1884 if (entryno != 0 && (entryno + 1) * sizeof(MultiXactOffset) != BLCKSZ)
1885 MemSet(offptr + 1, 0, BLCKSZ - (entryno + 1) * sizeof(MultiXactOffset));
1886
1887 MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
1888 LWLockRelease(lock);
1889 }
1890
1891 /*
1892 * And the same for members.
1893 *
1894 * (Re-)Initialize our idea of the latest page number for members.
1895 */
1896 pageno = MXOffsetToMemberPage(offset);
1897 pg_atomic_write_u64(&MultiXactMemberCtl->shared->latest_page_number,
1898 pageno);
1899
1900 /*
1901 * Zero out the remainder of the current members page. See notes in
1902 * TrimCLOG() for motivation.
1903 */
1904 flagsoff = MXOffsetToFlagsOffset(offset);
1905 if (flagsoff != 0)
1906 {
1907 int slotno;
1908 TransactionId *xidptr;
1909 int memberoff;
1911
1913 memberoff = MXOffsetToMemberOffset(offset);
1914 slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
1915 xidptr = (TransactionId *)
1916 (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
1917
1918 MemSet(xidptr, 0, BLCKSZ - memberoff);
1919
1920 /*
1921 * Note: we don't need to zero out the flag bits in the remaining
1922 * members of the current group, because they are always reset before
1923 * writing.
1924 */
1925
1926 MultiXactMemberCtl->shared->page_dirty[slotno] = true;
1927 LWLockRelease(lock);
1928 }
1929
1930 /* signal that we're officially up */
1931 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
1933 LWLockRelease(MultiXactGenLock);
1934
1935 /* Now compute how far away the next multixid wraparound is. */
1936 SetMultiXactIdLimit(oldestMXact, oldestMXactDB);
1937}

References MultiXactStateData::finishedStartup, FirstMultiXactId, if(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MemSet, MultiXactIdToOffsetEntry(), MultiXactIdToOffsetPage(), MultiXactMemberCtl, MultiXactOffsetCtl, MultiXactState, MXOffsetToFlagsOffset(), MXOffsetToMemberOffset(), MXOffsetToMemberPage(), MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactDB, MultiXactStateData::oldestMultiXactId, pg_atomic_write_u64(), SetMultiXactIdLimit(), SimpleLruGetBankLock(), SimpleLruReadPage(), and SimpleLruZeroPage().

Referenced by StartupXLOG().

◆ TruncateMultiXact()

void TruncateMultiXact ( MultiXactId  newOldestMulti,
Oid  newOldestMultiDB 
)

Definition at line 2619 of file multixact.c.

2620{
2621 MultiXactId oldestMulti;
2622 MultiXactId nextMulti;
2623 MultiXactOffset newOldestOffset;
2624 MultiXactOffset oldestOffset;
2625 MultiXactOffset nextOffset;
2626 mxtruncinfo trunc;
2627 MultiXactId earliest;
2628
2631 Assert(MultiXactIdIsValid(newOldestMulti));
2632
2633 /*
2634 * We can only allow one truncation to happen at once. Otherwise parts of
2635 * members might vanish while we're doing lookups or similar. There's no
2636 * need to have an interlock with creating new multis or such, since those
2637 * are constrained by the limits (which only grow, never shrink).
2638 */
2639 LWLockAcquire(MultiXactTruncationLock, LW_EXCLUSIVE);
2640
2641 LWLockAcquire(MultiXactGenLock, LW_SHARED);
2642 nextMulti = MultiXactState->nextMXact;
2643 nextOffset = MultiXactState->nextOffset;
2644 oldestMulti = MultiXactState->oldestMultiXactId;
2645 LWLockRelease(MultiXactGenLock);
2646
2647 /*
2648 * Make sure to only attempt truncation if there's values to truncate
2649 * away. In normal processing values shouldn't go backwards, but there's
2650 * some corner cases (due to bugs) where that's possible.
2651 */
2652 if (MultiXactIdPrecedesOrEquals(newOldestMulti, oldestMulti))
2653 {
2654 LWLockRelease(MultiXactTruncationLock);
2655 return;
2656 }
2657
2658 /*
2659 * Note we can't just plow ahead with the truncation; it's possible that
2660 * there are no segments to truncate, which is a problem because we are
2661 * going to attempt to read the offsets page to determine where to
2662 * truncate the members SLRU. So we first scan the directory to determine
2663 * the earliest offsets page number that we can read without error.
2664 *
2665 * When nextMXact is less than one segment away from multiWrapLimit,
2666 * SlruScanDirCbFindEarliest can find some early segment other than the
2667 * actual earliest. (MultiXactOffsetPagePrecedes(EARLIEST, LATEST)
2668 * returns false, because not all pairs of entries have the same answer.)
2669 * That can also arise when an earlier truncation attempt failed unlink()
2670 * or returned early from this function. The only consequence is
2671 * returning early, which wastes space that we could have liberated.
2672 *
2673 * NB: It's also possible that the page that oldestMulti is on has already
2674 * been truncated away, and we crashed before updating oldestMulti.
2675 */
2676 trunc.earliestExistingPage = -1;
2679 if (earliest < FirstMultiXactId)
2680 earliest = FirstMultiXactId;
2681
2682 /* If there's nothing to remove, we can bail out early. */
2683 if (MultiXactIdPrecedes(oldestMulti, earliest))
2684 {
2685 LWLockRelease(MultiXactTruncationLock);
2686 return;
2687 }
2688
2689 /*
2690 * First, compute the safe truncation point for MultiXactMember. This is
2691 * the starting offset of the oldest multixact.
2692 *
2693 * Hopefully, find_multixact_start will always work here, because we've
2694 * already checked that it doesn't precede the earliest MultiXact on disk.
2695 * But if it fails, don't truncate anything, and log a message.
2696 */
2697 if (oldestMulti == nextMulti)
2698 {
2699 /* there are NO MultiXacts */
2700 oldestOffset = nextOffset;
2701 }
2702 else if (!find_multixact_start(oldestMulti, &oldestOffset))
2703 {
2704 ereport(LOG,
2705 (errmsg("oldest MultiXact %u not found, earliest MultiXact %u, skipping truncation",
2706 oldestMulti, earliest)));
2707 LWLockRelease(MultiXactTruncationLock);
2708 return;
2709 }
2710
2711 /*
2712 * Secondly compute up to where to truncate. Lookup the corresponding
2713 * member offset for newOldestMulti for that.
2714 */
2715 if (newOldestMulti == nextMulti)
2716 {
2717 /* there are NO MultiXacts */
2718 newOldestOffset = nextOffset;
2719 }
2720 else if (!find_multixact_start(newOldestMulti, &newOldestOffset))
2721 {
2722 ereport(LOG,
2723 (errmsg("cannot truncate up to MultiXact %u because it does not exist on disk, skipping truncation",
2724 newOldestMulti)));
2725 LWLockRelease(MultiXactTruncationLock);
2726 return;
2727 }
2728
2729 elog(DEBUG1, "performing multixact truncation: "
2730 "offsets [%u, %u), offsets segments [%" PRIx64 ", %" PRIx64 "), "
2731 "members [%" PRIu64 ", %" PRIu64 "), members segments [%" PRIx64 ", %" PRIx64 ")",
2732 oldestMulti, newOldestMulti,
2733 MultiXactIdToOffsetSegment(oldestMulti),
2734 MultiXactIdToOffsetSegment(newOldestMulti),
2735 oldestOffset, newOldestOffset,
2736 MXOffsetToMemberSegment(oldestOffset),
2737 MXOffsetToMemberSegment(newOldestOffset));
2738
2739 /*
2740 * Do truncation, and the WAL logging of the truncation, in a critical
2741 * section. That way offsets/members cannot get out of sync anymore, i.e.
2742 * once consistent the newOldestMulti will always exist in members, even
2743 * if we crashed in the wrong moment.
2744 */
2746
2747 /*
2748 * Prevent checkpoints from being scheduled concurrently. This is critical
2749 * because otherwise a truncation record might not be replayed after a
2750 * crash/basebackup, even though the state of the data directory would
2751 * require it.
2752 */
2755
2756 /* WAL log truncation */
2757 WriteMTruncateXlogRec(newOldestMultiDB,
2758 oldestMulti, newOldestMulti,
2759 oldestOffset, newOldestOffset);
2760
2761 /*
2762 * Update in-memory limits before performing the truncation, while inside
2763 * the critical section: Have to do it before truncation, to prevent
2764 * concurrent lookups of those values. Has to be inside the critical
2765 * section as otherwise a future call to this function would error out,
2766 * while looking up the oldest member in offsets, if our caller crashes
2767 * before updating the limits.
2768 */
2769 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
2770 MultiXactState->oldestMultiXactId = newOldestMulti;
2771 MultiXactState->oldestMultiXactDB = newOldestMultiDB;
2772 MultiXactState->oldestOffset = newOldestOffset;
2773 LWLockRelease(MultiXactGenLock);
2774
2775 /* First truncate members */
2776 PerformMembersTruncation(oldestOffset, newOldestOffset);
2777
2778 /* Then offsets */
2779 PerformOffsetsTruncation(oldestMulti, newOldestMulti);
2780
2781 MyProc->delayChkptFlags &= ~DELAY_CHKPT_START;
2782
2784 LWLockRelease(MultiXactTruncationLock);
2785}
static void WriteMTruncateXlogRec(Oid oldestMultiDB, MultiXactId startTruncOff, MultiXactId endTruncOff, MultiXactOffset startTruncMemb, MultiXactOffset endTruncMemb)
Definition: multixact.c:2857
static bool SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int64 segpage, void *data)
Definition: multixact.c:2566
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:2842
#define DELAY_CHKPT_START
Definition: proc.h:135
bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
Definition: slru.c:1816
PGPROC * MyProc
Definition: proc.c:67
int delayChkptFlags
Definition: proc.h:257

References Assert(), DEBUG1, DELAY_CHKPT_START, PGPROC::delayChkptFlags, mxtruncinfo::earliestExistingPage, elog, END_CRIT_SECTION, ereport, errmsg(), find_multixact_start(), MultiXactStateData::finishedStartup, FirstMultiXactId, LOG, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MULTIXACT_OFFSETS_PER_PAGE, MultiXactIdIsValid, MultiXactIdPrecedes(), MultiXactIdPrecedesOrEquals(), MultiXactIdToOffsetSegment(), MultiXactOffsetCtl, MultiXactState, MXOffsetToMemberSegment(), MyProc, MultiXactStateData::nextMXact, MultiXactStateData::nextOffset, MultiXactStateData::oldestMultiXactDB, MultiXactStateData::oldestMultiXactId, MultiXactStateData::oldestOffset, PerformMembersTruncation(), PerformOffsetsTruncation(), RecoveryInProgress(), SlruScanDirCbFindEarliest(), SlruScanDirectory(), START_CRIT_SECTION, and WriteMTruncateXlogRec().

Referenced by vac_truncate_clog().

◆ WriteMTruncateXlogRec()

static void WriteMTruncateXlogRec ( Oid  oldestMultiDB,
MultiXactId  startTruncOff,
MultiXactId  endTruncOff,
MultiXactOffset  startTruncMemb,
MultiXactOffset  endTruncMemb 
)
static

Definition at line 2857 of file multixact.c.

2860{
2861 XLogRecPtr recptr;
2863
2864 xlrec.oldestMultiDB = oldestMultiDB;
2865
2866 xlrec.startTruncOff = startTruncOff;
2867 xlrec.endTruncOff = endTruncOff;
2868
2869 xlrec.startTruncMemb = startTruncMemb;
2870 xlrec.endTruncMemb = endTruncMemb;
2871
2874 recptr = XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_TRUNCATE_ID);
2875 XLogFlush(recptr);
2876}
void XLogFlush(XLogRecPtr record)
Definition: xlog.c:2783
uint64 XLogRecPtr
Definition: xlogdefs.h:21

References xl_multixact_truncate::endTruncMemb, xl_multixact_truncate::endTruncOff, xl_multixact_truncate::oldestMultiDB, SizeOfMultiXactTruncate, xl_multixact_truncate::startTruncMemb, xl_multixact_truncate::startTruncOff, XLOG_MULTIXACT_TRUNCATE_ID, XLogBeginInsert(), XLogFlush(), XLogInsert(), and XLogRegisterData().

Referenced by TruncateMultiXact().

Variable Documentation

◆ MultiXactMemberCtlData

SlruCtlData MultiXactMemberCtlData
static

Definition at line 117 of file multixact.c.

◆ MultiXactOffsetCtlData

SlruCtlData MultiXactOffsetCtlData
static

Definition at line 116 of file multixact.c.

◆ MultiXactState

◆ MXactCache

◆ MXactContext

MemoryContext MXactContext = NULL
static

Definition at line 248 of file multixact.c.

Referenced by AtEOXact_MultiXact(), mXactCachePut(), and PostPrepare_MultiXact().

◆ OldestMemberMXactId

◆ OldestVisibleMXactId