PostgreSQL Source Code  git master
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  * 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-2020, 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  */
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
75 {
76  int pageno = TransactionIdToPage(xid);
77  int entryno = TransactionIdToEntry(xid);
78  int slotno;
79  TransactionId *ptr;
80 
82  Assert(TransactionIdFollows(xid, parent));
83 
84  LWLockAcquire(SubtransSLRULock, 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  {
98  *ptr = parent;
99  SubTransCtl->shared->page_dirty[slotno] = true;
100  }
101 
102  LWLockRelease(SubtransSLRULock);
103 }
104 
105 /*
106  * Interrogate the parent of a transaction in the subtrans log.
107  */
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 */
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(SubtransSLRULock);
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  */
151 {
152  TransactionId parentXid = xid,
153  previousXid = xid;
154 
155  /* Can't ask about stuff that might not be around anymore */
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
186 {
188 }
189 
190 void
192 {
193  SubTransCtl->PagePrecedes = SubTransPagePrecedes;
195  SubtransSLRULock, "pg_subtrans",
197 }
198 
199 /*
200  * This func must be called ONCE on system install. It creates
201  * the initial SUBTRANS segment. (The SUBTRANS directory is assumed to
202  * have been created by the initdb shell script, and SUBTRANSShmemInit
203  * must have been called already.)
204  *
205  * Note: it's not really necessary to create the initial segment now,
206  * since slru.c would create it on first write anyway. But we may as well
207  * do it to be sure the directory is set up correctly.
208  */
209 void
211 {
212  int slotno;
213 
214  LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
215 
216  /* Create and zero the first page of the subtrans log */
217  slotno = ZeroSUBTRANSPage(0);
218 
219  /* Make sure it's written out */
221  Assert(!SubTransCtl->shared->page_dirty[slotno]);
222 
223  LWLockRelease(SubtransSLRULock);
224 }
225 
226 /*
227  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
228  *
229  * The page is not actually written, just set up in shared memory.
230  * The slot number of the new page is returned.
231  *
232  * Control lock must be held at entry, and will be held at exit.
233  */
234 static int
235 ZeroSUBTRANSPage(int pageno)
236 {
237  return SimpleLruZeroPage(SubTransCtl, pageno);
238 }
239 
240 /*
241  * This must be called ONCE during postmaster or standalone-backend startup,
242  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
243  *
244  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
245  * if there are none.
246  */
247 void
249 {
250  FullTransactionId nextXid;
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(SubtransSLRULock, LW_EXCLUSIVE);
261 
262  startPage = TransactionIdToPage(oldestActiveXID);
263  nextXid = ShmemVariableCache->nextXid;
264  endPage = TransactionIdToPage(XidFromFullTransactionId(nextXid));
265 
266  while (startPage != endPage)
267  {
268  (void) ZeroSUBTRANSPage(startPage);
269  startPage++;
270  /* must account for wraparound */
271  if (startPage > TransactionIdToPage(MaxTransactionId))
272  startPage = 0;
273  }
274  (void) ZeroSUBTRANSPage(startPage);
275 
276  LWLockRelease(SubtransSLRULock);
277 }
278 
279 /*
280  * Perform a checkpoint --- either during shutdown, or on-the-fly
281  */
282 void
284 {
285  /*
286  * Write 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);
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
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 &&
317  return;
318 
319  pageno = TransactionIdToPage(newestXact);
320 
321  LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
322 
323  /* Zero the page */
324  ZeroSUBTRANSPage(pageno);
325 
326  LWLockRelease(SubtransSLRULock);
327 }
328 
329 
330 /*
331  * Remove all SUBTRANS segments before the one holding the passed transaction ID
332  *
333  * oldestXact is the oldest TransactionXmin of any running transaction. This
334  * is called only during checkpoint.
335  */
336 void
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. We step
344  * back one transaction to avoid passing a cutoff page that hasn't been
345  * created yet in the rare case that oldestXact would be the first item on
346  * a page and oldestXact == next XID. In that case, if we didn't subtract
347  * one, we'd trigger SimpleLruTruncate's wraparound detection.
348  */
349  TransactionIdRetreat(oldestXact);
350  cutoffPage = TransactionIdToPage(oldestXact);
351 
352  SimpleLruTruncate(SubTransCtl, cutoffPage);
353 }
354 
355 
356 /*
357  * Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
358  *
359  * We need to use comparison of TransactionIds here in order to do the right
360  * thing with wraparound XID arithmetic. However, if we are asked about
361  * page number zero, we don't want to hand InvalidTransactionId to
362  * TransactionIdPrecedes: it'll get weird about permanent xact IDs. So,
363  * offset both xids by FirstNormalTransactionId to avoid that.
364  */
365 static bool
366 SubTransPagePrecedes(int page1, int page2)
367 {
368  TransactionId xid1;
369  TransactionId xid2;
370 
371  xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
372  xid1 += FirstNormalTransactionId;
373  xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
374  xid2 += FirstNormalTransactionId;
375 
376  return TransactionIdPrecedes(xid1, xid2);
377 }
static int ZeroSUBTRANSPage(int pageno)
Definition: subtrans.c:235
#define TransactionIdToEntry(xid)
Definition: subtrans.c:55
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:334
uint32 TransactionId
Definition: c.h:521
void SUBTRANSShmemInit(void)
Definition: subtrans.c:191
TransactionId SubTransGetTopmostTransaction(TransactionId xid)
Definition: subtrans.c:150
TransactionId SubTransGetParent(TransactionId xid)
Definition: subtrans.c:109
void SimpleLruTruncate(SlruCtl ctl, int cutoffPage)
Definition: slru.c:1225
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:349
void TruncateSUBTRANS(TransactionId oldestXact)
Definition: subtrans.c:337
#define TransactionIdRetreat(dest)
Definition: transam.h:141
FullTransactionId nextXid
Definition: transam.h:213
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition: slru.c:155
void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, LWLock *ctllock, const char *subdir, int tranche_id, SyncRequestHandler sync_handler)
Definition: slru.c:186
#define XidFromFullTransactionId(x)
Definition: transam.h:48
TransactionId TransactionXmin
Definition: snapmgr.c:112
#define SubTransCtl
Definition: subtrans.c:63
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1812
void BootStrapSUBTRANS(void)
Definition: subtrans.c:210
#define FirstNormalTransactionId
Definition: transam.h:34
#define ERROR
Definition: elog.h:43
void ExtendSUBTRANS(TransactionId newestXact)
Definition: subtrans.c:307
int SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, TransactionId xid)
Definition: slru.c:394
VariableCache ShmemVariableCache
Definition: varsup.c:34
void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
Definition: slru.c:1155
#define InvalidTransactionId
Definition: transam.h:31
void SimpleLruWritePage(SlruCtl ctl, int slotno)
Definition: slru.c:613
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:366
#define TransactionIdToPage(xid)
Definition: subtrans.c:54
int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
Definition: slru.c:494
#define NUM_SUBTRANS_BUFFERS
Definition: subtrans.h:15
void StartupSUBTRANS(TransactionId oldestActiveXID)
Definition: subtrans.c:248
#define Assert(condition)
Definition: c.h:746
size_t Size
Definition: c.h:474
void CheckPointSUBTRANS(void)
Definition: subtrans.c:283
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1208
Size SUBTRANSShmemSize(void)
Definition: subtrans.c:185
#define SUBTRANS_XACTS_PER_PAGE
Definition: subtrans.c:52
#define elog(elevel,...)
Definition: elog.h:214
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
void SubTransSetParent(TransactionId xid, TransactionId parent)
Definition: subtrans.c:74
int SimpleLruZeroPage(SlruCtl ctl, int pageno)
Definition: slru.c:279
static SlruCtlData SubTransCtlData
Definition: subtrans.c:61