1 : /*-------------------------------------------------------------------------
2 : *
3 : * subtrans.c
4 : * PostgreSQL subtransaction-log manager
5 : *
6 : * The pg_subtrans manager is a pg_clog-like manager that stores the parent
7 : * transaction Id for each transaction. It is a fundamental part of the
8 : * nested transactions implementation. A main transaction has a parent
9 : * of InvalidTransactionId, and each subtransaction has its immediate parent.
10 : * The tree can easily be walked from child to parent, but not in the
11 : * opposite direction.
12 : *
13 : * This code is based on clog.c, but the robustness requirements
14 : * are completely different from pg_clog, because we only need to remember
15 : * pg_subtrans information for currently-open transactions. Thus, there is
16 : * no need to preserve data over a crash and restart.
17 : *
18 : * There are no XLOG interactions since we do not care about preserving
19 : * data across crashes. During database startup, we simply force the
20 : * currently-active page of SUBTRANS to zeroes.
21 : *
22 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
23 : * Portions Copyright (c) 1994, Regents of the University of California
24 : *
25 : * $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.19 2007/08/01 22:45:07 tgl Exp $
26 : *
27 : *-------------------------------------------------------------------------
28 : */
29 : #include "postgres.h"
30 :
31 : #include "access/slru.h"
32 : #include "access/subtrans.h"
33 : #include "access/transam.h"
34 : #include "utils/tqual.h"
35 :
36 :
37 : /*
38 : * Defines for SubTrans page sizes. A page is the same BLCKSZ as is used
39 : * everywhere else in Postgres.
40 : *
41 : * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
42 : * SubTrans page numbering also wraps around at
43 : * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
44 : * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_SEGMENTS_PER_PAGE. We need take no
45 : * explicit notice of that fact in this module, except when comparing segment
46 : * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes).
47 : */
48 :
49 : /* We need four bytes per xact */
50 : #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
51 :
52 : #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
53 : #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
54 :
55 :
56 : /*
57 : * Link to shared-memory data structures for SUBTRANS control
58 : */
59 : static SlruCtlData SubTransCtlData;
60 :
61 : #define SubTransCtl (&SubTransCtlData)
62 :
63 :
64 : static int ZeroSUBTRANSPage(int pageno);
65 : static bool SubTransPagePrecedes(int page1, int page2);
66 :
67 :
68 : /*
69 : * Record the parent of a subtransaction in the subtrans log.
70 : */
71 : void
72 : SubTransSetParent(TransactionId xid, TransactionId parent)
73 40 : {
74 40 : int pageno = TransactionIdToPage(xid);
75 40 : int entryno = TransactionIdToEntry(xid);
76 : int slotno;
77 : TransactionId *ptr;
78 :
79 40 : LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
80 :
81 40 : slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
82 40 : ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
83 40 : ptr += entryno;
84 :
85 : /* Current state should be 0 */
86 : Assert(*ptr == InvalidTransactionId);
87 :
88 40 : *ptr = parent;
89 :
90 40 : SubTransCtl->shared->page_dirty[slotno] = true;
91 :
92 40 : LWLockRelease(SubtransControlLock);
93 40 : }
94 :
95 : /*
96 : * Interrogate the parent of a transaction in the subtrans log.
97 : */
98 : TransactionId
99 : SubTransGetParent(TransactionId xid)
100 0 : {
101 0 : int pageno = TransactionIdToPage(xid);
102 0 : int entryno = TransactionIdToEntry(xid);
103 : int slotno;
104 : TransactionId *ptr;
105 : TransactionId parent;
106 :
107 : /* Can't ask about stuff that might not be around anymore */
108 : Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
109 :
110 : /* Bootstrap and frozen XIDs have no parent */
111 0 : if (!TransactionIdIsNormal(xid))
112 0 : return InvalidTransactionId;
113 :
114 : /* lock is acquired by SimpleLruReadPage_ReadOnly */
115 :
116 0 : slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
117 0 : ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
118 0 : ptr += entryno;
119 :
120 0 : parent = *ptr;
121 :
122 0 : LWLockRelease(SubtransControlLock);
123 :
124 0 : return parent;
125 : }
126 :
127 : /*
128 : * SubTransGetTopmostTransaction
129 : *
130 : * Returns the topmost transaction of the given transaction id.
131 : *
132 : * Because we cannot look back further than TransactionXmin, it is possible
133 : * that this function will lie and return an intermediate subtransaction ID
134 : * instead of the true topmost parent ID. This is OK, because in practice
135 : * we only care about detecting whether the topmost parent is still running
136 : * or is part of a current snapshot's list of still-running transactions.
137 : * Therefore, any XID before TransactionXmin is as good as any other.
138 : */
139 : TransactionId
140 : SubTransGetTopmostTransaction(TransactionId xid)
141 0 : {
142 0 : TransactionId parentXid = xid,
143 0 : previousXid = xid;
144 :
145 : /* Can't ask about stuff that might not be around anymore */
146 : Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
147 :
148 0 : while (TransactionIdIsValid(parentXid))
149 : {
150 0 : previousXid = parentXid;
151 0 : if (TransactionIdPrecedes(parentXid, TransactionXmin))
152 0 : break;
153 0 : parentXid = SubTransGetParent(parentXid);
154 : }
155 :
156 : Assert(TransactionIdIsValid(previousXid));
157 :
158 0 : return previousXid;
159 : }
160 :
161 :
162 : /*
163 : * Initialization of shared memory for SUBTRANS
164 : */
165 : Size
166 : SUBTRANSShmemSize(void)
167 18 : {
168 18 : return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
169 : }
170 :
171 : void
172 : SUBTRANSShmemInit(void)
173 16 : {
174 16 : SubTransCtl->PagePrecedes = SubTransPagePrecedes;
175 16 : SimpleLruInit(SubTransCtl, "SUBTRANS Ctl", NUM_SUBTRANS_BUFFERS, 0,
176 : SubtransControlLock, "pg_subtrans");
177 : /* Override default assumption that writes should be fsync'd */
178 16 : SubTransCtl->do_fsync = false;
179 16 : }
180 :
181 : /*
182 : * This func must be called ONCE on system install. It creates
183 : * the initial SUBTRANS segment. (The SUBTRANS directory is assumed to
184 : * have been created by the initdb shell script, and SUBTRANSShmemInit
185 : * must have been called already.)
186 : *
187 : * Note: it's not really necessary to create the initial segment now,
188 : * since slru.c would create it on first write anyway. But we may as well
189 : * do it to be sure the directory is set up correctly.
190 : */
191 : void
192 : BootStrapSUBTRANS(void)
193 1 : {
194 : int slotno;
195 :
196 1 : LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
197 :
198 : /* Create and zero the first page of the subtrans log */
199 1 : slotno = ZeroSUBTRANSPage(0);
200 :
201 : /* Make sure it's written out */
202 1 : SimpleLruWritePage(SubTransCtl, slotno, NULL);
203 : Assert(!SubTransCtl->shared->page_dirty[slotno]);
204 :
205 1 : LWLockRelease(SubtransControlLock);
206 1 : }
207 :
208 : /*
209 : * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
210 : *
211 : * The page is not actually written, just set up in shared memory.
212 : * The slot number of the new page is returned.
213 : *
214 : * Control lock must be held at entry, and will be held at exit.
215 : */
216 : static int
217 : ZeroSUBTRANSPage(int pageno)
218 18 : {
219 18 : return SimpleLruZeroPage(SubTransCtl, pageno);
220 : }
221 :
222 : /*
223 : * This must be called ONCE during postmaster or standalone-backend startup,
224 : * after StartupXLOG has initialized ShmemVariableCache->nextXid.
225 : *
226 : * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
227 : * if there are none.
228 : */
229 : void
230 : StartupSUBTRANS(TransactionId oldestActiveXID)
231 14 : {
232 : int startPage;
233 : int endPage;
234 :
235 : /*
236 : * Since we don't expect pg_subtrans to be valid across crashes, we
237 : * initialize the currently-active page(s) to zeroes during startup.
238 : * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
239 : * the new page without regard to whatever was previously on disk.
240 : */
241 14 : LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
242 :
243 14 : startPage = TransactionIdToPage(oldestActiveXID);
244 14 : endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
245 :
246 28 : while (startPage != endPage)
247 : {
248 0 : (void) ZeroSUBTRANSPage(startPage);
249 0 : startPage++;
250 : }
251 14 : (void) ZeroSUBTRANSPage(startPage);
252 :
253 14 : LWLockRelease(SubtransControlLock);
254 14 : }
255 :
256 : /*
257 : * This must be called ONCE during postmaster or standalone-backend shutdown
258 : */
259 : void
260 : ShutdownSUBTRANS(void)
261 13 : {
262 : /*
263 : * Flush dirty SUBTRANS pages to disk
264 : *
265 : * This is not actually necessary from a correctness point of view. We do
266 : * it merely as a debugging aid.
267 : */
268 13 : SimpleLruFlush(SubTransCtl, false);
269 13 : }
270 :
271 : /*
272 : * Perform a checkpoint --- either during shutdown, or on-the-fly
273 : */
274 : void
275 : CheckPointSUBTRANS(void)
276 19 : {
277 : /*
278 : * Flush dirty SUBTRANS pages to disk
279 : *
280 : * This is not actually necessary from a correctness point of view. We do
281 : * it merely to improve the odds that writing of dirty pages is done by
282 : * the checkpoint process and not by backends.
283 : */
284 19 : SimpleLruFlush(SubTransCtl, true);
285 19 : }
286 :
287 :
288 : /*
289 : * Make sure that SUBTRANS has room for a newly-allocated XID.
290 : *
291 : * NB: this is called while holding XidGenLock. We want it to be very fast
292 : * most of the time; even when it's not so fast, no actual I/O need happen
293 : * unless we're forced to write out a dirty subtrans page to make room
294 : * in shared memory.
295 : */
296 : void
297 : ExtendSUBTRANS(TransactionId newestXact)
298 4430 : {
299 : int pageno;
300 :
301 : /*
302 : * No work except at first XID of a page. But beware: just after
303 : * wraparound, the first XID of page zero is FirstNormalTransactionId.
304 : */
305 4430 : if (TransactionIdToEntry(newestXact) != 0 &&
306 : !TransactionIdEquals(newestXact, FirstNormalTransactionId))
307 4427 : return;
308 :
309 3 : pageno = TransactionIdToPage(newestXact);
310 :
311 3 : LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
312 :
313 : /* Zero the page */
314 3 : ZeroSUBTRANSPage(pageno);
315 :
316 3 : LWLockRelease(SubtransControlLock);
317 : }
318 :
319 :
320 : /*
321 : * Remove all SUBTRANS segments before the one holding the passed transaction ID
322 : *
323 : * This is normally called during checkpoint, with oldestXact being the
324 : * oldest TransactionXmin of any running transaction.
325 : */
326 : void
327 : TruncateSUBTRANS(TransactionId oldestXact)
328 19 : {
329 : int cutoffPage;
330 :
331 : /*
332 : * The cutoff point is the start of the segment containing oldestXact. We
333 : * pass the *page* containing oldestXact to SimpleLruTruncate.
334 : */
335 19 : cutoffPage = TransactionIdToPage(oldestXact);
336 :
337 19 : SimpleLruTruncate(SubTransCtl, cutoffPage);
338 19 : }
339 :
340 :
341 : /*
342 : * Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
343 : *
344 : * We need to use comparison of TransactionIds here in order to do the right
345 : * thing with wraparound XID arithmetic. However, if we are asked about
346 : * page number zero, we don't want to hand InvalidTransactionId to
347 : * TransactionIdPrecedes: it'll get weird about permanent xact IDs. So,
348 : * offset both xids by FirstNormalTransactionId to avoid that.
349 : */
350 : static bool
351 : SubTransPagePrecedes(int page1, int page2)
352 63 : {
353 : TransactionId xid1;
354 : TransactionId xid2;
355 :
356 63 : xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
357 63 : xid1 += FirstNormalTransactionId;
358 63 : xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
359 63 : xid2 += FirstNormalTransactionId;
360 :
361 63 : return TransactionIdPrecedes(xid1, xid2);
362 : }
|