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