Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * varsup.c
4 : : * postgres OID & XID variables support routines
5 : : *
6 : : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/backend/access/transam/varsup.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres.h"
15 : :
16 : : #include "access/clog.h"
17 : : #include "access/commit_ts.h"
18 : : #include "access/subtrans.h"
19 : : #include "access/transam.h"
20 : : #include "access/xact.h"
21 : : #include "access/xlogutils.h"
22 : : #include "miscadmin.h"
23 : : #include "postmaster/autovacuum.h"
24 : : #include "storage/pmsignal.h"
25 : : #include "storage/proc.h"
26 : : #include "storage/subsystems.h"
27 : : #include "utils/lsyscache.h"
28 : : #include "utils/syscache.h"
29 : :
30 : :
31 : : /* Number of OIDs to prefetch (preallocate) per XLOG write */
32 : : #define VAR_OID_PREFETCH 8192
33 : :
34 : : static void VarsupShmemRequest(void *arg);
35 : :
36 : : /* pointer to variables struct in shared memory */
37 : : TransamVariablesData *TransamVariables = NULL;
38 : :
39 : : const ShmemCallbacks VarsupShmemCallbacks = {
40 : : .request_fn = VarsupShmemRequest,
41 : : };
42 : :
43 : : /*
44 : : * Request shared memory for TransamVariables.
45 : : */
46 : : static void
29 heikki.linnakangas@i 47 :GNC 1244 : VarsupShmemRequest(void *arg)
48 : : {
49 : 1244 : ShmemRequestStruct(.name = "TransamVariables",
50 : : .size = sizeof(TransamVariablesData),
51 : : .ptr = (void **) &TransamVariables,
52 : : );
879 heikki.linnakangas@i 53 :CBC 1244 : }
54 : :
55 : : /*
56 : : * Allocate the next FullTransactionId for a new transaction or
57 : : * subtransaction.
58 : : *
59 : : * The new XID is also stored into MyProc->xid/ProcGlobal->xids[] before
60 : : * returning.
61 : : *
62 : : * Note: when this is called, we are actually already inside a valid
63 : : * transaction, since XIDs are now not allocated until the transaction
64 : : * does something. So it is safe to do a database lookup if we want to
65 : : * issue a warning about XID wrap.
66 : : */
67 : : FullTransactionId
7978 tgl@sss.pgh.pa.us 68 : 171529 : GetNewTransactionId(bool isSubXact)
69 : : {
70 : : FullTransactionId full_xid;
71 : : TransactionId xid;
72 : :
73 : : /*
74 : : * Workers synchronize transaction state at the beginning of each parallel
75 : : * operation, so we can't account for new XIDs after that point.
76 : : */
4023 rhaas@postgresql.org 77 [ - + ]: 171529 : if (IsInParallelMode())
4023 rhaas@postgresql.org 78 [ # # ]:UBC 0 : elog(ERROR, "cannot assign TransactionIds during a parallel operation");
79 : :
80 : : /*
81 : : * During bootstrap initialization, we return the special bootstrap
82 : : * transaction id.
83 : : */
7744 tgl@sss.pgh.pa.us 84 [ + + ]:CBC 171529 : if (IsBootstrapProcessingMode())
85 : : {
6814 86 [ - + ]: 57 : Assert(!isSubXact);
2090 andres@anarazel.de 87 : 57 : MyProc->xid = BootstrapTransactionId;
88 : 57 : ProcGlobal->xids[MyProc->pgxactoff] = BootstrapTransactionId;
2595 tmunro@postgresql.or 89 : 57 : return FullTransactionIdFromEpochAndXid(0, BootstrapTransactionId);
90 : : }
91 : :
92 : : /* safety check, we should never get this far in a HS standby */
5918 tgl@sss.pgh.pa.us 93 [ - + ]: 171472 : if (RecoveryInProgress())
5918 tgl@sss.pgh.pa.us 94 [ # # ]:UBC 0 : elog(ERROR, "cannot assign TransactionIds during recovery");
95 : :
8984 tgl@sss.pgh.pa.us 96 :CBC 171472 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
97 : :
879 heikki.linnakangas@i 98 : 171472 : full_xid = TransamVariables->nextXid;
2595 tmunro@postgresql.or 99 : 171472 : xid = XidFromFullTransactionId(full_xid);
100 : :
101 : : /*----------
102 : : * Check to see if it's safe to assign another XID. This protects against
103 : : * catastrophic data loss due to XID wraparound. The basic rules are:
104 : : *
105 : : * If we're past xidVacLimit, start trying to force autovacuum cycles.
106 : : * If we're past xidWarnLimit, start issuing warnings.
107 : : * If we're past xidStopLimit, refuse to execute transactions, unless
108 : : * we are running in single-user mode (which gives an escape hatch
109 : : * to the DBA who somehow got past the earlier defenses).
110 : : *
111 : : * Note that this coding also appears in GetNewMultiXactId.
112 : : *----------
113 : : */
879 heikki.linnakangas@i 114 [ - + ]: 171472 : if (TransactionIdFollowsOrEquals(xid, TransamVariables->xidVacLimit))
115 : : {
116 : : /*
117 : : * For safety's sake, we release XidGenLock while sending signals,
118 : : * warnings, etc. This is not so much because we care about
119 : : * preserving concurrency in this situation, as to avoid any
120 : : * possibility of deadlock while doing get_database_name(). First,
121 : : * copy all the shared values we'll need in this path.
122 : : */
879 heikki.linnakangas@i 123 :UBC 0 : TransactionId xidWarnLimit = TransamVariables->xidWarnLimit;
124 : 0 : TransactionId xidStopLimit = TransamVariables->xidStopLimit;
125 : 0 : TransactionId xidWrapLimit = TransamVariables->xidWrapLimit;
126 : 0 : Oid oldest_datoid = TransamVariables->oldestXidDB;
127 : :
6091 tgl@sss.pgh.pa.us 128 : 0 : LWLockRelease(XidGenLock);
129 : :
130 : : /*
131 : : * To avoid swamping the postmaster with signals, we issue the autovac
132 : : * request only once per 64K transaction starts. This still gives
133 : : * plenty of chances before we get into real trouble.
134 : : */
7121 135 [ # # # # ]: 0 : if (IsUnderPostmaster && (xid % 65536) == 0)
7019 alvherre@alvh.no-ip. 136 : 0 : SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
137 : :
7744 tgl@sss.pgh.pa.us 138 [ # # # # ]: 0 : if (IsUnderPostmaster &&
6091 139 : 0 : TransactionIdFollowsOrEquals(xid, xidStopLimit))
140 : : {
5912 bruce@momjian.us 141 : 0 : char *oldest_datname = get_database_name(oldest_datoid);
142 : :
143 : : /* complain even if that DB has disappeared */
6091 tgl@sss.pgh.pa.us 144 [ # # ]: 0 : if (oldest_datname)
145 [ # # ]: 0 : ereport(ERROR,
146 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
147 : : errmsg("database is not accepting commands that assign new transaction IDs to avoid wraparound data loss in database \"%s\"",
148 : : oldest_datname),
149 : : errhint("Execute a database-wide VACUUM in that database.\n"
150 : : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
151 : : else
152 [ # # ]: 0 : ereport(ERROR,
153 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
154 : : errmsg("database is not accepting commands that assign new transaction IDs to avoid wraparound data loss in database with OID %u",
155 : : oldest_datoid),
156 : : errhint("Execute a database-wide VACUUM in that database.\n"
157 : : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
158 : : }
159 [ # # ]: 0 : else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
160 : : {
5912 bruce@momjian.us 161 : 0 : char *oldest_datname = get_database_name(oldest_datoid);
162 : :
163 : : /* complain even if that DB has disappeared */
6091 tgl@sss.pgh.pa.us 164 [ # # ]: 0 : if (oldest_datname)
165 [ # # ]: 0 : ereport(WARNING,
166 : : (errmsg("database \"%s\" must be vacuumed within %u transactions",
167 : : oldest_datname,
168 : : xidWrapLimit - xid),
169 : : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
170 : : (double) (xidWrapLimit - xid) / (MaxTransactionId / 2) * 100),
171 : : errhint("To avoid transaction ID assignment failures, execute a database-wide VACUUM in that database.\n"
172 : : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
173 : : else
174 [ # # ]: 0 : ereport(WARNING,
175 : : (errmsg("database with OID %u must be vacuumed within %u transactions",
176 : : oldest_datoid,
177 : : xidWrapLimit - xid),
178 : : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
179 : : (double) (xidWrapLimit - xid) / (MaxTransactionId / 2) * 100),
180 : : errhint("To avoid XID assignment failures, execute a database-wide VACUUM in that database.\n"
181 : : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
182 : : }
183 : :
184 : : /* Re-acquire lock and start over */
185 : 0 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 186 : 0 : full_xid = TransamVariables->nextXid;
2580 tmunro@postgresql.or 187 : 0 : xid = XidFromFullTransactionId(full_xid);
188 : : }
189 : :
190 : : /*
191 : : * If we are allocating the first XID of a new page of the commit log,
192 : : * zero out that commit-log page before returning. We must do this while
193 : : * holding XidGenLock, else another xact could acquire and commit a later
194 : : * XID before we zero the page. Fortunately, a page of the commit log
195 : : * holds 32K or more transactions, so we don't have to do this very often.
196 : : *
197 : : * Extend pg_subtrans and pg_commit_ts too.
198 : : */
9019 tgl@sss.pgh.pa.us 199 :CBC 171472 : ExtendCLOG(xid);
4171 alvherre@alvh.no-ip. 200 : 171472 : ExtendCommitTs(xid);
7978 tgl@sss.pgh.pa.us 201 : 171472 : ExtendSUBTRANS(xid);
202 : :
203 : : /*
204 : : * Now advance the nextXid counter. This must not happen until after we
205 : : * have successfully completed ExtendCLOG() --- if that routine fails, we
206 : : * want the next incoming transaction to try it again. We cannot assign
207 : : * more XIDs until there is CLOG space for them.
208 : : */
879 heikki.linnakangas@i 209 : 171472 : FullTransactionIdAdvance(&TransamVariables->nextXid);
210 : :
211 : : /*
212 : : * We must store the new XID into the shared ProcArray before releasing
213 : : * XidGenLock. This ensures that every active XID older than
214 : : * latestCompletedXid is present in the ProcArray, which is essential for
215 : : * correct OldestXmin tracking; see src/backend/access/transam/README.
216 : : *
217 : : * Note that readers of ProcGlobal->xids/PGPROC->xid should be careful to
218 : : * fetch the value for each proc only once, rather than assume they can
219 : : * read a value multiple times and get the same answer each time. Note we
220 : : * are assuming that TransactionId and int fetch/store are atomic.
221 : : *
222 : : * The same comments apply to the subxact xid count and overflow fields.
223 : : *
224 : : * Use of a write barrier prevents dangerous code rearrangement in this
225 : : * function; other backends could otherwise e.g. be examining my subxids
226 : : * info concurrently, and we don't want them to see an invalid
227 : : * intermediate state, such as an incremented nxids before the array entry
228 : : * is filled.
229 : : *
230 : : * Other processes that read nxids should do so before reading xids
231 : : * elements with a pg_read_barrier() in between, so that they can be sure
232 : : * not to read an uninitialized array element; see
233 : : * src/backend/storage/lmgr/README.barrier.
234 : : *
235 : : * If there's no room to fit a subtransaction XID into PGPROC, set the
236 : : * cache-overflowed flag instead. This forces readers to look in
237 : : * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There is a
238 : : * race-condition window, in that the new XID will not appear as running
239 : : * until its parent link has been placed into pg_subtrans. However, that
240 : : * will happen before anyone could possibly have a reason to inquire about
241 : : * the status of the XID, so it seems OK. (Snapshots taken during this
242 : : * window *will* include the parent XID, so they will deliver the correct
243 : : * answer later on when someone does have a reason to inquire.)
244 : : */
2734 andres@anarazel.de 245 [ + + ]: 171472 : if (!isSubXact)
246 : : {
2090 247 [ - + ]: 165257 : Assert(ProcGlobal->subxidStates[MyProc->pgxactoff].count == 0);
248 [ - + ]: 165257 : Assert(!ProcGlobal->subxidStates[MyProc->pgxactoff].overflowed);
249 [ - + ]: 165257 : Assert(MyProc->subxidStatus.count == 0);
250 [ - + ]: 165257 : Assert(!MyProc->subxidStatus.overflowed);
251 : :
252 : : /* LWLockRelease acts as barrier */
253 : 165257 : MyProc->xid = xid;
254 : 165257 : ProcGlobal->xids[MyProc->pgxactoff] = xid;
255 : : }
256 : : else
257 : : {
258 : 6215 : XidCacheStatus *substat = &ProcGlobal->subxidStates[MyProc->pgxactoff];
259 : 6215 : int nxids = MyProc->subxidStatus.count;
260 : :
261 [ - + ]: 6215 : Assert(substat->count == MyProc->subxidStatus.count);
262 [ - + ]: 6215 : Assert(substat->overflowed == MyProc->subxidStatus.overflowed);
263 : :
2734 264 [ + + ]: 6215 : if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
265 : : {
266 : 2464 : MyProc->subxids.xids[nxids] = xid;
267 : 2464 : pg_write_barrier();
2090 268 : 2464 : MyProc->subxidStatus.count = substat->count = nxids + 1;
269 : : }
270 : : else
271 : 3751 : MyProc->subxidStatus.overflowed = substat->overflowed = true;
272 : : }
273 : :
8984 tgl@sss.pgh.pa.us 274 : 171472 : LWLockRelease(XidGenLock);
275 : :
2595 tmunro@postgresql.or 276 : 171472 : return full_xid;
277 : : }
278 : :
279 : : /*
280 : : * Read nextXid but don't allocate it.
281 : : */
282 : : FullTransactionId
283 : 43946 : ReadNextFullTransactionId(void)
284 : : {
285 : : FullTransactionId fullXid;
286 : :
8984 tgl@sss.pgh.pa.us 287 : 43946 : LWLockAcquire(XidGenLock, LW_SHARED);
879 heikki.linnakangas@i 288 : 43946 : fullXid = TransamVariables->nextXid;
8984 tgl@sss.pgh.pa.us 289 : 43946 : LWLockRelease(XidGenLock);
290 : :
2595 tmunro@postgresql.or 291 : 43946 : return fullXid;
292 : : }
293 : :
294 : : /*
295 : : * Advance nextXid to the value after a given xid. The epoch is inferred.
296 : : * This must only be called during recovery or from two-phase start-up code.
297 : : */
298 : : void
299 : 2995526 : AdvanceNextFullTransactionIdPastXid(TransactionId xid)
300 : : {
301 : : FullTransactionId newNextFullXid;
302 : : TransactionId next_xid;
303 : : uint32 epoch;
304 : :
305 : : /*
306 : : * It is safe to read nextXid without a lock, because this is only called
307 : : * from the startup process or single-process mode, meaning that no other
308 : : * process can modify it.
309 : : */
310 [ + + - + ]: 2995526 : Assert(AmStartupProcess() || !IsUnderPostmaster);
311 : :
312 : : /* Fast return if this isn't an xid high enough to move the needle. */
879 heikki.linnakangas@i 313 : 2995526 : next_xid = XidFromFullTransactionId(TransamVariables->nextXid);
2595 tmunro@postgresql.or 314 [ + + ]: 2995526 : if (!TransactionIdFollowsOrEquals(xid, next_xid))
315 : 2969225 : return;
316 : :
317 : : /*
318 : : * Compute the FullTransactionId that comes after the given xid. To do
319 : : * this, we preserve the existing epoch, but detect when we've wrapped
320 : : * into a new epoch. This is necessary because WAL records and 2PC state
321 : : * currently contain 32 bit xids. The wrap logic is safe in those cases
322 : : * because the span of active xids cannot exceed one epoch at any given
323 : : * point in the WAL stream.
324 : : */
325 [ - + ]: 26301 : TransactionIdAdvance(xid);
879 heikki.linnakangas@i 326 : 26301 : epoch = EpochFromFullTransactionId(TransamVariables->nextXid);
2595 tmunro@postgresql.or 327 [ - + ]: 26301 : if (unlikely(xid < next_xid))
2595 tmunro@postgresql.or 328 :UBC 0 : ++epoch;
2595 tmunro@postgresql.or 329 :CBC 26301 : newNextFullXid = FullTransactionIdFromEpochAndXid(epoch, xid);
330 : :
331 : : /*
332 : : * We still need to take a lock to modify the value when there are
333 : : * concurrent readers.
334 : : */
335 : 26301 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 336 : 26301 : TransamVariables->nextXid = newNextFullXid;
2595 tmunro@postgresql.or 337 : 26301 : LWLockRelease(XidGenLock);
338 : : }
339 : :
340 : : /*
341 : : * Advance the cluster-wide value for the oldest valid clog entry.
342 : : *
343 : : * We must acquire XactTruncationLock to advance the oldestClogXid. It's not
344 : : * necessary to hold the lock during the actual clog truncation, only when we
345 : : * advance the limit, as code looking up arbitrary xids is required to hold
346 : : * XactTruncationLock from when it tests oldestClogXid through to when it
347 : : * completes the clog lookup.
348 : : */
349 : : void
3330 rhaas@postgresql.org 350 : 1136 : AdvanceOldestClogXid(TransactionId oldest_datfrozenxid)
351 : : {
2181 tgl@sss.pgh.pa.us 352 : 1136 : LWLockAcquire(XactTruncationLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 353 [ + + ]: 1136 : if (TransactionIdPrecedes(TransamVariables->oldestClogXid,
354 : : oldest_datfrozenxid))
355 : : {
356 : 1079 : TransamVariables->oldestClogXid = oldest_datfrozenxid;
357 : : }
2181 tgl@sss.pgh.pa.us 358 : 1136 : LWLockRelease(XactTruncationLock);
3330 rhaas@postgresql.org 359 : 1136 : }
360 : :
361 : : /*
362 : : * Determine the last safe XID to allocate using the currently oldest
363 : : * datfrozenxid (ie, the oldest XID that might exist in any database
364 : : * of our cluster), and the OID of the (or a) database with that value.
365 : : */
366 : : void
6091 tgl@sss.pgh.pa.us 367 : 1294 : SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
368 : : {
369 : : TransactionId xidVacLimit;
370 : : TransactionId xidWarnLimit;
371 : : TransactionId xidStopLimit;
372 : : TransactionId xidWrapLimit;
373 : : TransactionId curXid;
374 : :
7121 375 [ - + ]: 1294 : Assert(TransactionIdIsNormal(oldest_datfrozenxid));
376 : :
377 : : /*
378 : : * The place where we actually get into deep trouble is halfway around
379 : : * from the oldest potentially-existing XID. (This calculation is
380 : : * probably off by one or two counts, because the special XIDs reduce the
381 : : * size of the loop a little bit. But we throw in plenty of slop below,
382 : : * so it doesn't matter.)
383 : : */
384 : 1294 : xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
7744 385 [ - + ]: 1294 : if (xidWrapLimit < FirstNormalTransactionId)
7744 tgl@sss.pgh.pa.us 386 :UBC 0 : xidWrapLimit += FirstNormalTransactionId;
387 : :
388 : : /*
389 : : * We'll refuse to continue assigning XIDs in interactive mode once we get
390 : : * within 3M transactions of data loss. This leaves lots of room for the
391 : : * DBA to fool around fixing things in a standalone backend, while not
392 : : * being significant compared to total XID space. (VACUUM requires an XID
393 : : * if it truncates at wal_level!=minimal. "VACUUM (ANALYZE)", which a DBA
394 : : * might do by reflex, assigns an XID. Hence, we had better be sure
395 : : * there's lots of XIDs left...) Also, at default BLCKSZ, this leaves two
396 : : * completely-idle segments. In the event of edge-case bugs involving
397 : : * page or segment arithmetic, idle segments render the bugs unreachable
398 : : * outside of single-user mode.
399 : : */
2103 noah@leadboat.com 400 :CBC 1294 : xidStopLimit = xidWrapLimit - 3000000;
7744 tgl@sss.pgh.pa.us 401 [ - + ]: 1294 : if (xidStopLimit < FirstNormalTransactionId)
7744 tgl@sss.pgh.pa.us 402 :UBC 0 : xidStopLimit -= FirstNormalTransactionId;
403 : :
404 : : /*
405 : : * We'll start complaining loudly when we get within 100M transactions of
406 : : * data loss. This is kind of arbitrary, but if you let your gas gauge
407 : : * get down to 5% of full, would you be looking for the next gas station?
408 : : * We need to be fairly liberal about this number because there are lots
409 : : * of scenarios where most transactions are done by automatic clients that
410 : : * won't pay attention to warnings. (No, we're not gonna make this
411 : : * configurable. If you know enough to configure it, you know enough to
412 : : * not get in this kind of trouble in the first place.)
413 : : */
46 nathan@postgresql.or 414 :GNC 1294 : xidWarnLimit = xidWrapLimit - 100000000;
7744 tgl@sss.pgh.pa.us 415 [ - + ]:CBC 1294 : if (xidWarnLimit < FirstNormalTransactionId)
7744 tgl@sss.pgh.pa.us 416 :UBC 0 : xidWarnLimit -= FirstNormalTransactionId;
417 : :
418 : : /*
419 : : * We'll start trying to force autovacuums when oldest_datfrozenxid gets
420 : : * to be more than autovacuum_freeze_max_age transactions old.
421 : : *
422 : : * Note: guc.c ensures that autovacuum_freeze_max_age is in a sane range,
423 : : * so that xidVacLimit will be well before xidWarnLimit.
424 : : *
425 : : * Note: autovacuum_freeze_max_age is a PGC_POSTMASTER parameter so that
426 : : * we don't have to worry about dealing with on-the-fly changes in its
427 : : * value. It doesn't look practical to update shared state from a GUC
428 : : * assign hook (too many processes would try to execute the hook,
429 : : * resulting in race conditions as well as crashes of those not connected
430 : : * to shared memory). Perhaps this can be improved someday. See also
431 : : * SetMultiXactIdLimit.
432 : : */
7121 tgl@sss.pgh.pa.us 433 :CBC 1294 : xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
434 [ - + ]: 1294 : if (xidVacLimit < FirstNormalTransactionId)
7121 tgl@sss.pgh.pa.us 435 :UBC 0 : xidVacLimit += FirstNormalTransactionId;
436 : :
437 : : /* Grab lock for just long enough to set the new limit values */
7744 tgl@sss.pgh.pa.us 438 :CBC 1294 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 439 : 1294 : TransamVariables->oldestXid = oldest_datfrozenxid;
440 : 1294 : TransamVariables->xidVacLimit = xidVacLimit;
441 : 1294 : TransamVariables->xidWarnLimit = xidWarnLimit;
442 : 1294 : TransamVariables->xidStopLimit = xidStopLimit;
443 : 1294 : TransamVariables->xidWrapLimit = xidWrapLimit;
444 : 1294 : TransamVariables->oldestXidDB = oldest_datoid;
445 : 1294 : curXid = XidFromFullTransactionId(TransamVariables->nextXid);
7744 tgl@sss.pgh.pa.us 446 : 1294 : LWLockRelease(XidGenLock);
447 : :
448 : : /* Log the info */
7161 alvherre@alvh.no-ip. 449 [ + + ]: 1294 : ereport(DEBUG1,
450 : : (errmsg_internal("transaction ID wrap limit is %u, limited by database with OID %u",
451 : : xidWrapLimit, oldest_datoid)));
452 : :
453 : : /*
454 : : * If past the autovacuum force point, immediately signal an autovac
455 : : * request. The reason for this is that autovac only processes one
456 : : * database per invocation. Once it's finished cleaning up the oldest
457 : : * database, it'll call here, and we'll signal the postmaster to start
458 : : * another iteration immediately if there are still any old databases.
459 : : */
7121 tgl@sss.pgh.pa.us 460 [ - + - - ]: 1294 : if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
5921 tgl@sss.pgh.pa.us 461 [ # # ]:UBC 0 : IsUnderPostmaster && !InRecovery)
7019 alvherre@alvh.no-ip. 462 : 0 : SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
463 : :
464 : : /* Give an immediate warning if past the wrap warn point */
5921 tgl@sss.pgh.pa.us 465 [ - + - - ]:CBC 1294 : if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
466 : : {
467 : : char *oldest_datname;
468 : :
469 : : /*
470 : : * We can be called when not inside a transaction, for example during
471 : : * StartupXLOG(). In such a case we cannot do database access, so we
472 : : * must just report the oldest DB's OID.
473 : : *
474 : : * Note: it's also possible that get_database_name fails and returns
475 : : * NULL, for example because the database just got dropped. We'll
476 : : * still warn, even though the warning might now be unnecessary.
477 : : */
5676 tgl@sss.pgh.pa.us 478 [ # # ]:UBC 0 : if (IsTransactionState())
479 : 0 : oldest_datname = get_database_name(oldest_datoid);
480 : : else
481 : 0 : oldest_datname = NULL;
482 : :
6091 483 [ # # ]: 0 : if (oldest_datname)
484 [ # # ]: 0 : ereport(WARNING,
485 : : (errmsg("database \"%s\" must be vacuumed within %u transactions",
486 : : oldest_datname,
487 : : xidWrapLimit - curXid),
488 : : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
489 : : (double) (xidWrapLimit - curXid) / (MaxTransactionId / 2) * 100),
490 : : errhint("To avoid XID assignment failures, execute a database-wide VACUUM in that database.\n"
491 : : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
492 : : else
493 [ # # ]: 0 : ereport(WARNING,
494 : : (errmsg("database with OID %u must be vacuumed within %u transactions",
495 : : oldest_datoid,
496 : : xidWrapLimit - curXid),
497 : : errdetail("Approximately %.2f%% of transaction IDs are available for use.",
498 : : (double) (xidWrapLimit - curXid) / (MaxTransactionId / 2) * 100),
499 : : errhint("To avoid XID assignment failures, execute a database-wide VACUUM in that database.\n"
500 : : "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
501 : : }
6091 tgl@sss.pgh.pa.us 502 :CBC 1294 : }
503 : :
504 : :
505 : : /*
506 : : * ForceTransactionIdLimitUpdate -- does the XID wrap-limit data need updating?
507 : : *
508 : : * We primarily check whether oldestXidDB is valid. The cases we have in
509 : : * mind are that that database was dropped, or the field was reset to zero
510 : : * by pg_resetwal. In either case we should force recalculation of the
511 : : * wrap limit. Also do it if oldestXid is old enough to be forcing
512 : : * autovacuums or other actions; this ensures we update our state as soon
513 : : * as possible once extra overhead is being incurred.
514 : : */
515 : : bool
6090 516 : 1061 : ForceTransactionIdLimitUpdate(void)
517 : : {
518 : : TransactionId nextXid;
519 : : TransactionId xidVacLimit;
520 : : TransactionId oldestXid;
521 : : Oid oldestXidDB;
522 : :
523 : : /* Locking is probably not really necessary, but let's be careful */
6091 524 : 1061 : LWLockAcquire(XidGenLock, LW_SHARED);
879 heikki.linnakangas@i 525 : 1061 : nextXid = XidFromFullTransactionId(TransamVariables->nextXid);
526 : 1061 : xidVacLimit = TransamVariables->xidVacLimit;
527 : 1061 : oldestXid = TransamVariables->oldestXid;
528 : 1061 : oldestXidDB = TransamVariables->oldestXidDB;
6091 tgl@sss.pgh.pa.us 529 : 1061 : LWLockRelease(XidGenLock);
530 : :
531 [ - + ]: 1061 : if (!TransactionIdIsNormal(oldestXid))
6090 tgl@sss.pgh.pa.us 532 :UBC 0 : return true; /* shouldn't happen, but just in case */
5921 tgl@sss.pgh.pa.us 533 [ - + ]:CBC 1061 : if (!TransactionIdIsValid(xidVacLimit))
5921 tgl@sss.pgh.pa.us 534 :UBC 0 : return true; /* this shouldn't happen anymore either */
5921 tgl@sss.pgh.pa.us 535 [ - + ]:CBC 1061 : if (TransactionIdFollowsOrEquals(nextXid, xidVacLimit))
2451 michael@paquier.xyz 536 :UBC 0 : return true; /* past xidVacLimit, don't delay updating */
5924 rhaas@postgresql.org 537 [ - + ]:CBC 1061 : if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(oldestXidDB)))
6090 tgl@sss.pgh.pa.us 538 :UBC 0 : return true; /* could happen, per comments above */
6090 tgl@sss.pgh.pa.us 539 :CBC 1061 : return false;
540 : : }
541 : :
542 : :
543 : : /*
544 : : * GetNewObjectId -- allocate a new OID
545 : : *
546 : : * OIDs are generated by a cluster-wide counter. Since they are only 32 bits
547 : : * wide, counter wraparound will occur eventually, and therefore it is unwise
548 : : * to assume they are unique unless precautions are taken to make them so.
549 : : * Hence, this routine should generally not be used directly. The only direct
550 : : * callers should be GetNewOidWithIndex() and GetNewRelFileNumber() in
551 : : * catalog/catalog.c.
552 : : */
553 : : Oid
9034 554 : 610751 : GetNewObjectId(void)
555 : : {
556 : : Oid result;
557 : :
558 : : /* safety check, we should never get this far in a HS standby */
5918 559 [ - + ]: 610751 : if (RecoveryInProgress())
5918 tgl@sss.pgh.pa.us 560 [ # # ]:UBC 0 : elog(ERROR, "cannot assign OIDs during recovery");
561 : :
8984 tgl@sss.pgh.pa.us 562 :CBC 610751 : LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
563 : :
564 : : /*
565 : : * Check for wraparound of the OID counter. We *must* not return 0
566 : : * (InvalidOid), and in normal operation we mustn't return anything below
567 : : * FirstNormalObjectId since that range is reserved for initdb (see
568 : : * IsCatalogRelationOid()). Note we are relying on unsigned comparison.
569 : : *
570 : : * During initdb, we start the OID generator at FirstGenbkiObjectId, so we
571 : : * only wrap if before that point when in bootstrap or standalone mode.
572 : : * The first time through this routine after normal postmaster start, the
573 : : * counter will be forced up to FirstNormalObjectId. This mechanism
574 : : * leaves the OIDs between FirstGenbkiObjectId and FirstNormalObjectId
575 : : * available for automatic assignment during initdb, while ensuring they
576 : : * will never conflict with user-assigned OIDs.
577 : : */
879 heikki.linnakangas@i 578 [ + + ]: 610751 : if (TransamVariables->nextOid < ((Oid) FirstNormalObjectId))
579 : : {
7692 tgl@sss.pgh.pa.us 580 [ + + ]: 151081 : if (IsPostmasterEnvironment)
581 : : {
582 : : /* wraparound, or first post-initdb assignment, in normal mode */
879 heikki.linnakangas@i 583 : 406 : TransamVariables->nextOid = FirstNormalObjectId;
584 : 406 : TransamVariables->oidCount = 0;
585 : : }
586 : : else
587 : : {
588 : : /* we may be bootstrapping, so don't enforce the full range */
589 [ - + ]: 150675 : if (TransamVariables->nextOid < ((Oid) FirstGenbkiObjectId))
590 : : {
591 : : /* wraparound in standalone mode (unlikely but possible) */
879 heikki.linnakangas@i 592 :UBC 0 : TransamVariables->nextOid = FirstNormalObjectId;
593 : 0 : TransamVariables->oidCount = 0;
594 : : }
595 : : }
596 : : }
597 : :
598 : : /* If we run out of logged for use oids then we must log more */
879 heikki.linnakangas@i 599 [ + + ]:CBC 610751 : if (TransamVariables->oidCount == 0)
600 : : {
601 : 690 : XLogPutNextOid(TransamVariables->nextOid + VAR_OID_PREFETCH);
602 : 690 : TransamVariables->oidCount = VAR_OID_PREFETCH;
603 : : }
604 : :
605 : 610751 : result = TransamVariables->nextOid;
606 : :
607 : 610751 : (TransamVariables->nextOid)++;
608 : 610751 : (TransamVariables->oidCount)--;
609 : :
8984 tgl@sss.pgh.pa.us 610 : 610751 : LWLockRelease(OidGenLock);
611 : :
9034 612 : 610751 : return result;
613 : : }
614 : :
615 : : /*
616 : : * SetNextObjectId
617 : : *
618 : : * This may only be called during initdb; it advances the OID counter
619 : : * to the specified value.
620 : : */
621 : : static void
1755 622 : 55 : SetNextObjectId(Oid nextOid)
623 : : {
624 : : /* Safety check, this is only allowable during initdb */
625 [ - + ]: 55 : if (IsPostmasterEnvironment)
1755 tgl@sss.pgh.pa.us 626 [ # # ]:UBC 0 : elog(ERROR, "cannot advance OID counter anymore");
627 : :
628 : : /* Taking the lock is, therefore, just pro forma; but do it anyway */
1755 tgl@sss.pgh.pa.us 629 :CBC 55 : LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
630 : :
879 heikki.linnakangas@i 631 [ - + ]: 55 : if (TransamVariables->nextOid > nextOid)
1755 tgl@sss.pgh.pa.us 632 [ # # ]:UBC 0 : elog(ERROR, "too late to advance OID counter to %u, it is now %u",
633 : : nextOid, TransamVariables->nextOid);
634 : :
879 heikki.linnakangas@i 635 :CBC 55 : TransamVariables->nextOid = nextOid;
636 : 55 : TransamVariables->oidCount = 0;
637 : :
1755 tgl@sss.pgh.pa.us 638 : 55 : LWLockRelease(OidGenLock);
639 : 55 : }
640 : :
641 : : /*
642 : : * StopGeneratingPinnedObjectIds
643 : : *
644 : : * This is called once during initdb to force the OID counter up to
645 : : * FirstUnpinnedObjectId. This supports letting initdb's post-bootstrap
646 : : * processing create some pinned objects early on. Once it's done doing
647 : : * so, it calls this (via pg_stop_making_pinned_objects()) so that the
648 : : * remaining objects it makes will be considered un-pinned.
649 : : */
650 : : void
651 : 55 : StopGeneratingPinnedObjectIds(void)
652 : : {
653 : 55 : SetNextObjectId(FirstUnpinnedObjectId);
654 : 55 : }
655 : :
656 : :
657 : : #ifdef USE_ASSERT_CHECKING
658 : :
659 : : /*
660 : : * Assert that xid is between [oldestXid, nextXid], which is the range we
661 : : * expect XIDs coming from tables etc to be in.
662 : : *
663 : : * As TransamVariables->oldestXid could change just after this call without
664 : : * further precautions, and as a wrapped-around xid could again fall within
665 : : * the valid range, this assertion can only detect if something is definitely
666 : : * wrong, but not establish correctness.
667 : : *
668 : : * This intentionally does not expose a return value, to avoid code being
669 : : * introduced that depends on the return value.
670 : : */
671 : : void
2093 andres@anarazel.de 672 : 18033581 : AssertTransactionIdInAllowableRange(TransactionId xid)
673 : : {
674 : : TransactionId oldest_xid;
675 : : TransactionId next_xid;
676 : :
677 [ - + ]: 18033581 : Assert(TransactionIdIsValid(xid));
678 : :
679 : : /* we may see bootstrap / frozen */
680 [ - + ]: 18033581 : if (!TransactionIdIsNormal(xid))
2093 andres@anarazel.de 681 :UBC 0 : return;
682 : :
683 : : /*
684 : : * We can't acquire XidGenLock, as this may be called with XidGenLock
685 : : * already held (or with other locks that don't allow XidGenLock to be
686 : : * nested). That's ok for our purposes though, since we already rely on
687 : : * 32bit reads to be atomic. While nextXid is 64 bit, we only look at the
688 : : * lower 32bit, so a skewed read doesn't hurt.
689 : : *
690 : : * There's no increased danger of falling outside [oldest, next] by
691 : : * accessing them without a lock. xid needs to have been created with
692 : : * GetNewTransactionId() in the originating session, and the locks there
693 : : * pair with the memory barrier below. We do however accept xid to be <=
694 : : * to next_xid, instead of just <, as xid could be from the procarray,
695 : : * before we see the updated nextXid value.
696 : : */
2093 andres@anarazel.de 697 :CBC 18033581 : pg_memory_barrier();
879 heikki.linnakangas@i 698 : 18033581 : oldest_xid = TransamVariables->oldestXid;
699 : 18033581 : next_xid = XidFromFullTransactionId(TransamVariables->nextXid);
700 : :
2093 andres@anarazel.de 701 [ - + - - ]: 18033581 : Assert(TransactionIdFollowsOrEquals(xid, oldest_xid) ||
702 : : TransactionIdPrecedesOrEquals(xid, next_xid));
703 : : }
704 : : #endif
|