Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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