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-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 
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  */
31 Datum
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
43  lastxid = consume_xids_common(InvalidFullTransactionId, (uint64) nxids);
44 
46 }
47 
48 /*
49  * Consume XIDs, up to the given XID.
50  */
52 Datum
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  */
70 static FullTransactionId
71 consume_xids_common(FullTransactionId untilxid, uint64 nxids)
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",
146  XidFromFullTransactionId(lastxid),
147  EpochFromFullTransactionId(untilxid),
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  */
170 static 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 
199 static 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 }
unsigned int uint32
Definition: c.h:506
#define Min(x, y)
Definition: c.h:1004
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#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:1170
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1783
@ LW_EXCLUSIVE
Definition: lwlock.h:114
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
uintptr_t Datum
Definition: postgres.h:64
PGPROC * MyProc
Definition: proc.c:66
uint64 value
Definition: transam.h:67
XidCacheStatus subxidStatus
Definition: proc.h:259
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:423
#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)