]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/subtrans.c
pgindent run for 9.4
[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_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-2014, 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_SEGMENTS_PER_PAGE.  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).
48  */
49
50 /* We need four bytes per xact */
51 #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
52
53 #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
54 #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
55
56
57 /*
58  * Link to shared-memory data structures for SUBTRANS control
59  */
60 static SlruCtlData SubTransCtlData;
61
62 #define SubTransCtl  (&SubTransCtlData)
63
64
65 static int      ZeroSUBTRANSPage(int pageno);
66 static bool SubTransPagePrecedes(int page1, int page2);
67
68
69 /*
70  * Record the parent of a subtransaction in the subtrans log.
71  *
72  * In some cases we may need to overwrite an existing value.
73  */
74 void
75 SubTransSetParent(TransactionId xid, TransactionId parent, bool overwriteOK)
76 {
77         int                     pageno = TransactionIdToPage(xid);
78         int                     entryno = TransactionIdToEntry(xid);
79         int                     slotno;
80         TransactionId *ptr;
81
82         Assert(TransactionIdIsValid(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         /* Current state should be 0 */
91         Assert(*ptr == InvalidTransactionId ||
92                    (*ptr == parent && overwriteOK));
93
94         *ptr = parent;
95
96         SubTransCtl->shared->page_dirty[slotno] = true;
97
98         LWLockRelease(SubtransControlLock);
99 }
100
101 /*
102  * Interrogate the parent of a transaction in the subtrans log.
103  */
104 TransactionId
105 SubTransGetParent(TransactionId xid)
106 {
107         int                     pageno = TransactionIdToPage(xid);
108         int                     entryno = TransactionIdToEntry(xid);
109         int                     slotno;
110         TransactionId *ptr;
111         TransactionId parent;
112
113         /* Can't ask about stuff that might not be around anymore */
114         Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
115
116         /* Bootstrap and frozen XIDs have no parent */
117         if (!TransactionIdIsNormal(xid))
118                 return InvalidTransactionId;
119
120         /* lock is acquired by SimpleLruReadPage_ReadOnly */
121
122         slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
123         ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
124         ptr += entryno;
125
126         parent = *ptr;
127
128         LWLockRelease(SubtransControlLock);
129
130         return parent;
131 }
132
133 /*
134  * SubTransGetTopmostTransaction
135  *
136  * Returns the topmost transaction of the given transaction id.
137  *
138  * Because we cannot look back further than TransactionXmin, it is possible
139  * that this function will lie and return an intermediate subtransaction ID
140  * instead of the true topmost parent ID.  This is OK, because in practice
141  * we only care about detecting whether the topmost parent is still running
142  * or is part of a current snapshot's list of still-running transactions.
143  * Therefore, any XID before TransactionXmin is as good as any other.
144  */
145 TransactionId
146 SubTransGetTopmostTransaction(TransactionId xid)
147 {
148         TransactionId parentXid = xid,
149                                 previousXid = xid;
150
151         /* Can't ask about stuff that might not be around anymore */
152         Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
153
154         while (TransactionIdIsValid(parentXid))
155         {
156                 previousXid = parentXid;
157                 if (TransactionIdPrecedes(parentXid, TransactionXmin))
158                         break;
159                 parentXid = SubTransGetParent(parentXid);
160         }
161
162         Assert(TransactionIdIsValid(previousXid));
163
164         return previousXid;
165 }
166
167
168 /*
169  * Initialization of shared memory for SUBTRANS
170  */
171 Size
172 SUBTRANSShmemSize(void)
173 {
174         return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
175 }
176
177 void
178 SUBTRANSShmemInit(void)
179 {
180         SubTransCtl->PagePrecedes = SubTransPagePrecedes;
181         SimpleLruInit(SubTransCtl, "SUBTRANS Ctl", NUM_SUBTRANS_BUFFERS, 0,
182                                   SubtransControlLock, "pg_subtrans");
183         /* Override default assumption that writes should be fsync'd */
184         SubTransCtl->do_fsync = false;
185 }
186
187 /*
188  * This func must be called ONCE on system install.  It creates
189  * the initial SUBTRANS segment.  (The SUBTRANS directory is assumed to
190  * have been created by the initdb shell script, and SUBTRANSShmemInit
191  * must have been called already.)
192  *
193  * Note: it's not really necessary to create the initial segment now,
194  * since slru.c would create it on first write anyway.  But we may as well
195  * do it to be sure the directory is set up correctly.
196  */
197 void
198 BootStrapSUBTRANS(void)
199 {
200         int                     slotno;
201
202         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
203
204         /* Create and zero the first page of the subtrans log */
205         slotno = ZeroSUBTRANSPage(0);
206
207         /* Make sure it's written out */
208         SimpleLruWritePage(SubTransCtl, slotno);
209         Assert(!SubTransCtl->shared->page_dirty[slotno]);
210
211         LWLockRelease(SubtransControlLock);
212 }
213
214 /*
215  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
216  *
217  * The page is not actually written, just set up in shared memory.
218  * The slot number of the new page is returned.
219  *
220  * Control lock must be held at entry, and will be held at exit.
221  */
222 static int
223 ZeroSUBTRANSPage(int pageno)
224 {
225         return SimpleLruZeroPage(SubTransCtl, pageno);
226 }
227
228 /*
229  * This must be called ONCE during postmaster or standalone-backend startup,
230  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
231  *
232  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
233  * if there are none.
234  */
235 void
236 StartupSUBTRANS(TransactionId oldestActiveXID)
237 {
238         int                     startPage;
239         int                     endPage;
240
241         /*
242          * Since we don't expect pg_subtrans to be valid across crashes, we
243          * initialize the currently-active page(s) to zeroes during startup.
244          * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
245          * the new page without regard to whatever was previously on disk.
246          */
247         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
248
249         startPage = TransactionIdToPage(oldestActiveXID);
250         endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
251
252         while (startPage != endPage)
253         {
254                 (void) ZeroSUBTRANSPage(startPage);
255                 startPage++;
256         }
257         (void) ZeroSUBTRANSPage(startPage);
258
259         LWLockRelease(SubtransControlLock);
260 }
261
262 /*
263  * This must be called ONCE during postmaster or standalone-backend shutdown
264  */
265 void
266 ShutdownSUBTRANS(void)
267 {
268         /*
269          * Flush dirty SUBTRANS pages to disk
270          *
271          * This is not actually necessary from a correctness point of view. We do
272          * it merely as a debugging aid.
273          */
274         TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(false);
275         SimpleLruFlush(SubTransCtl, false);
276         TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(false);
277 }
278
279 /*
280  * Perform a checkpoint --- either during shutdown, or on-the-fly
281  */
282 void
283 CheckPointSUBTRANS(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 to improve the odds that writing of dirty pages is done by
290          * the checkpoint process and not by backends.
291          */
292         TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
293         SimpleLruFlush(SubTransCtl, true);
294         TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
295 }
296
297
298 /*
299  * Make sure that SUBTRANS has room for a newly-allocated XID.
300  *
301  * NB: this is called while holding XidGenLock.  We want it to be very fast
302  * most of the time; even when it's not so fast, no actual I/O need happen
303  * unless we're forced to write out a dirty subtrans page to make room
304  * in shared memory.
305  */
306 void
307 ExtendSUBTRANS(TransactionId newestXact)
308 {
309         int                     pageno;
310
311         /*
312          * No work except at first XID of a page.  But beware: just after
313          * wraparound, the first XID of page zero is FirstNormalTransactionId.
314          */
315         if (TransactionIdToEntry(newestXact) != 0 &&
316                 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
317                 return;
318
319         pageno = TransactionIdToPage(newestXact);
320
321         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
322
323         /* Zero the page */
324         ZeroSUBTRANSPage(pageno);
325
326         LWLockRelease(SubtransControlLock);
327 }
328
329
330 /*
331  * Remove all SUBTRANS segments before the one holding the passed transaction ID
332  *
333  * This is normally called during checkpoint, with oldestXact being the
334  * oldest TransactionXmin of any running transaction.
335  */
336 void
337 TruncateSUBTRANS(TransactionId oldestXact)
338 {
339         int                     cutoffPage;
340
341         /*
342          * The cutoff point is the start of the segment containing oldestXact. We
343          * pass the *page* containing oldestXact to SimpleLruTruncate.
344          */
345         cutoffPage = TransactionIdToPage(oldestXact);
346
347         SimpleLruTruncate(SubTransCtl, cutoffPage);
348 }
349
350
351 /*
352  * Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
353  *
354  * We need to use comparison of TransactionIds here in order to do the right
355  * thing with wraparound XID arithmetic.  However, if we are asked about
356  * page number zero, we don't want to hand InvalidTransactionId to
357  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
358  * offset both xids by FirstNormalTransactionId to avoid that.
359  */
360 static bool
361 SubTransPagePrecedes(int page1, int page2)
362 {
363         TransactionId xid1;
364         TransactionId xid2;
365
366         xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
367         xid1 += FirstNormalTransactionId;
368         xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
369         xid2 += FirstNormalTransactionId;
370
371         return TransactionIdPrecedes(xid1, xid2);
372 }