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-2019, 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 | SubTransSetParent(TransactionId xid, TransactionId parent) |
75 | { |
76 | int pageno = TransactionIdToPage(xid); |
77 | int entryno = TransactionIdToEntry(xid); |
78 | int slotno; |
79 | TransactionId *ptr; |
80 | |
81 | Assert(TransactionIdIsValid(parent)); |
82 | Assert(TransactionIdFollows(xid, parent)); |
83 | |
84 | LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); |
85 | |
86 | slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid); |
87 | ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno]; |
88 | 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 | if (*ptr != parent) |
96 | { |
97 | Assert(*ptr == InvalidTransactionId); |
98 | *ptr = parent; |
99 | SubTransCtl->shared->page_dirty[slotno] = true; |
100 | } |
101 | |
102 | LWLockRelease(SubtransControlLock); |
103 | } |
104 | |
105 | /* |
106 | * Interrogate the parent of a transaction in the subtrans log. |
107 | */ |
108 | TransactionId |
109 | SubTransGetParent(TransactionId xid) |
110 | { |
111 | int pageno = TransactionIdToPage(xid); |
112 | 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 | Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin)); |
119 | |
120 | /* Bootstrap and frozen XIDs have no parent */ |
121 | if (!TransactionIdIsNormal(xid)) |
122 | return InvalidTransactionId; |
123 | |
124 | /* lock is acquired by SimpleLruReadPage_ReadOnly */ |
125 | |
126 | slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid); |
127 | ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno]; |
128 | ptr += entryno; |
129 | |
130 | parent = *ptr; |
131 | |
132 | LWLockRelease(SubtransControlLock); |
133 | |
134 | 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 | SubTransGetTopmostTransaction(TransactionId xid) |
151 | { |
152 | TransactionId parentXid = xid, |
153 | previousXid = xid; |
154 | |
155 | /* Can't ask about stuff that might not be around anymore */ |
156 | Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin)); |
157 | |
158 | while (TransactionIdIsValid(parentXid)) |
159 | { |
160 | previousXid = parentXid; |
161 | if (TransactionIdPrecedes(parentXid, TransactionXmin)) |
162 | break; |
163 | 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 | if (!TransactionIdPrecedes(parentXid, previousXid)) |
171 | elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u" , |
172 | previousXid, parentXid); |
173 | } |
174 | |
175 | Assert(TransactionIdIsValid(previousXid)); |
176 | |
177 | return previousXid; |
178 | } |
179 | |
180 | |
181 | /* |
182 | * Initialization of shared memory for SUBTRANS |
183 | */ |
184 | Size |
185 | SUBTRANSShmemSize(void) |
186 | { |
187 | return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0); |
188 | } |
189 | |
190 | void |
191 | SUBTRANSShmemInit(void) |
192 | { |
193 | SubTransCtl->PagePrecedes = SubTransPagePrecedes; |
194 | SimpleLruInit(SubTransCtl, "subtrans" , NUM_SUBTRANS_BUFFERS, 0, |
195 | SubtransControlLock, "pg_subtrans" , |
196 | LWTRANCHE_SUBTRANS_BUFFERS); |
197 | /* Override default assumption that writes should be fsync'd */ |
198 | SubTransCtl->do_fsync = false; |
199 | } |
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 | BootStrapSUBTRANS(void) |
213 | { |
214 | int slotno; |
215 | |
216 | LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); |
217 | |
218 | /* Create and zero the first page of the subtrans log */ |
219 | slotno = ZeroSUBTRANSPage(0); |
220 | |
221 | /* Make sure it's written out */ |
222 | SimpleLruWritePage(SubTransCtl, slotno); |
223 | Assert(!SubTransCtl->shared->page_dirty[slotno]); |
224 | |
225 | LWLockRelease(SubtransControlLock); |
226 | } |
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 | ZeroSUBTRANSPage(int pageno) |
238 | { |
239 | 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->nextFullXid. |
245 | * |
246 | * oldestActiveXID is the oldest XID of any prepared transaction, or nextFullXid |
247 | * if there are none. |
248 | */ |
249 | void |
250 | StartupSUBTRANS(TransactionId oldestActiveXID) |
251 | { |
252 | FullTransactionId nextFullXid; |
253 | int startPage; |
254 | int endPage; |
255 | |
256 | /* |
257 | * Since we don't expect pg_subtrans to be valid across crashes, we |
258 | * initialize the currently-active page(s) to zeroes during startup. |
259 | * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero |
260 | * the new page without regard to whatever was previously on disk. |
261 | */ |
262 | LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); |
263 | |
264 | startPage = TransactionIdToPage(oldestActiveXID); |
265 | nextFullXid = ShmemVariableCache->nextFullXid; |
266 | endPage = TransactionIdToPage(XidFromFullTransactionId(nextFullXid)); |
267 | |
268 | while (startPage != endPage) |
269 | { |
270 | (void) ZeroSUBTRANSPage(startPage); |
271 | startPage++; |
272 | /* must account for wraparound */ |
273 | if (startPage > TransactionIdToPage(MaxTransactionId)) |
274 | startPage = 0; |
275 | } |
276 | (void) ZeroSUBTRANSPage(startPage); |
277 | |
278 | LWLockRelease(SubtransControlLock); |
279 | } |
280 | |
281 | /* |
282 | * This must be called ONCE during postmaster or standalone-backend shutdown |
283 | */ |
284 | void |
285 | ShutdownSUBTRANS(void) |
286 | { |
287 | /* |
288 | * Flush dirty SUBTRANS pages to disk |
289 | * |
290 | * This is not actually necessary from a correctness point of view. We do |
291 | * it merely as a debugging aid. |
292 | */ |
293 | TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(false); |
294 | SimpleLruFlush(SubTransCtl, false); |
295 | TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(false); |
296 | } |
297 | |
298 | /* |
299 | * Perform a checkpoint --- either during shutdown, or on-the-fly |
300 | */ |
301 | void |
302 | CheckPointSUBTRANS(void) |
303 | { |
304 | /* |
305 | * Flush dirty SUBTRANS pages to disk |
306 | * |
307 | * This is not actually necessary from a correctness point of view. We do |
308 | * it merely to improve the odds that writing of dirty pages is done by |
309 | * the checkpoint process and not by backends. |
310 | */ |
311 | TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true); |
312 | SimpleLruFlush(SubTransCtl, true); |
313 | TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true); |
314 | } |
315 | |
316 | |
317 | /* |
318 | * Make sure that SUBTRANS has room for a newly-allocated XID. |
319 | * |
320 | * NB: this is called while holding XidGenLock. We want it to be very fast |
321 | * most of the time; even when it's not so fast, no actual I/O need happen |
322 | * unless we're forced to write out a dirty subtrans page to make room |
323 | * in shared memory. |
324 | */ |
325 | void |
326 | ExtendSUBTRANS(TransactionId newestXact) |
327 | { |
328 | int pageno; |
329 | |
330 | /* |
331 | * No work except at first XID of a page. But beware: just after |
332 | * wraparound, the first XID of page zero is FirstNormalTransactionId. |
333 | */ |
334 | if (TransactionIdToEntry(newestXact) != 0 && |
335 | !TransactionIdEquals(newestXact, FirstNormalTransactionId)) |
336 | return; |
337 | |
338 | pageno = TransactionIdToPage(newestXact); |
339 | |
340 | LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); |
341 | |
342 | /* Zero the page */ |
343 | ZeroSUBTRANSPage(pageno); |
344 | |
345 | LWLockRelease(SubtransControlLock); |
346 | } |
347 | |
348 | |
349 | /* |
350 | * Remove all SUBTRANS segments before the one holding the passed transaction ID |
351 | * |
352 | * This is normally called during checkpoint, with oldestXact being the |
353 | * oldest TransactionXmin of any running transaction. |
354 | */ |
355 | void |
356 | TruncateSUBTRANS(TransactionId oldestXact) |
357 | { |
358 | int cutoffPage; |
359 | |
360 | /* |
361 | * The cutoff point is the start of the segment containing oldestXact. We |
362 | * pass the *page* containing oldestXact to SimpleLruTruncate. We step |
363 | * back one transaction to avoid passing a cutoff page that hasn't been |
364 | * created yet in the rare case that oldestXact would be the first item on |
365 | * a page and oldestXact == next XID. In that case, if we didn't subtract |
366 | * one, we'd trigger SimpleLruTruncate's wraparound detection. |
367 | */ |
368 | TransactionIdRetreat(oldestXact); |
369 | cutoffPage = TransactionIdToPage(oldestXact); |
370 | |
371 | SimpleLruTruncate(SubTransCtl, cutoffPage); |
372 | } |
373 | |
374 | |
375 | /* |
376 | * Decide which of two SUBTRANS page numbers is "older" for truncation purposes. |
377 | * |
378 | * We need to use comparison of TransactionIds here in order to do the right |
379 | * thing with wraparound XID arithmetic. However, if we are asked about |
380 | * page number zero, we don't want to hand InvalidTransactionId to |
381 | * TransactionIdPrecedes: it'll get weird about permanent xact IDs. So, |
382 | * offset both xids by FirstNormalTransactionId to avoid that. |
383 | */ |
384 | static bool |
385 | SubTransPagePrecedes(int page1, int page2) |
386 | { |
387 | TransactionId xid1; |
388 | TransactionId xid2; |
389 | |
390 | xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE; |
391 | xid1 += FirstNormalTransactionId; |
392 | xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE; |
393 | xid2 += FirstNormalTransactionId; |
394 | |
395 | return TransactionIdPrecedes(xid1, xid2); |
396 | } |
397 | |