PostgreSQL Source Code git master
xid_wraparound.c
Go to the documentation of this file.
1/*--------------------------------------------------------------------------
2 *
3 * xid_wraparound.c
4 * Utilities for testing XID wraparound
5 *
6 *
7 * Portions Copyright (c) 1996-2025, 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
23
24static int64 consume_xids_shortcut(void);
26
27/*
28 * Consume the specified number of XIDs.
29 */
33{
34 int64 nxids = PG_GETARG_INT64(0);
35 FullTransactionId lastxid;
36
37 if (nxids < 0)
38 elog(ERROR, "invalid nxids argument: %lld", (long long) nxids);
39
40 if (nxids == 0)
41 lastxid = ReadNextFullTransactionId();
42 else
44
46}
47
48/*
49 * Consume XIDs, up to the given XID.
50 */
54{
56 FullTransactionId lastxid;
57
58 if (!FullTransactionIdIsNormal(targetxid))
59 elog(ERROR, "targetxid %llu is not normal",
60 (unsigned long long) U64FromFullTransactionId(targetxid));
61
62 lastxid = consume_xids_common(targetxid, 0);
63
65}
66
67/*
68 * Common functionality between the two public functions.
69 */
72{
73 FullTransactionId lastxid;
74 uint64 last_reported_at = 0;
75 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 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 (void) GetTopTransactionId();
93
94 for (;;)
95 {
96 uint64 xids_left;
97
99
100 /* How many XIDs do we have left to consume? */
101 if (nxids > 0)
102 {
103 if (consumed >= nxids)
104 break;
105 xids_left = nxids - consumed;
106 }
107 else
108 {
109 if (FullTransactionIdFollowsOrEquals(lastxid, untilxid))
110 break;
111 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 if (xids_left > 2000 &&
119 consumed - last_reported_at < REPORT_INTERVAL &&
121 {
122 int64 consumed_by_shortcut = consume_xids_shortcut();
123
124 if (consumed_by_shortcut > 0)
125 {
126 consumed += consumed_by_shortcut;
127 continue;
128 }
129 }
130
131 /* Slow path: Call GetNewTransactionId to allocate a new XID. */
132 lastxid = GetNewTransactionId(true);
133 consumed++;
134
135 /* Report progress */
136 if (consumed - last_reported_at >= REPORT_INTERVAL)
137 {
138 if (nxids > 0)
139 elog(NOTICE, "consumed %llu / %llu XIDs, latest %u:%u",
140 (unsigned long long) consumed, (unsigned long long) nxids,
142 XidFromFullTransactionId(lastxid));
143 else
144 elog(NOTICE, "consumed up to %u:%u / %u:%u",
148 XidFromFullTransactionId(untilxid));
149 last_reported_at = consumed;
150 }
151 }
152
153 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 */
170static inline uint32
172{
173 uint32 low = XidFromFullTransactionId(fullxid);
174 uint32 rem;
175 uint32 distance;
176
177 if (low < 5 || low >= UINT32_MAX - 5)
178 return 0;
179 distance = UINT32_MAX - 5 - low;
180
181 rem = low % COMMIT_TS_XACTS_PER_PAGE;
182 if (rem == 0)
183 return 0;
184 distance = Min(distance, COMMIT_TS_XACTS_PER_PAGE - rem);
185
186 rem = low % SUBTRANS_XACTS_PER_PAGE;
187 if (rem == 0)
188 return 0;
189 distance = Min(distance, SUBTRANS_XACTS_PER_PAGE - rem);
190
191 rem = low % CLOG_XACTS_PER_PAGE;
192 if (rem == 0)
193 return 0;
194 distance = Min(distance, CLOG_XACTS_PER_PAGE - rem);
195
196 return distance;
197}
198
199static int64
201{
202 FullTransactionId nextXid;
203 uint32 consumed;
204
205 LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
206 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 consumed = XidSkip(nextXid);
213 if (consumed > 0)
214 TransamVariables->nextXid.value += (uint64) consumed;
215
216 LWLockRelease(XidGenLock);
217
218 return consumed;
219}
#define Min(x, y)
Definition: c.h:961
int64_t int64
Definition: c.h:485
uint64_t uint64
Definition: c.h:489
uint32_t uint32
Definition: c.h:488
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1168
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1781
@ LW_EXCLUSIVE
Definition: lwlock.h:114
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
uintptr_t Datum
Definition: postgres.h:69
PGPROC * MyProc
Definition: proc.c:66
uint64 value
Definition: transam.h:67
XidCacheStatus subxidStatus
Definition: proc.h:263
FullTransactionId nextXid
Definition: transam.h:220
bool overflowed
Definition: proc.h:46
#define FullTransactionIdIsNormal(x)
Definition: transam.h:58
#define EpochFromFullTransactionId(x)
Definition: transam.h:47
#define U64FromFullTransactionId(x)
Definition: transam.h:49
#define FullTransactionIdFollowsOrEquals(a, b)
Definition: transam.h:54
#define XidFromFullTransactionId(x)
Definition: transam.h:48
#define InvalidFullTransactionId
Definition: transam.h:56
FullTransactionId ReadNextFullTransactionId(void)
Definition: varsup.c:288
FullTransactionId GetNewTransactionId(bool isSubXact)
Definition: varsup.c:77
TransamVariablesData * TransamVariables
Definition: varsup.c:34
TransactionId GetTopTransactionId(void)
Definition: xact.c:425
#define PG_GETARG_FULLTRANSACTIONID(X)
Definition: xid8.h:29
#define PG_RETURN_FULLTRANSACTIONID(X)
Definition: xid8.h:30
PG_FUNCTION_INFO_V1(consume_xids)
static uint32 XidSkip(FullTransactionId fullxid)
#define CLOG_XACTS_PER_PAGE
#define COMMIT_TS_XACTS_PER_PAGE
#define SUBTRANS_XACTS_PER_PAGE
Datum consume_xids_until(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
static FullTransactionId consume_xids_common(FullTransactionId untilxid, uint64 nxids)
Datum consume_xids(PG_FUNCTION_ARGS)
#define REPORT_INTERVAL
static int64 consume_xids_shortcut(void)