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 236 : 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 234 : PG_FUNCTION_INFO_V1(consume_xids); 31 : Datum 32 228 : consume_xids(PG_FUNCTION_ARGS) 33 : { 34 228 : int64 nxids = PG_GETARG_INT64(0); 35 : FullTransactionId lastxid; 36 : 37 228 : if (nxids < 0) 38 0 : elog(ERROR, "invalid nxids argument: %lld", (long long) nxids); 39 : 40 228 : if (nxids == 0) 41 0 : lastxid = ReadNextFullTransactionId(); 42 : else 43 228 : lastxid = consume_xids_common(InvalidFullTransactionId, (uint64) nxids); 44 : 45 226 : PG_RETURN_FULLTRANSACTIONID(lastxid); 46 : } 47 : 48 : /* 49 : * Consume XIDs, up to the given XID. 50 : */ 51 8 : PG_FUNCTION_INFO_V1(consume_xids_until); 52 : Datum 53 2 : consume_xids_until(PG_FUNCTION_ARGS) 54 : { 55 2 : FullTransactionId targetxid = PG_GETARG_FULLTRANSACTIONID(0); 56 : FullTransactionId lastxid; 57 : 58 2 : if (!FullTransactionIdIsNormal(targetxid)) 59 0 : elog(ERROR, "targetxid %llu is not normal", 60 : (unsigned long long) U64FromFullTransactionId(targetxid)); 61 : 62 2 : lastxid = consume_xids_common(targetxid, 0); 63 : 64 2 : PG_RETURN_FULLTRANSACTIONID(lastxid); 65 : } 66 : 67 : /* 68 : * Common functionality between the two public functions. 69 : */ 70 : static FullTransactionId 71 230 : consume_xids_common(FullTransactionId untilxid, uint64 nxids) 72 : { 73 : FullTransactionId lastxid; 74 230 : uint64 last_reported_at = 0; 75 230 : 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 230 : 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 230 : (void) GetTopTransactionId(); 93 : 94 : for (;;) 95 97027396 : { 96 : uint64 xids_left; 97 : 98 97027626 : CHECK_FOR_INTERRUPTS(); 99 : 100 : /* How many XIDs do we have left to consume? */ 101 97027626 : if (nxids > 0) 102 : { 103 83358792 : if (consumed >= nxids) 104 226 : break; 105 83358566 : xids_left = nxids - consumed; 106 : } 107 : else 108 : { 109 13668834 : if (FullTransactionIdFollowsOrEquals(lastxid, untilxid)) 110 2 : break; 111 13668832 : 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 97027398 : if (xids_left > 2000 && 119 96653812 : consumed - last_reported_at < REPORT_INTERVAL && 120 96651218 : MyProc->subxidStatus.overflowed) 121 : { 122 96636268 : int64 consumed_by_shortcut = consume_xids_shortcut(); 123 : 124 96636268 : if (consumed_by_shortcut > 0) 125 : { 126 48302666 : consumed += consumed_by_shortcut; 127 48302666 : continue; 128 : } 129 : } 130 : 131 : /* Slow path: Call GetNewTransactionId to allocate a new XID. */ 132 48724732 : lastxid = GetNewTransactionId(true); 133 48724730 : consumed++; 134 : 135 : /* Report progress */ 136 48724730 : if (consumed - last_reported_at >= REPORT_INTERVAL) 137 : { 138 2622 : if (nxids > 0) 139 2224 : 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 398 : elog(NOTICE, "consumed up to %u:%u / %u:%u", 145 : EpochFromFullTransactionId(lastxid), 146 : XidFromFullTransactionId(lastxid), 147 : EpochFromFullTransactionId(untilxid), 148 : XidFromFullTransactionId(untilxid)); 149 2622 : last_reported_at = consumed; 150 : } 151 : } 152 : 153 228 : 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 96636268 : XidSkip(FullTransactionId fullxid) 172 : { 173 96636268 : uint32 low = XidFromFullTransactionId(fullxid); 174 : uint32 rem; 175 : uint32 distance; 176 : 177 96636268 : if (low < 5 || low >= UINT32_MAX - 5) 178 32 : return 0; 179 96636236 : distance = UINT32_MAX - 5 - low; 180 : 181 96636236 : rem = low % COMMIT_TS_XACTS_PER_PAGE; 182 96636236 : if (rem == 0) 183 34538934 : return 0; 184 62097302 : distance = Min(distance, COMMIT_TS_XACTS_PER_PAGE - rem); 185 : 186 62097302 : rem = low % SUBTRANS_XACTS_PER_PAGE; 187 62097302 : if (rem == 0) 188 13794636 : return 0; 189 48302666 : distance = Min(distance, SUBTRANS_XACTS_PER_PAGE - rem); 190 : 191 48302666 : rem = low % CLOG_XACTS_PER_PAGE; 192 48302666 : if (rem == 0) 193 0 : return 0; 194 48302666 : distance = Min(distance, CLOG_XACTS_PER_PAGE - rem); 195 : 196 48302666 : return distance; 197 : } 198 : 199 : static int64 200 96636268 : consume_xids_shortcut(void) 201 : { 202 : FullTransactionId nextXid; 203 : uint32 consumed; 204 : 205 96636268 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE); 206 96636268 : 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 96636268 : consumed = XidSkip(nextXid); 213 96636268 : if (consumed > 0) 214 48302666 : TransamVariables->nextXid.value += (uint64) consumed; 215 : 216 96636268 : LWLockRelease(XidGenLock); 217 : 218 96636268 : return consumed; 219 : }