]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/subtrans.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / src / backend / access / transam / subtrans.c
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 }