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-2024, 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 /*
55  * Although we return an int64 the actual value can't currently exceed
56  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE.
57  */
58 static inline int64
60 {
61  return xid / (int64) SUBTRANS_XACTS_PER_PAGE;
62 }
63 
64 #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
65 
66 
67 /*
68  * Link to shared-memory data structures for SUBTRANS control
69  */
71 
72 #define SubTransCtl (&SubTransCtlData)
73 
74 
75 static int ZeroSUBTRANSPage(int64 pageno);
76 static bool SubTransPagePrecedes(int64 page1, int64 page2);
77 
78 
79 /*
80  * Record the parent of a subtransaction in the subtrans log.
81  */
82 void
84 {
85  int64 pageno = TransactionIdToPage(xid);
86  int entryno = TransactionIdToEntry(xid);
87  int slotno;
88  TransactionId *ptr;
89 
91  Assert(TransactionIdFollows(xid, parent));
92 
93  LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
94 
95  slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
96  ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
97  ptr += entryno;
98 
99  /*
100  * It's possible we'll try to set the parent xid multiple times but we
101  * shouldn't ever be changing the xid from one valid xid to another valid
102  * xid, which would corrupt the data structure.
103  */
104  if (*ptr != parent)
105  {
106  Assert(*ptr == InvalidTransactionId);
107  *ptr = parent;
108  SubTransCtl->shared->page_dirty[slotno] = true;
109  }
110 
111  LWLockRelease(SubtransSLRULock);
112 }
113 
114 /*
115  * Interrogate the parent of a transaction in the subtrans log.
116  */
119 {
120  int64 pageno = TransactionIdToPage(xid);
121  int entryno = TransactionIdToEntry(xid);
122  int slotno;
123  TransactionId *ptr;
124  TransactionId parent;
125 
126  /* Can't ask about stuff that might not be around anymore */
128 
129  /* Bootstrap and frozen XIDs have no parent */
130  if (!TransactionIdIsNormal(xid))
131  return InvalidTransactionId;
132 
133  /* lock is acquired by SimpleLruReadPage_ReadOnly */
134 
135  slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
136  ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
137  ptr += entryno;
138 
139  parent = *ptr;
140 
141  LWLockRelease(SubtransSLRULock);
142 
143  return parent;
144 }
145 
146 /*
147  * SubTransGetTopmostTransaction
148  *
149  * Returns the topmost transaction of the given transaction id.
150  *
151  * Because we cannot look back further than TransactionXmin, it is possible
152  * that this function will lie and return an intermediate subtransaction ID
153  * instead of the true topmost parent ID. This is OK, because in practice
154  * we only care about detecting whether the topmost parent is still running
155  * or is part of a current snapshot's list of still-running transactions.
156  * Therefore, any XID before TransactionXmin is as good as any other.
157  */
160 {
161  TransactionId parentXid = xid,
162  previousXid = xid;
163 
164  /* Can't ask about stuff that might not be around anymore */
166 
167  while (TransactionIdIsValid(parentXid))
168  {
169  previousXid = parentXid;
170  if (TransactionIdPrecedes(parentXid, TransactionXmin))
171  break;
172  parentXid = SubTransGetParent(parentXid);
173 
174  /*
175  * By convention the parent xid gets allocated first, so should always
176  * precede the child xid. Anything else points to a corrupted data
177  * structure that could lead to an infinite loop, so exit.
178  */
179  if (!TransactionIdPrecedes(parentXid, previousXid))
180  elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
181  previousXid, parentXid);
182  }
183 
184  Assert(TransactionIdIsValid(previousXid));
185 
186  return previousXid;
187 }
188 
189 
190 /*
191  * Initialization of shared memory for SUBTRANS
192  */
193 Size
195 {
197 }
198 
199 void
201 {
202  SubTransCtl->PagePrecedes = SubTransPagePrecedes;
204  SubtransSLRULock, "pg_subtrans",
206  false);
208 }
209 
210 /*
211  * This func must be called ONCE on system install. It creates
212  * the initial SUBTRANS segment. (The SUBTRANS directory is assumed to
213  * have been created by the initdb shell script, and SUBTRANSShmemInit
214  * must have been called already.)
215  *
216  * Note: it's not really necessary to create the initial segment now,
217  * since slru.c would create it on first write anyway. But we may as well
218  * do it to be sure the directory is set up correctly.
219  */
220 void
222 {
223  int slotno;
224 
225  LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
226 
227  /* Create and zero the first page of the subtrans log */
228  slotno = ZeroSUBTRANSPage(0);
229 
230  /* Make sure it's written out */
232  Assert(!SubTransCtl->shared->page_dirty[slotno]);
233 
234  LWLockRelease(SubtransSLRULock);
235 }
236 
237 /*
238  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
239  *
240  * The page is not actually written, just set up in shared memory.
241  * The slot number of the new page is returned.
242  *
243  * Control lock must be held at entry, and will be held at exit.
244  */
245 static int
246 ZeroSUBTRANSPage(int64 pageno)
247 {
248  return SimpleLruZeroPage(SubTransCtl, pageno);
249 }
250 
251 /*
252  * This must be called ONCE during postmaster or standalone-backend startup,
253  * after StartupXLOG has initialized TransamVariables->nextXid.
254  *
255  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
256  * if there are none.
257  */
258 void
260 {
261  FullTransactionId nextXid;
262  int64 startPage;
263  int64 endPage;
264 
265  /*
266  * Since we don't expect pg_subtrans to be valid across crashes, we
267  * initialize the currently-active page(s) to zeroes during startup.
268  * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
269  * the new page without regard to whatever was previously on disk.
270  */
271  LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
272 
273  startPage = TransactionIdToPage(oldestActiveXID);
274  nextXid = TransamVariables->nextXid;
275  endPage = TransactionIdToPage(XidFromFullTransactionId(nextXid));
276 
277  while (startPage != endPage)
278  {
279  (void) ZeroSUBTRANSPage(startPage);
280  startPage++;
281  /* must account for wraparound */
282  if (startPage > TransactionIdToPage(MaxTransactionId))
283  startPage = 0;
284  }
285  (void) ZeroSUBTRANSPage(startPage);
286 
287  LWLockRelease(SubtransSLRULock);
288 }
289 
290 /*
291  * Perform a checkpoint --- either during shutdown, or on-the-fly
292  */
293 void
295 {
296  /*
297  * Write dirty SUBTRANS pages to disk
298  *
299  * This is not actually necessary from a correctness point of view. We do
300  * it merely to improve the odds that writing of dirty pages is done by
301  * the checkpoint process and not by backends.
302  */
303  TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
305  TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
306 }
307 
308 
309 /*
310  * Make sure that SUBTRANS has room for a newly-allocated XID.
311  *
312  * NB: this is called while holding XidGenLock. We want it to be very fast
313  * most of the time; even when it's not so fast, no actual I/O need happen
314  * unless we're forced to write out a dirty subtrans page to make room
315  * in shared memory.
316  */
317 void
319 {
320  int64 pageno;
321 
322  /*
323  * No work except at first XID of a page. But beware: just after
324  * wraparound, the first XID of page zero is FirstNormalTransactionId.
325  */
326  if (TransactionIdToEntry(newestXact) != 0 &&
328  return;
329 
330  pageno = TransactionIdToPage(newestXact);
331 
332  LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
333 
334  /* Zero the page */
335  ZeroSUBTRANSPage(pageno);
336 
337  LWLockRelease(SubtransSLRULock);
338 }
339 
340 
341 /*
342  * Remove all SUBTRANS segments before the one holding the passed transaction ID
343  *
344  * oldestXact is the oldest TransactionXmin of any running transaction. This
345  * is called only during checkpoint.
346  */
347 void
349 {
350  int64 cutoffPage;
351 
352  /*
353  * The cutoff point is the start of the segment containing oldestXact. We
354  * pass the *page* containing oldestXact to SimpleLruTruncate. We step
355  * back one transaction to avoid passing a cutoff page that hasn't been
356  * created yet in the rare case that oldestXact would be the first item on
357  * a page and oldestXact == next XID. In that case, if we didn't subtract
358  * one, we'd trigger SimpleLruTruncate's wraparound detection.
359  */
360  TransactionIdRetreat(oldestXact);
361  cutoffPage = TransactionIdToPage(oldestXact);
362 
363  SimpleLruTruncate(SubTransCtl, cutoffPage);
364 }
365 
366 
367 /*
368  * Decide whether a SUBTRANS page number is "older" for truncation purposes.
369  * Analogous to CLOGPagePrecedes().
370  */
371 static bool
372 SubTransPagePrecedes(int64 page1, int64 page2)
373 {
374  TransactionId xid1;
375  TransactionId xid2;
376 
377  xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
378  xid1 += FirstNormalTransactionId + 1;
379  xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
380  xid2 += FirstNormalTransactionId + 1;
381 
382  return (TransactionIdPrecedes(xid1, xid2) &&
384 }
uint32 TransactionId
Definition: c.h:641
size_t Size
Definition: c.h:594
#define ERROR
Definition: elog.h:39
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Assert(fmt[strlen(fmt) - 1] !='\n')
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1168
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1781
@ LWTRANCHE_SUBTRANS_BUFFER
Definition: lwlock.h:183
@ LW_EXCLUSIVE
Definition: lwlock.h:116
int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
Definition: slru.c:530
void SimpleLruWritePage(SlruCtl ctl, int slotno)
Definition: slru.c:649
void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
Definition: slru.c:1199
void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, LWLock *ctllock, const char *subdir, int tranche_id, SyncRequestHandler sync_handler, bool long_segment_names)
Definition: slru.c:215
int SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok, TransactionId xid)
Definition: slru.c:430
int SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
Definition: slru.c:308
void SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
Definition: slru.c:1269
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition: slru.c:183
#define SlruPagePrecedesUnitTests(ctl, per_page)
Definition: slru.h:168
TransactionId TransactionXmin
Definition: snapmgr.c:105
FullTransactionId nextXid
Definition: transam.h:220
void SUBTRANSShmemInit(void)
Definition: subtrans.c:200
void SubTransSetParent(TransactionId xid, TransactionId parent)
Definition: subtrans.c:83
TransactionId SubTransGetTopmostTransaction(TransactionId xid)
Definition: subtrans.c:159
#define SUBTRANS_XACTS_PER_PAGE
Definition: subtrans.c:52
#define TransactionIdToEntry(xid)
Definition: subtrans.c:64
static SlruCtlData SubTransCtlData
Definition: subtrans.c:70
void ExtendSUBTRANS(TransactionId newestXact)
Definition: subtrans.c:318
void StartupSUBTRANS(TransactionId oldestActiveXID)
Definition: subtrans.c:259
void CheckPointSUBTRANS(void)
Definition: subtrans.c:294
Size SUBTRANSShmemSize(void)
Definition: subtrans.c:194
TransactionId SubTransGetParent(TransactionId xid)
Definition: subtrans.c:118
static bool SubTransPagePrecedes(int64 page1, int64 page2)
Definition: subtrans.c:372
static int64 TransactionIdToPage(TransactionId xid)
Definition: subtrans.c:59
static int ZeroSUBTRANSPage(int64 pageno)
Definition: subtrans.c:246
#define SubTransCtl
Definition: subtrans.c:72
void BootStrapSUBTRANS(void)
Definition: subtrans.c:221
void TruncateSUBTRANS(TransactionId oldestXact)
Definition: subtrans.c:348
#define NUM_SUBTRANS_BUFFERS
Definition: subtrans.h:15
@ SYNC_HANDLER_NONE
Definition: sync.h:42
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:314
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:329
#define TransactionIdRetreat(dest)
Definition: transam.h:141
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define XidFromFullTransactionId(x)
Definition: transam.h:48
#define FirstNormalTransactionId
Definition: transam.h:34
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
#define MaxTransactionId
Definition: transam.h:35
TransamVariablesData * TransamVariables
Definition: varsup.c:34