1 : /*-------------------------------------------------------------------------
2 : *
3 : * varsup.c
4 : * postgres OID & XID variables support routines
5 : *
6 : * Copyright (c) 2000-2008, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.80 2007/11/15 21:14:32 momjian Exp $
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 :
14 : #include "postgres.h"
15 :
16 : #include "access/clog.h"
17 : #include "access/subtrans.h"
18 : #include "access/transam.h"
19 : #include "miscadmin.h"
20 : #include "postmaster/autovacuum.h"
21 : #include "storage/pmsignal.h"
22 : #include "storage/proc.h"
23 : #include "utils/builtins.h"
24 :
25 :
26 : /* Number of OIDs to prefetch (preallocate) per XLOG write */
27 : #define VAR_OID_PREFETCH 8192
28 :
29 : /* pointer to "variable cache" in shared memory (set up by shmem.c) */
30 : VariableCache ShmemVariableCache = NULL;
31 :
32 :
33 : /*
34 : * Allocate the next XID for my new transaction or subtransaction.
35 : *
36 : * The new XID is also stored into MyProc before returning.
37 : */
38 : TransactionId
39 : GetNewTransactionId(bool isSubXact)
40 8671 : {
41 : TransactionId xid;
42 :
43 : /*
44 : * During bootstrap initialization, we return the special bootstrap
45 : * transaction id.
46 : */
47 8671 : if (IsBootstrapProcessingMode())
48 : {
49 : Assert(!isSubXact);
50 4241 : MyProc->xid = BootstrapTransactionId;
51 4241 : return BootstrapTransactionId;
52 : }
53 :
54 4430 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
55 :
56 4430 : xid = ShmemVariableCache->nextXid;
57 :
58 : /*----------
59 : * Check to see if it's safe to assign another XID. This protects against
60 : * catastrophic data loss due to XID wraparound. The basic rules are:
61 : *
62 : * If we're past xidVacLimit, start trying to force autovacuum cycles.
63 : * If we're past xidWarnLimit, start issuing warnings.
64 : * If we're past xidStopLimit, refuse to execute transactions, unless
65 : * we are running in a standalone backend (which gives an escape hatch
66 : * to the DBA who somehow got past the earlier defenses).
67 : *
68 : * Test is coded to fall out as fast as possible during normal operation,
69 : * ie, when the vac limit is set and we haven't violated it.
70 : *----------
71 : */
72 4430 : if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit) &&
73 : TransactionIdIsValid(ShmemVariableCache->xidVacLimit))
74 : {
75 : /*
76 : * To avoid swamping the postmaster with signals, we issue the autovac
77 : * request only once per 64K transaction starts. This still gives
78 : * plenty of chances before we get into real trouble.
79 : */
80 0 : if (IsUnderPostmaster && (xid % 65536) == 0)
81 0 : SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
82 :
83 0 : if (IsUnderPostmaster &&
84 : TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit))
85 0 : ereport(ERROR,
86 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
87 : errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
88 : NameStr(ShmemVariableCache->limit_datname)),
89 : errhint("Stop the postmaster and use a standalone backend to vacuum database \"%s\".",
90 : NameStr(ShmemVariableCache->limit_datname))));
91 0 : else if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit))
92 0 : ereport(WARNING,
93 : (errmsg("database \"%s\" must be vacuumed within %u transactions",
94 : NameStr(ShmemVariableCache->limit_datname),
95 : ShmemVariableCache->xidWrapLimit - xid),
96 : errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",
97 : NameStr(ShmemVariableCache->limit_datname))));
98 : }
99 :
100 : /*
101 : * If we are allocating the first XID of a new page of the commit log,
102 : * zero out that commit-log page before returning. We must do this while
103 : * holding XidGenLock, else another xact could acquire and commit a later
104 : * XID before we zero the page. Fortunately, a page of the commit log
105 : * holds 32K or more transactions, so we don't have to do this very often.
106 : *
107 : * Extend pg_subtrans too.
108 : */
109 4430 : ExtendCLOG(xid);
110 4430 : ExtendSUBTRANS(xid);
111 :
112 : /*
113 : * Now advance the nextXid counter. This must not happen until after we
114 : * have successfully completed ExtendCLOG() --- if that routine fails, we
115 : * want the next incoming transaction to try it again. We cannot assign
116 : * more XIDs until there is CLOG space for them.
117 : */
118 4430 : TransactionIdAdvance(ShmemVariableCache->nextXid);
119 :
120 : /*
121 : * We must store the new XID into the shared ProcArray before releasing
122 : * XidGenLock. This ensures that every active XID older than
123 : * latestCompletedXid is present in the ProcArray, which is essential for
124 : * correct OldestXmin tracking; see src/backend/access/transam/README.
125 : *
126 : * XXX by storing xid into MyProc without acquiring ProcArrayLock, we are
127 : * relying on fetch/store of an xid to be atomic, else other backends
128 : * might see a partially-set xid here. But holding both locks at once
129 : * would be a nasty concurrency hit. So for now, assume atomicity.
130 : *
131 : * Note that readers of PGPROC xid fields should be careful to fetch the
132 : * value only once, rather than assume they can read a value multiple
133 : * times and get the same answer each time.
134 : *
135 : * The same comments apply to the subxact xid count and overflow fields.
136 : *
137 : * A solution to the atomic-store problem would be to give each PGPROC its
138 : * own spinlock used only for fetching/storing that PGPROC's xid and
139 : * related fields.
140 : *
141 : * If there's no room to fit a subtransaction XID into PGPROC, set the
142 : * cache-overflowed flag instead. This forces readers to look in
143 : * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There is a
144 : * race-condition window, in that the new XID will not appear as running
145 : * until its parent link has been placed into pg_subtrans. However, that
146 : * will happen before anyone could possibly have a reason to inquire about
147 : * the status of the XID, so it seems OK. (Snapshots taken during this
148 : * window *will* include the parent XID, so they will deliver the correct
149 : * answer later on when someone does have a reason to inquire.)
150 : */
151 : {
152 : /*
153 : * Use volatile pointer to prevent code rearrangement; other backends
154 : * could be examining my subxids info concurrently, and we don't want
155 : * them to see an invalid intermediate state, such as incrementing
156 : * nxids before filling the array entry. Note we are assuming that
157 : * TransactionId and int fetch/store are atomic.
158 : */
159 4430 : volatile PGPROC *myproc = MyProc;
160 :
161 4430 : if (!isSubXact)
162 4390 : myproc->xid = xid;
163 : else
164 : {
165 40 : int nxids = myproc->subxids.nxids;
166 :
167 40 : if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
168 : {
169 40 : myproc->subxids.xids[nxids] = xid;
170 40 : myproc->subxids.nxids = nxids + 1;
171 : }
172 : else
173 0 : myproc->subxids.overflowed = true;
174 : }
175 : }
176 :
177 4430 : LWLockRelease(XidGenLock);
178 :
179 4430 : return xid;
180 : }
181 :
182 : /*
183 : * Read nextXid but don't allocate it.
184 : */
185 : TransactionId
186 : ReadNewTransactionId(void)
187 347 : {
188 : TransactionId xid;
189 :
190 347 : LWLockAcquire(XidGenLock, LW_SHARED);
191 347 : xid = ShmemVariableCache->nextXid;
192 347 : LWLockRelease(XidGenLock);
193 :
194 347 : return xid;
195 : }
196 :
197 : /*
198 : * Determine the last safe XID to allocate given the currently oldest
199 : * datfrozenxid (ie, the oldest XID that might exist in any database
200 : * of our cluster).
201 : */
202 : void
203 : SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
204 : Name oldest_datname)
205 20 : {
206 : TransactionId xidVacLimit;
207 : TransactionId xidWarnLimit;
208 : TransactionId xidStopLimit;
209 : TransactionId xidWrapLimit;
210 : TransactionId curXid;
211 :
212 : Assert(TransactionIdIsNormal(oldest_datfrozenxid));
213 :
214 : /*
215 : * The place where we actually get into deep trouble is halfway around
216 : * from the oldest potentially-existing XID. (This calculation is
217 : * probably off by one or two counts, because the special XIDs reduce the
218 : * size of the loop a little bit. But we throw in plenty of slop below,
219 : * so it doesn't matter.)
220 : */
221 20 : xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
222 20 : if (xidWrapLimit < FirstNormalTransactionId)
223 0 : xidWrapLimit += FirstNormalTransactionId;
224 :
225 : /*
226 : * We'll refuse to continue assigning XIDs in interactive mode once we get
227 : * within 1M transactions of data loss. This leaves lots of room for the
228 : * DBA to fool around fixing things in a standalone backend, while not
229 : * being significant compared to total XID space. (Note that since
230 : * vacuuming requires one transaction per table cleaned, we had better be
231 : * sure there's lots of XIDs left...)
232 : */
233 20 : xidStopLimit = xidWrapLimit - 1000000;
234 20 : if (xidStopLimit < FirstNormalTransactionId)
235 0 : xidStopLimit -= FirstNormalTransactionId;
236 :
237 : /*
238 : * We'll start complaining loudly when we get within 10M transactions of
239 : * the stop point. This is kind of arbitrary, but if you let your gas
240 : * gauge get down to 1% of full, would you be looking for the next gas
241 : * station? We need to be fairly liberal about this number because there
242 : * are lots of scenarios where most transactions are done by automatic
243 : * clients that won't pay attention to warnings. (No, we're not gonna make
244 : * this configurable. If you know enough to configure it, you know enough
245 : * to not get in this kind of trouble in the first place.)
246 : */
247 20 : xidWarnLimit = xidStopLimit - 10000000;
248 20 : if (xidWarnLimit < FirstNormalTransactionId)
249 0 : xidWarnLimit -= FirstNormalTransactionId;
250 :
251 : /*
252 : * We'll start trying to force autovacuums when oldest_datfrozenxid gets
253 : * to be more than autovacuum_freeze_max_age transactions old.
254 : *
255 : * Note: guc.c ensures that autovacuum_freeze_max_age is in a sane range,
256 : * so that xidVacLimit will be well before xidWarnLimit.
257 : *
258 : * Note: autovacuum_freeze_max_age is a PGC_POSTMASTER parameter so that
259 : * we don't have to worry about dealing with on-the-fly changes in its
260 : * value. It doesn't look practical to update shared state from a GUC
261 : * assign hook (too many processes would try to execute the hook,
262 : * resulting in race conditions as well as crashes of those not connected
263 : * to shared memory). Perhaps this can be improved someday.
264 : */
265 20 : xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
266 20 : if (xidVacLimit < FirstNormalTransactionId)
267 0 : xidVacLimit += FirstNormalTransactionId;
268 :
269 : /* Grab lock for just long enough to set the new limit values */
270 20 : LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
271 20 : ShmemVariableCache->oldestXid = oldest_datfrozenxid;
272 20 : ShmemVariableCache->xidVacLimit = xidVacLimit;
273 20 : ShmemVariableCache->xidWarnLimit = xidWarnLimit;
274 20 : ShmemVariableCache->xidStopLimit = xidStopLimit;
275 20 : ShmemVariableCache->xidWrapLimit = xidWrapLimit;
276 20 : namecpy(&ShmemVariableCache->limit_datname, oldest_datname);
277 20 : curXid = ShmemVariableCache->nextXid;
278 20 : LWLockRelease(XidGenLock);
279 :
280 : /* Log the info */
281 20 : ereport(DEBUG1,
282 : (errmsg("transaction ID wrap limit is %u, limited by database \"%s\"",
283 : xidWrapLimit, NameStr(*oldest_datname))));
284 :
285 : /*
286 : * If past the autovacuum force point, immediately signal an autovac
287 : * request. The reason for this is that autovac only processes one
288 : * database per invocation. Once it's finished cleaning up the oldest
289 : * database, it'll call here, and we'll signal the postmaster to start
290 : * another iteration immediately if there are still any old databases.
291 : */
292 20 : if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
293 : IsUnderPostmaster)
294 0 : SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
295 :
296 : /* Give an immediate warning if past the wrap warn point */
297 20 : if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit))
298 0 : ereport(WARNING,
299 : (errmsg("database \"%s\" must be vacuumed within %u transactions",
300 : NameStr(*oldest_datname),
301 : xidWrapLimit - curXid),
302 : errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",
303 : NameStr(*oldest_datname))));
304 20 : }
305 :
306 :
307 : /*
308 : * GetNewObjectId -- allocate a new OID
309 : *
310 : * OIDs are generated by a cluster-wide counter. Since they are only 32 bits
311 : * wide, counter wraparound will occur eventually, and therefore it is unwise
312 : * to assume they are unique unless precautions are taken to make them so.
313 : * Hence, this routine should generally not be used directly. The only
314 : * direct callers should be GetNewOid() and GetNewRelFileNode() in
315 : * catalog/catalog.c.
316 : */
317 : Oid
318 : GetNewObjectId(void)
319 24891 : {
320 : Oid result;
321 :
322 24891 : LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
323 :
324 : /*
325 : * Check for wraparound of the OID counter. We *must* not return 0
326 : * (InvalidOid); and as long as we have to check that, it seems a good
327 : * idea to skip over everything below FirstNormalObjectId too. (This
328 : * basically just avoids lots of collisions with bootstrap-assigned OIDs
329 : * right after a wrap occurs, so as to avoid a possibly large number of
330 : * iterations in GetNewOid.) Note we are relying on unsigned comparison.
331 : *
332 : * During initdb, we start the OID generator at FirstBootstrapObjectId, so
333 : * we only enforce wrapping to that point when in bootstrap or standalone
334 : * mode. The first time through this routine after normal postmaster
335 : * start, the counter will be forced up to FirstNormalObjectId. This
336 : * mechanism leaves the OIDs between FirstBootstrapObjectId and
337 : * FirstNormalObjectId available for automatic assignment during initdb,
338 : * while ensuring they will never conflict with user-assigned OIDs.
339 : */
340 24891 : if (ShmemVariableCache->nextOid < ((Oid) FirstNormalObjectId))
341 : {
342 1513 : if (IsPostmasterEnvironment)
343 : {
344 : /* wraparound in normal environment */
345 1 : ShmemVariableCache->nextOid = FirstNormalObjectId;
346 1 : ShmemVariableCache->oidCount = 0;
347 : }
348 : else
349 : {
350 : /* we may be bootstrapping, so don't enforce the full range */
351 1512 : if (ShmemVariableCache->nextOid < ((Oid) FirstBootstrapObjectId))
352 : {
353 : /* wraparound in standalone environment? */
354 0 : ShmemVariableCache->nextOid = FirstBootstrapObjectId;
355 0 : ShmemVariableCache->oidCount = 0;
356 : }
357 : }
358 : }
359 :
360 : /* If we run out of logged for use oids then we must log more */
361 24891 : if (ShmemVariableCache->oidCount == 0)
362 : {
363 12 : XLogPutNextOid(ShmemVariableCache->nextOid + VAR_OID_PREFETCH);
364 12 : ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
365 : }
366 :
367 24891 : result = ShmemVariableCache->nextOid;
368 :
369 24891 : (ShmemVariableCache->nextOid)++;
370 24891 : (ShmemVariableCache->oidCount)--;
371 :
372 24891 : LWLockRelease(OidGenLock);
373 :
374 24891 : return result;
375 : }
|