Line data Source code
1 : /*-------------------------------------------------------------------------- 2 : * 3 : * xid_wraparound.c 4 : * Utilities for testing XID wraparound 5 : * 6 : * 7 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group 8 : * Portions Copyright (c) 1994, Regents of the University of California 9 : * 10 : * IDENTIFICATION 11 : * src/test/modules/xid_wraparound/xid_wraparound.c 12 : * 13 : * ------------------------------------------------------------------------- 14 : */ 15 : #include "postgres.h" 16 : 17 : #include "access/xact.h" 18 : #include "miscadmin.h" 19 : #include "storage/proc.h" 20 : #include "utils/xid8.h" 21 : 22 0 : PG_MODULE_MAGIC; 23 : 24 : static int64 consume_xids_shortcut(void); 25 : static FullTransactionId consume_xids_common(FullTransactionId untilxid, uint64 nxids); 26 : 27 : /* 28 : * Consume the specified number of XIDs. 29 : */ 30 0 : PG_FUNCTION_INFO_V1(consume_xids); 31 : Datum 32 0 : consume_xids(PG_FUNCTION_ARGS) 33 : { 34 0 : int64 nxids = PG_GETARG_INT64(0); 35 : FullTransactionId lastxid; 36 : 37 0 : if (nxids < 0) 38 0 : elog(ERROR, "invalid nxids argument: %lld", (long long) nxids); 39 : 40 0 : if (nxids == 0) 41 0 : lastxid = ReadNextFullTransactionId(); 42 : else 43 0 : lastxid = consume_xids_common(InvalidFullTransactionId, (uint64) nxids); 44 : 45 0 : PG_RETURN_FULLTRANSACTIONID(lastxid); 46 : } 47 : 48 : /* 49 : * Consume XIDs, up to the given XID. 50 : */ 51 0 : PG_FUNCTION_INFO_V1(consume_xids_until); 52 : Datum 53 0 : consume_xids_until(PG_FUNCTION_ARGS) 54 : { 55 0 : FullTransactionId targetxid = PG_GETARG_FULLTRANSACTIONID(0); 56 : FullTransactionId lastxid; 57 : 58 0 : if (!FullTransactionIdIsNormal(targetxid)) 59 0 : elog(ERROR, "targetxid %llu is not normal", 60 : (unsigned long long) U64FromFullTransactionId(targetxid)); 61 : 62 0 : lastxid = consume_xids_common(targetxid, 0); 63 : 64 0 : PG_RETURN_FULLTRANSACTIONID(lastxid); 65 : } 66 : 67 : /* 68 : * Common functionality between the two public functions. 69 : */ 70 : static FullTransactionId 71 0 : consume_xids_common(FullTransactionId untilxid, uint64 nxids) 72 : { 73 : FullTransactionId lastxid; 74 0 : uint64 last_reported_at = 0; 75 0 : uint64 consumed = 0; 76 : 77 : /* Print a NOTICE every REPORT_INTERVAL xids */ 78 : #define REPORT_INTERVAL (10 * 1000000) 79 : 80 : /* initialize 'lastxid' with the system's current next XID */ 81 0 : lastxid = ReadNextFullTransactionId(); 82 : 83 : /* 84 : * We consume XIDs by calling GetNewTransactionId(true), which marks the 85 : * consumed XIDs as subtransactions of the current top-level transaction. 86 : * For that to work, this transaction must have a top-level XID. 87 : * 88 : * GetNewTransactionId registers them in the subxid cache in PGPROC, until 89 : * the cache overflows, but beyond that, we don't keep track of the 90 : * consumed XIDs. 91 : */ 92 0 : (void) GetTopTransactionId(); 93 : 94 : for (;;) 95 0 : { 96 : uint64 xids_left; 97 : 98 0 : CHECK_FOR_INTERRUPTS(); 99 : 100 : /* How many XIDs do we have left to consume? */ 101 0 : if (nxids > 0) 102 : { 103 0 : if (consumed >= nxids) 104 0 : break; 105 0 : xids_left = nxids - consumed; 106 : } 107 : else 108 : { 109 0 : if (FullTransactionIdFollowsOrEquals(lastxid, untilxid)) 110 0 : break; 111 0 : xids_left = U64FromFullTransactionId(untilxid) - U64FromFullTransactionId(lastxid); 112 : } 113 : 114 : /* 115 : * If we still have plenty of XIDs to consume, try to take a shortcut 116 : * and bump up the nextXid counter directly. 117 : */ 118 0 : if (xids_left > 2000 && 119 0 : consumed - last_reported_at < REPORT_INTERVAL && 120 0 : MyProc->subxidStatus.overflowed) 121 : { 122 0 : int64 consumed_by_shortcut = consume_xids_shortcut(); 123 : 124 0 : if (consumed_by_shortcut > 0) 125 : { 126 0 : consumed += consumed_by_shortcut; 127 0 : continue; 128 : } 129 : } 130 : 131 : /* Slow path: Call GetNewTransactionId to allocate a new XID. */ 132 0 : lastxid = GetNewTransactionId(true); 133 0 : consumed++; 134 : 135 : /* Report progress */ 136 0 : if (consumed - last_reported_at >= REPORT_INTERVAL) 137 : { 138 0 : if (nxids > 0) 139 0 : elog(NOTICE, "consumed %llu / %llu XIDs, latest %u:%u", 140 : (unsigned long long) consumed, (unsigned long long) nxids, 141 : EpochFromFullTransactionId(lastxid), 142 : XidFromFullTransactionId(lastxid)); 143 : else 144 0 : elog(NOTICE, "consumed up to %u:%u / %u:%u", 145 : EpochFromFullTransactionId(lastxid), 146 : XidFromFullTransactionId(lastxid), 147 : EpochFromFullTransactionId(untilxid), 148 : XidFromFullTransactionId(untilxid)); 149 0 : last_reported_at = consumed; 150 : } 151 : } 152 : 153 0 : return lastxid; 154 : } 155 : 156 : /* 157 : * These constants copied from .c files, because they're private. 158 : */ 159 : #define COMMIT_TS_XACTS_PER_PAGE (BLCKSZ / 10) 160 : #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId)) 161 : #define CLOG_XACTS_PER_BYTE 4 162 : #define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE) 163 : 164 : /* 165 : * All the interesting action in GetNewTransactionId happens when we extend 166 : * the SLRUs, or at the uint32 wraparound. If the nextXid counter is not close 167 : * to any of those interesting values, take a shortcut and bump nextXID 168 : * directly, close to the next "interesting" value. 169 : */ 170 : static inline uint32 171 0 : XidSkip(FullTransactionId fullxid) 172 : { 173 0 : uint32 low = XidFromFullTransactionId(fullxid); 174 : uint32 rem; 175 : uint32 distance; 176 : 177 0 : if (low < 5 || low >= UINT32_MAX - 5) 178 0 : return 0; 179 0 : distance = UINT32_MAX - 5 - low; 180 : 181 0 : rem = low % COMMIT_TS_XACTS_PER_PAGE; 182 0 : if (rem == 0) 183 0 : return 0; 184 0 : distance = Min(distance, COMMIT_TS_XACTS_PER_PAGE - rem); 185 : 186 0 : rem = low % SUBTRANS_XACTS_PER_PAGE; 187 0 : if (rem == 0) 188 0 : return 0; 189 0 : distance = Min(distance, SUBTRANS_XACTS_PER_PAGE - rem); 190 : 191 0 : rem = low % CLOG_XACTS_PER_PAGE; 192 0 : if (rem == 0) 193 0 : return 0; 194 0 : distance = Min(distance, CLOG_XACTS_PER_PAGE - rem); 195 : 196 0 : return distance; 197 : } 198 : 199 : static int64 200 0 : consume_xids_shortcut(void) 201 : { 202 : FullTransactionId nextXid; 203 : uint32 consumed; 204 : 205 0 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE); 206 0 : nextXid = TransamVariables->nextXid; 207 : 208 : /* 209 : * Go slow near the "interesting values". The interesting zones include 5 210 : * transactions before and after SLRU page switches. 211 : */ 212 0 : consumed = XidSkip(nextXid); 213 0 : if (consumed > 0) 214 0 : TransamVariables->nextXid.value += (uint64) consumed; 215 : 216 0 : LWLockRelease(XidGenLock); 217 : 218 0 : return consumed; 219 : }