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