Age Owner Branch data TLA 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 : :
136 msawada@postgresql.o 22 :GNC 118 : 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 : 117 : PG_FUNCTION_INFO_V1(consume_xids);
31 : : Datum
32 : 114 : consume_xids(PG_FUNCTION_ARGS)
33 : : {
34 : 114 : int64 nxids = PG_GETARG_INT64(0);
35 : : FullTransactionId lastxid;
36 : :
37 [ - + ]: 114 : if (nxids < 0)
136 msawada@postgresql.o 38 [ # # ]:UNC 0 : elog(ERROR, "invalid nxids argument: %lld", (long long) nxids);
39 : :
136 msawada@postgresql.o 40 [ - + ]:GNC 114 : if (nxids == 0)
136 msawada@postgresql.o 41 :UNC 0 : lastxid = ReadNextFullTransactionId();
42 : : else
136 msawada@postgresql.o 43 :GNC 114 : lastxid = consume_xids_common(InvalidFullTransactionId, (uint64) nxids);
44 : :
45 : 113 : PG_RETURN_FULLTRANSACTIONID(lastxid);
46 : : }
47 : :
48 : : /*
49 : : * Consume XIDs, up to the given XID.
50 : : */
51 : 4 : PG_FUNCTION_INFO_V1(consume_xids_until);
52 : : Datum
53 : 1 : consume_xids_until(PG_FUNCTION_ARGS)
54 : : {
55 : 1 : FullTransactionId targetxid = PG_GETARG_FULLTRANSACTIONID(0);
56 : : FullTransactionId lastxid;
57 : :
58 [ - + ]: 1 : if (!FullTransactionIdIsNormal(targetxid))
136 msawada@postgresql.o 59 [ # # ]:UNC 0 : elog(ERROR, "targetxid %llu is not normal",
60 : : (unsigned long long) U64FromFullTransactionId(targetxid));
61 : :
136 msawada@postgresql.o 62 :GNC 1 : lastxid = consume_xids_common(targetxid, 0);
63 : :
64 : 1 : PG_RETURN_FULLTRANSACTIONID(lastxid);
65 : : }
66 : :
67 : : /*
68 : : * Common functionality between the two public functions.
69 : : */
70 : : static FullTransactionId
71 : 115 : consume_xids_common(FullTransactionId untilxid, uint64 nxids)
72 : : {
73 : : FullTransactionId lastxid;
74 : 115 : uint64 last_reported_at = 0;
75 : 115 : 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 : 115 : 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 : 115 : (void) GetTopTransactionId();
93 : :
94 : : for (;;)
95 : 48514918 : {
96 : : uint64 xids_left;
97 : :
98 [ - + ]: 48515033 : CHECK_FOR_INTERRUPTS();
99 : :
100 : : /* How many XIDs do we have left to consume? */
101 [ + + ]: 48515033 : if (nxids > 0)
102 : : {
103 [ + + ]: 41680614 : if (consumed >= nxids)
104 : 113 : break;
105 : 41680501 : xids_left = nxids - consumed;
106 : : }
107 : : else
108 : : {
109 [ + + ]: 6834419 : if (FullTransactionIdFollowsOrEquals(lastxid, untilxid))
110 : 1 : break;
111 : 6834418 : 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 [ + + ]: 48514919 : if (xids_left > 2000 &&
119 [ + + ]: 48326903 : consumed - last_reported_at < REPORT_INTERVAL &&
120 [ + + ]: 48325606 : MyProc->subxidStatus.overflowed)
121 : : {
122 : 48318131 : int64 consumed_by_shortcut = consume_xids_shortcut();
123 : :
124 [ + + ]: 48318131 : if (consumed_by_shortcut > 0)
125 : : {
126 : 24151331 : consumed += consumed_by_shortcut;
127 : 24151331 : continue;
128 : : }
129 : : }
130 : :
131 : : /* Slow path: Call GetNewTransactionId to allocate a new XID. */
132 : 24363588 : lastxid = GetNewTransactionId(true);
133 : 24363587 : consumed++;
134 : :
135 : : /* Report progress */
136 [ + + ]: 24363587 : if (consumed - last_reported_at >= REPORT_INTERVAL)
137 : : {
138 [ + + ]: 1311 : if (nxids > 0)
139 [ + - ]: 1112 : 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 [ + - ]: 199 : elog(NOTICE, "consumed up to %u:%u / %u:%u",
145 : : EpochFromFullTransactionId(lastxid),
146 : : XidFromFullTransactionId(lastxid),
147 : : EpochFromFullTransactionId(untilxid),
148 : : XidFromFullTransactionId(untilxid));
149 : 1311 : last_reported_at = consumed;
150 : : }
151 : : }
152 : :
153 : 114 : 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 : 48318131 : XidSkip(FullTransactionId fullxid)
172 : : {
173 : 48318131 : uint32 low = XidFromFullTransactionId(fullxid);
174 : : uint32 rem;
175 : : uint32 distance;
176 : :
177 [ + + + + ]: 48318131 : if (low < 5 || low >= UINT32_MAX - 5)
178 : 16 : return 0;
179 : 48318115 : distance = UINT32_MAX - 5 - low;
180 : :
181 : 48318115 : rem = low % COMMIT_TS_XACTS_PER_PAGE;
182 [ + + ]: 48318115 : if (rem == 0)
183 : 17269467 : return 0;
184 : 31048648 : distance = Min(distance, COMMIT_TS_XACTS_PER_PAGE - rem);
185 : :
186 : 31048648 : rem = low % SUBTRANS_XACTS_PER_PAGE;
187 [ + + ]: 31048648 : if (rem == 0)
188 : 6897317 : return 0;
189 : 24151331 : distance = Min(distance, SUBTRANS_XACTS_PER_PAGE - rem);
190 : :
191 : 24151331 : rem = low % CLOG_XACTS_PER_PAGE;
192 [ - + ]: 24151331 : if (rem == 0)
136 msawada@postgresql.o 193 :UNC 0 : return 0;
136 msawada@postgresql.o 194 :GNC 24151331 : distance = Min(distance, CLOG_XACTS_PER_PAGE - rem);
195 : :
196 : 24151331 : return distance;
197 : : }
198 : :
199 : : static int64
200 : 48318131 : consume_xids_shortcut(void)
201 : : {
202 : : FullTransactionId nextXid;
203 : : uint32 consumed;
204 : :
205 : 48318131 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
128 heikki.linnakangas@i 206 : 48318131 : 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 : : */
136 msawada@postgresql.o 212 : 48318131 : consumed = XidSkip(nextXid);
213 [ + + ]: 48318131 : if (consumed > 0)
128 heikki.linnakangas@i 214 : 24151331 : TransamVariables->nextXid.value += (uint64) consumed;
215 : :
136 msawada@postgresql.o 216 : 48318131 : LWLockRelease(XidGenLock);
217 : :
218 : 48318131 : return consumed;
219 : : }
|