]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/subtrans.c
Convert the arithmetic for shared memory size calculation from 'int'
[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-2005, 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.10 2005/08/20 23:26:08 tgl Exp $
26  *
27  *-------------------------------------------------------------------------
28  */
29 #include "postgres.h"
30
31 #include "access/slru.h"
32 #include "access/subtrans.h"
33 #include "utils/tqual.h"
34
35
36 /*
37  * Defines for SubTrans page sizes.  A page is the same BLCKSZ as is used
38  * everywhere else in Postgres.
39  *
40  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
41  * SubTrans page numbering also wraps around at
42  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
43  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_SEGMENTS_PER_PAGE.  We need take no
44  * explicit notice of that fact in this module, except when comparing segment
45  * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes).
46  */
47
48 /* We need four bytes per xact */
49 #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
50
51 #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
52 #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
53
54
55 /*
56  * Link to shared-memory data structures for SUBTRANS control
57  */
58 static SlruCtlData SubTransCtlData;
59
60 #define SubTransCtl  (&SubTransCtlData)
61
62
63 static int      ZeroSUBTRANSPage(int pageno);
64 static bool SubTransPagePrecedes(int page1, int page2);
65
66
67 /*
68  * Record the parent of a subtransaction in the subtrans log.
69  */
70 void
71 SubTransSetParent(TransactionId xid, TransactionId parent)
72 {
73         int                     pageno = TransactionIdToPage(xid);
74         int                     entryno = TransactionIdToEntry(xid);
75         int                     slotno;
76         TransactionId *ptr;
77
78         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
79
80         slotno = SimpleLruReadPage(SubTransCtl, pageno, xid);
81         ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
82         ptr += entryno;
83
84         /* Current state should be 0 */
85         Assert(*ptr == InvalidTransactionId);
86
87         *ptr = parent;
88
89         SubTransCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
90
91         LWLockRelease(SubtransControlLock);
92 }
93
94 /*
95  * Interrogate the parent of a transaction in the subtrans log.
96  */
97 TransactionId
98 SubTransGetParent(TransactionId xid)
99 {
100         int                     pageno = TransactionIdToPage(xid);
101         int                     entryno = TransactionIdToEntry(xid);
102         int                     slotno;
103         TransactionId *ptr;
104         TransactionId parent;
105
106         /* Can't ask about stuff that might not be around anymore */
107         Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
108
109         /* Bootstrap and frozen XIDs have no parent */
110         if (!TransactionIdIsNormal(xid))
111                 return InvalidTransactionId;
112
113         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
114
115         slotno = SimpleLruReadPage(SubTransCtl, pageno, xid);
116         ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
117         ptr += entryno;
118
119         parent = *ptr;
120
121         LWLockRelease(SubtransControlLock);
122
123         return parent;
124 }
125
126 /*
127  * SubTransGetTopmostTransaction
128  *
129  * Returns the topmost transaction of the given transaction id.
130  *
131  * Because we cannot look back further than TransactionXmin, it is possible
132  * that this function will lie and return an intermediate subtransaction ID
133  * instead of the true topmost parent ID.  This is OK, because in practice
134  * we only care about detecting whether the topmost parent is still running
135  * or is part of a current snapshot's list of still-running transactions.
136  * Therefore, any XID before TransactionXmin is as good as any other.
137  */
138 TransactionId
139 SubTransGetTopmostTransaction(TransactionId xid)
140 {
141         TransactionId parentXid = xid,
142                                 previousXid = xid;
143
144         /* Can't ask about stuff that might not be around anymore */
145         Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
146
147         while (TransactionIdIsValid(parentXid))
148         {
149                 previousXid = parentXid;
150                 if (TransactionIdPrecedes(parentXid, TransactionXmin))
151                         break;
152                 parentXid = SubTransGetParent(parentXid);
153         }
154
155         Assert(TransactionIdIsValid(previousXid));
156
157         return previousXid;
158 }
159
160
161 /*
162  * Initialization of shared memory for SUBTRANS
163  */
164 Size
165 SUBTRANSShmemSize(void)
166 {
167         return SimpleLruShmemSize();
168 }
169
170 void
171 SUBTRANSShmemInit(void)
172 {
173         SubTransCtl->PagePrecedes = SubTransPagePrecedes;
174         SimpleLruInit(SubTransCtl, "SUBTRANS Ctl",
175                                   SubtransControlLock, "pg_subtrans");
176         /* Override default assumption that writes should be fsync'd */
177         SubTransCtl->do_fsync = false;
178 }
179
180 /*
181  * This func must be called ONCE on system install.  It creates
182  * the initial SUBTRANS segment.  (The SUBTRANS directory is assumed to
183  * have been created by the initdb shell script, and SUBTRANSShmemInit
184  * must have been called already.)
185  *
186  * Note: it's not really necessary to create the initial segment now,
187  * since slru.c would create it on first write anyway.  But we may as well
188  * do it to be sure the directory is set up correctly.
189  */
190 void
191 BootStrapSUBTRANS(void)
192 {
193         int                     slotno;
194
195         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
196
197         /* Create and zero the first page of the subtrans log */
198         slotno = ZeroSUBTRANSPage(0);
199
200         /* Make sure it's written out */
201         SimpleLruWritePage(SubTransCtl, slotno, NULL);
202         Assert(SubTransCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
203
204         LWLockRelease(SubtransControlLock);
205 }
206
207 /*
208  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
209  *
210  * The page is not actually written, just set up in shared memory.
211  * The slot number of the new page is returned.
212  *
213  * Control lock must be held at entry, and will be held at exit.
214  */
215 static int
216 ZeroSUBTRANSPage(int pageno)
217 {
218         return SimpleLruZeroPage(SubTransCtl, pageno);
219 }
220
221 /*
222  * This must be called ONCE during postmaster or standalone-backend startup,
223  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
224  *
225  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
226  * if there are none.
227  */
228 void
229 StartupSUBTRANS(TransactionId oldestActiveXID)
230 {
231         int                     startPage;
232         int                     endPage;
233
234         /*
235          * Since we don't expect pg_subtrans to be valid across crashes, we
236          * initialize the currently-active page(s) to zeroes during startup.
237          * Whenever we advance into a new page, ExtendSUBTRANS will likewise
238          * zero the new page without regard to whatever was previously on
239          * disk.
240          */
241         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
242
243         startPage = TransactionIdToPage(oldestActiveXID);
244         endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
245
246         while (startPage != endPage)
247         {
248                 (void) ZeroSUBTRANSPage(startPage);
249                 startPage++;
250         }
251         (void) ZeroSUBTRANSPage(startPage);
252
253         LWLockRelease(SubtransControlLock);
254 }
255
256 /*
257  * This must be called ONCE during postmaster or standalone-backend shutdown
258  */
259 void
260 ShutdownSUBTRANS(void)
261 {
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         SimpleLruFlush(SubTransCtl, false);
269 }
270
271 /*
272  * Perform a checkpoint --- either during shutdown, or on-the-fly
273  */
274 void
275 CheckPointSUBTRANS(void)
276 {
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
282          * by the checkpoint process and not by backends.
283          */
284         SimpleLruFlush(SubTransCtl, true);
285 }
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 {
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         if (TransactionIdToEntry(newestXact) != 0 &&
306                 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
307                 return;
308
309         pageno = TransactionIdToPage(newestXact);
310
311         LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
312
313         /* Zero the page */
314         ZeroSUBTRANSPage(pageno);
315
316         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 {
329         int                     cutoffPage;
330
331         /*
332          * The cutoff point is the start of the segment containing oldestXact.
333          * We pass the *page* containing oldestXact to SimpleLruTruncate.
334          */
335         cutoffPage = TransactionIdToPage(oldestXact);
336
337         SimpleLruTruncate(SubTransCtl, cutoffPage);
338 }
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 {
353         TransactionId xid1;
354         TransactionId xid2;
355
356         xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
357         xid1 += FirstNormalTransactionId;
358         xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
359         xid2 += FirstNormalTransactionId;
360
361         return TransactionIdPrecedes(xid1, xid2);
362 }