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 "miscadmin.h"
35 #include "pg_trace.h"
36 #include "utils/guc_hooks.h"
37 #include "utils/snapmgr.h"
38 
39 
40 /*
41  * Defines for SubTrans page sizes. A page is the same BLCKSZ as is used
42  * everywhere else in Postgres.
43  *
44  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
45  * SubTrans page numbering also wraps around at
46  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
47  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need take no
48  * explicit notice of that fact in this module, except when comparing segment
49  * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes) and zeroing
50  * them in StartupSUBTRANS.
51  */
52 
53 /* We need four bytes per xact */
54 #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
55 
56 /*
57  * Although we return an int64 the actual value can't currently exceed
58  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE.
59  */
60 static inline int64
62 {
63  return xid / (int64) SUBTRANS_XACTS_PER_PAGE;
64 }
65 
66 #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
67 
68 
69 /*
70  * Link to shared-memory data structures for SUBTRANS control
71  */
73 
74 #define SubTransCtl (&SubTransCtlData)
75 
76 
77 static int ZeroSUBTRANSPage(int64 pageno);
78 static bool SubTransPagePrecedes(int64 page1, int64 page2);
79 
80 
81 /*
82  * Record the parent of a subtransaction in the subtrans log.
83  */
84 void
86 {
87  int64 pageno = TransactionIdToPage(xid);
88  int entryno = TransactionIdToEntry(xid);
89  int slotno;
90  LWLock *lock;
91  TransactionId *ptr;
92 
94  Assert(TransactionIdFollows(xid, parent));
95 
96  lock = SimpleLruGetBankLock(SubTransCtl, pageno);
98 
99  slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
100  ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
101  ptr += entryno;
102 
103  /*
104  * It's possible we'll try to set the parent xid multiple times but we
105  * shouldn't ever be changing the xid from one valid xid to another valid
106  * xid, which would corrupt the data structure.
107  */
108  if (*ptr != parent)
109  {
110  Assert(*ptr == InvalidTransactionId);
111  *ptr = parent;
112  SubTransCtl->shared->page_dirty[slotno] = true;
113  }
114 
115  LWLockRelease(lock);
116 }
117 
118 /*
119  * Interrogate the parent of a transaction in the subtrans log.
120  */
123 {
124  int64 pageno = TransactionIdToPage(xid);
125  int entryno = TransactionIdToEntry(xid);
126  int slotno;
127  TransactionId *ptr;
128  TransactionId parent;
129 
130  /* Can't ask about stuff that might not be around anymore */
132 
133  /* Bootstrap and frozen XIDs have no parent */
134  if (!TransactionIdIsNormal(xid))
135  return InvalidTransactionId;
136 
137  /* lock is acquired by SimpleLruReadPage_ReadOnly */
138 
139  slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
140  ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
141  ptr += entryno;
142 
143  parent = *ptr;
144 
146 
147  return parent;
148 }
149 
150 /*
151  * SubTransGetTopmostTransaction
152  *
153  * Returns the topmost transaction of the given transaction id.
154  *
155  * Because we cannot look back further than TransactionXmin, it is possible
156  * that this function will lie and return an intermediate subtransaction ID
157  * instead of the true topmost parent ID. This is OK, because in practice
158  * we only care about detecting whether the topmost parent is still running
159  * or is part of a current snapshot's list of still-running transactions.
160  * Therefore, any XID before TransactionXmin is as good as any other.
161  */
164 {
165  TransactionId parentXid = xid,
166  previousXid = xid;
167 
168  /* Can't ask about stuff that might not be around anymore */
170 
171  while (TransactionIdIsValid(parentXid))
172  {
173  previousXid = parentXid;
174  if (TransactionIdPrecedes(parentXid, TransactionXmin))
175  break;
176  parentXid = SubTransGetParent(parentXid);
177 
178  /*
179  * By convention the parent xid gets allocated first, so should always
180  * precede the child xid. Anything else points to a corrupted data
181  * structure that could lead to an infinite loop, so exit.
182  */
183  if (!TransactionIdPrecedes(parentXid, previousXid))
184  elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
185  previousXid, parentXid);
186  }
187 
188  Assert(TransactionIdIsValid(previousXid));
189 
190  return previousXid;
191 }
192 
193 /*
194  * Number of shared SUBTRANS buffers.
195  *
196  * If asked to autotune, use 2MB for every 1GB of shared buffers, up to 8MB.
197  * Otherwise just cap the configured amount to be between 16 and the maximum
198  * allowed.
199  */
200 static int
202 {
203  /* auto-tune based on shared buffers */
204  if (subtransaction_buffers == 0)
205  return SimpleLruAutotuneBuffers(512, 1024);
206 
208 }
209 
210 /*
211  * Initialization of shared memory for SUBTRANS
212  */
213 Size
215 {
217 }
218 
219 void
221 {
222  /* If auto-tuning is requested, now is the time to do it */
223  if (subtransaction_buffers == 0)
224  {
225  char buf[32];
226 
227  snprintf(buf, sizeof(buf), "%d", SUBTRANSShmemBuffers());
228  SetConfigOption("subtransaction_buffers", buf, PGC_POSTMASTER,
230 
231  /*
232  * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
233  * However, if the DBA explicitly set subtransaction_buffers = 0 in
234  * the config file, then PGC_S_DYNAMIC_DEFAULT will fail to override
235  * that and we must force the matter with PGC_S_OVERRIDE.
236  */
237  if (subtransaction_buffers == 0) /* failed to apply it? */
238  SetConfigOption("subtransaction_buffers", buf, PGC_POSTMASTER,
240  }
242 
243  SubTransCtl->PagePrecedes = SubTransPagePrecedes;
244  SimpleLruInit(SubTransCtl, "subtransaction", SUBTRANSShmemBuffers(), 0,
245  "pg_subtrans", LWTRANCHE_SUBTRANS_BUFFER,
248 }
249 
250 /*
251  * GUC check_hook for subtransaction_buffers
252  */
253 bool
255 {
256  return check_slru_buffers("subtransaction_buffers", newval);
257 }
258 
259 /*
260  * This func must be called ONCE on system install. It creates
261  * the initial SUBTRANS segment. (The SUBTRANS directory is assumed to
262  * have been created by the initdb shell script, and SUBTRANSShmemInit
263  * must have been called already.)
264  *
265  * Note: it's not really necessary to create the initial segment now,
266  * since slru.c would create it on first write anyway. But we may as well
267  * do it to be sure the directory is set up correctly.
268  */
269 void
271 {
272  int slotno;
274 
276 
277  /* Create and zero the first page of the subtrans log */
278  slotno = ZeroSUBTRANSPage(0);
279 
280  /* Make sure it's written out */
282  Assert(!SubTransCtl->shared->page_dirty[slotno]);
283 
284  LWLockRelease(lock);
285 }
286 
287 /*
288  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
289  *
290  * The page is not actually written, just set up in shared memory.
291  * The slot number of the new page is returned.
292  *
293  * Control lock must be held at entry, and will be held at exit.
294  */
295 static int
296 ZeroSUBTRANSPage(int64 pageno)
297 {
298  return SimpleLruZeroPage(SubTransCtl, pageno);
299 }
300 
301 /*
302  * This must be called ONCE during postmaster or standalone-backend startup,
303  * after StartupXLOG has initialized TransamVariables->nextXid.
304  *
305  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
306  * if there are none.
307  */
308 void
310 {
311  FullTransactionId nextXid;
312  int64 startPage;
313  int64 endPage;
314  LWLock *prevlock = NULL;
315  LWLock *lock;
316 
317  /*
318  * Since we don't expect pg_subtrans to be valid across crashes, we
319  * initialize the currently-active page(s) to zeroes during startup.
320  * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
321  * the new page without regard to whatever was previously on disk.
322  */
323  startPage = TransactionIdToPage(oldestActiveXID);
324  nextXid = TransamVariables->nextXid;
325  endPage = TransactionIdToPage(XidFromFullTransactionId(nextXid));
326 
327  for (;;)
328  {
329  lock = SimpleLruGetBankLock(SubTransCtl, startPage);
330  if (prevlock != lock)
331  {
332  if (prevlock)
333  LWLockRelease(prevlock);
335  prevlock = lock;
336  }
337 
338  (void) ZeroSUBTRANSPage(startPage);
339  if (startPage == endPage)
340  break;
341 
342  startPage++;
343  /* must account for wraparound */
344  if (startPage > TransactionIdToPage(MaxTransactionId))
345  startPage = 0;
346  }
347 
348  LWLockRelease(lock);
349 }
350 
351 /*
352  * Perform a checkpoint --- either during shutdown, or on-the-fly
353  */
354 void
356 {
357  /*
358  * Write dirty SUBTRANS pages to disk
359  *
360  * This is not actually necessary from a correctness point of view. We do
361  * it merely to improve the odds that writing of dirty pages is done by
362  * the checkpoint process and not by backends.
363  */
364  TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
366  TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
367 }
368 
369 
370 /*
371  * Make sure that SUBTRANS has room for a newly-allocated XID.
372  *
373  * NB: this is called while holding XidGenLock. We want it to be very fast
374  * most of the time; even when it's not so fast, no actual I/O need happen
375  * unless we're forced to write out a dirty subtrans page to make room
376  * in shared memory.
377  */
378 void
380 {
381  int64 pageno;
382  LWLock *lock;
383 
384  /*
385  * No work except at first XID of a page. But beware: just after
386  * wraparound, the first XID of page zero is FirstNormalTransactionId.
387  */
388  if (TransactionIdToEntry(newestXact) != 0 &&
390  return;
391 
392  pageno = TransactionIdToPage(newestXact);
393 
394  lock = SimpleLruGetBankLock(SubTransCtl, pageno);
396 
397  /* Zero the page */
398  ZeroSUBTRANSPage(pageno);
399 
400  LWLockRelease(lock);
401 }
402 
403 
404 /*
405  * Remove all SUBTRANS segments before the one holding the passed transaction ID
406  *
407  * oldestXact is the oldest TransactionXmin of any running transaction. This
408  * is called only during checkpoint.
409  */
410 void
412 {
413  int64 cutoffPage;
414 
415  /*
416  * The cutoff point is the start of the segment containing oldestXact. We
417  * pass the *page* containing oldestXact to SimpleLruTruncate. We step
418  * back one transaction to avoid passing a cutoff page that hasn't been
419  * created yet in the rare case that oldestXact would be the first item on
420  * a page and oldestXact == next XID. In that case, if we didn't subtract
421  * one, we'd trigger SimpleLruTruncate's wraparound detection.
422  */
423  TransactionIdRetreat(oldestXact);
424  cutoffPage = TransactionIdToPage(oldestXact);
425 
426  SimpleLruTruncate(SubTransCtl, cutoffPage);
427 }
428 
429 
430 /*
431  * Decide whether a SUBTRANS page number is "older" for truncation purposes.
432  * Analogous to CLOGPagePrecedes().
433  */
434 static bool
435 SubTransPagePrecedes(int64 page1, int64 page2)
436 {
437  TransactionId xid1;
438  TransactionId xid2;
439 
440  xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
441  xid1 += FirstNormalTransactionId + 1;
442  xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
443  xid2 += FirstNormalTransactionId + 1;
444 
445  return (TransactionIdPrecedes(xid1, xid2) &&
447 }
#define Min(x, y)
Definition: c.h:1004
#define Max(x, y)
Definition: c.h:998
#define Assert(condition)
Definition: c.h:858
uint32 TransactionId
Definition: c.h:652
size_t Size
Definition: c.h:605
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
int subtransaction_buffers
Definition: globals.c:167
void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source)
Definition: guc.c:4285
#define newval
GucSource
Definition: guc.h:108
@ PGC_S_DYNAMIC_DEFAULT
Definition: guc.h:110
@ PGC_S_OVERRIDE
Definition: guc.h:119
@ PGC_POSTMASTER
Definition: guc.h:70
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1170
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1783
@ LWTRANCHE_SUBTRANS_BUFFER
Definition: lwlock.h:181
@ LWTRANCHE_SUBTRANS_SLRU
Definition: lwlock.h:215
@ LW_EXCLUSIVE
Definition: lwlock.h:114
static rewind_source * source
Definition: pg_rewind.c:89
static char * buf
Definition: pg_test_fsync.c:73
#define snprintf
Definition: port.h:238
void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, const char *subdir, int buffer_tranche_id, int bank_tranche_id, SyncRequestHandler sync_handler, bool long_segment_names)
Definition: slru.c:238
int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
Definition: slru.c:591
void SimpleLruWritePage(SlruCtl ctl, int slotno)
Definition: slru.c:715
void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
Definition: slru.c:1305
int SimpleLruAutotuneBuffers(int divisor, int max)
Definition: slru.c:217
int SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok, TransactionId xid)
Definition: slru.c:488
int SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
Definition: slru.c:361
void SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
Definition: slru.c:1391
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition: slru.c:184
bool check_slru_buffers(const char *name, int *newval)
Definition: slru.c:341
static LWLock * SimpleLruGetBankLock(SlruCtl ctl, int64 pageno)
Definition: slru.h:179
#define SlruPagePrecedesUnitTests(ctl, per_page)
Definition: slru.h:203
#define SLRU_MAX_ALLOWED_BUFFERS
Definition: slru.h:24
TransactionId TransactionXmin
Definition: snapmgr.c:98
Definition: lwlock.h:42
FullTransactionId nextXid
Definition: transam.h:220
bool check_subtrans_buffers(int *newval, void **extra, GucSource source)
Definition: subtrans.c:254
void SUBTRANSShmemInit(void)
Definition: subtrans.c:220
void SubTransSetParent(TransactionId xid, TransactionId parent)
Definition: subtrans.c:85
TransactionId SubTransGetTopmostTransaction(TransactionId xid)
Definition: subtrans.c:163
#define SUBTRANS_XACTS_PER_PAGE
Definition: subtrans.c:54
#define TransactionIdToEntry(xid)
Definition: subtrans.c:66
static SlruCtlData SubTransCtlData
Definition: subtrans.c:72
void ExtendSUBTRANS(TransactionId newestXact)
Definition: subtrans.c:379
void StartupSUBTRANS(TransactionId oldestActiveXID)
Definition: subtrans.c:309
void CheckPointSUBTRANS(void)
Definition: subtrans.c:355
Size SUBTRANSShmemSize(void)
Definition: subtrans.c:214
static int SUBTRANSShmemBuffers(void)
Definition: subtrans.c:201
TransactionId SubTransGetParent(TransactionId xid)
Definition: subtrans.c:122
static bool SubTransPagePrecedes(int64 page1, int64 page2)
Definition: subtrans.c:435
static int64 TransactionIdToPage(TransactionId xid)
Definition: subtrans.c:61
static int ZeroSUBTRANSPage(int64 pageno)
Definition: subtrans.c:296
#define SubTransCtl
Definition: subtrans.c:74
void BootStrapSUBTRANS(void)
Definition: subtrans.c:270
void TruncateSUBTRANS(TransactionId oldestXact)
Definition: subtrans.c:411
@ 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