1
/*-------------------------------------------------------------------------
4
* PostgreSQL subtransaction-log manager
6
* The pg_subtrans manager is a pg_clog-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
13
* This code is based on clog.c, but the robustness requirements
14
* are completely different from pg_clog, 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.
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.
22
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
23
* Portions Copyright (c) 1994, Regents of the University of California
25
* $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.7 2004-12-31 21:59:29 pgsql Exp $
27
*-------------------------------------------------------------------------
31
#include "access/slru.h"
32
#include "access/subtrans.h"
33
#include "storage/sinval.h"
34
#include "utils/tqual.h"
38
* Defines for SubTrans page sizes. A page is the same BLCKSZ as is used
39
* everywhere else in Postgres.
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_SEGMENTS_PER_PAGE. 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).
49
/* We need four bytes per xact */
50
#define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
52
#define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
53
#define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
57
* Link to shared-memory data structures for SUBTRANS control
59
static SlruCtlData SubTransCtlData;
61
#define SubTransCtl (&SubTransCtlData)
64
static int ZeroSUBTRANSPage(int pageno);
65
static bool SubTransPagePrecedes(int page1, int page2);
69
* Record the parent of a subtransaction in the subtrans log.
72
SubTransSetParent(TransactionId xid, TransactionId parent)
74
int pageno = TransactionIdToPage(xid);
75
int entryno = TransactionIdToEntry(xid);
79
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
81
slotno = SimpleLruReadPage(SubTransCtl, pageno, xid);
82
ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
85
/* Current state should be 0 */
86
Assert(*ptr == InvalidTransactionId);
90
SubTransCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
92
LWLockRelease(SubtransControlLock);
96
* Interrogate the parent of a transaction in the subtrans log.
99
SubTransGetParent(TransactionId xid)
101
int pageno = TransactionIdToPage(xid);
102
int entryno = TransactionIdToEntry(xid);
105
TransactionId parent;
107
/* Can't ask about stuff that might not be around anymore */
108
Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
110
/* Bootstrap and frozen XIDs have no parent */
111
if (!TransactionIdIsNormal(xid))
112
return InvalidTransactionId;
114
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
116
slotno = SimpleLruReadPage(SubTransCtl, pageno, xid);
117
ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
122
LWLockRelease(SubtransControlLock);
128
* SubTransGetTopmostTransaction
130
* Returns the topmost transaction of the given transaction id.
132
* Because we cannot look back further than TransactionXmin, it is possible
133
* that this function will lie and return an intermediate subtransaction ID
134
* instead of the true topmost parent ID. This is OK, because in practice
135
* we only care about detecting whether the topmost parent is still running
136
* or is part of a current snapshot's list of still-running transactions.
137
* Therefore, any XID before TransactionXmin is as good as any other.
140
SubTransGetTopmostTransaction(TransactionId xid)
142
TransactionId parentXid = xid,
145
/* Can't ask about stuff that might not be around anymore */
146
Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
148
while (TransactionIdIsValid(parentXid))
150
previousXid = parentXid;
151
if (TransactionIdPrecedes(parentXid, TransactionXmin))
153
parentXid = SubTransGetParent(parentXid);
156
Assert(TransactionIdIsValid(previousXid));
163
* Initialization of shared memory for SUBTRANS
167
SUBTRANSShmemSize(void)
169
return SimpleLruShmemSize();
173
SUBTRANSShmemInit(void)
175
SubTransCtl->PagePrecedes = SubTransPagePrecedes;
176
SimpleLruInit(SubTransCtl, "SUBTRANS Ctl",
177
SubtransControlLock, "pg_subtrans");
178
/* Override default assumption that writes should be fsync'd */
179
SubTransCtl->do_fsync = false;
183
* This func must be called ONCE on system install. It creates
184
* the initial SUBTRANS segment. (The SUBTRANS directory is assumed to
185
* have been created by the initdb shell script, and SUBTRANSShmemInit
186
* must have been called already.)
188
* Note: it's not really necessary to create the initial segment now,
189
* since slru.c would create it on first write anyway. But we may as well
190
* do it to be sure the directory is set up correctly.
193
BootStrapSUBTRANS(void)
197
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
199
/* Create and zero the first page of the subtrans log */
200
slotno = ZeroSUBTRANSPage(0);
202
/* Make sure it's written out */
203
SimpleLruWritePage(SubTransCtl, slotno, NULL);
204
Assert(SubTransCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
206
LWLockRelease(SubtransControlLock);
210
* Initialize (or reinitialize) a page of SUBTRANS to zeroes.
212
* The page is not actually written, just set up in shared memory.
213
* The slot number of the new page is returned.
215
* Control lock must be held at entry, and will be held at exit.
218
ZeroSUBTRANSPage(int pageno)
220
return SimpleLruZeroPage(SubTransCtl, pageno);
224
* This must be called ONCE during postmaster or standalone-backend startup,
225
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
228
StartupSUBTRANS(void)
233
* Since we don't expect pg_subtrans to be valid across crashes, we
234
* initialize the currently-active page to zeroes during startup.
235
* Whenever we advance into a new page, ExtendSUBTRANS will likewise
236
* zero the new page without regard to whatever was previously on
239
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
241
startPage = TransactionIdToPage(ShmemVariableCache->nextXid);
242
(void) ZeroSUBTRANSPage(startPage);
244
LWLockRelease(SubtransControlLock);
248
* This must be called ONCE during postmaster or standalone-backend shutdown
251
ShutdownSUBTRANS(void)
254
* Flush dirty SUBTRANS pages to disk
256
* This is not actually necessary from a correctness point of view. We do
257
* it merely as a debugging aid.
259
SimpleLruFlush(SubTransCtl, false);
263
* Perform a checkpoint --- either during shutdown, or on-the-fly
266
CheckPointSUBTRANS(void)
269
* Flush dirty SUBTRANS pages to disk
271
* This is not actually necessary from a correctness point of view. We do
272
* it merely to improve the odds that writing of dirty pages is done
273
* by the checkpoint process and not by backends.
275
SimpleLruFlush(SubTransCtl, true);
280
* Make sure that SUBTRANS has room for a newly-allocated XID.
282
* NB: this is called while holding XidGenLock. We want it to be very fast
283
* most of the time; even when it's not so fast, no actual I/O need happen
284
* unless we're forced to write out a dirty subtrans page to make room
288
ExtendSUBTRANS(TransactionId newestXact)
293
* No work except at first XID of a page. But beware: just after
294
* wraparound, the first XID of page zero is FirstNormalTransactionId.
296
if (TransactionIdToEntry(newestXact) != 0 &&
297
!TransactionIdEquals(newestXact, FirstNormalTransactionId))
300
pageno = TransactionIdToPage(newestXact);
302
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
305
ZeroSUBTRANSPage(pageno);
307
LWLockRelease(SubtransControlLock);
312
* Remove all SUBTRANS segments before the one holding the passed transaction ID
314
* This is normally called during checkpoint, with oldestXact being the
315
* oldest TransactionXmin of any running transaction.
318
TruncateSUBTRANS(TransactionId oldestXact)
323
* The cutoff point is the start of the segment containing oldestXact.
324
* We pass the *page* containing oldestXact to SimpleLruTruncate.
326
cutoffPage = TransactionIdToPage(oldestXact);
328
SimpleLruTruncate(SubTransCtl, cutoffPage);
333
* Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
335
* We need to use comparison of TransactionIds here in order to do the right
336
* thing with wraparound XID arithmetic. However, if we are asked about
337
* page number zero, we don't want to hand InvalidTransactionId to
338
* TransactionIdPrecedes: it'll get weird about permanent xact IDs. So,
339
* offset both xids by FirstNormalTransactionId to avoid that.
342
SubTransPagePrecedes(int page1, int page2)
347
xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
348
xid1 += FirstNormalTransactionId;
349
xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
350
xid2 += FirstNormalTransactionId;
352
return TransactionIdPrecedes(xid1, xid2);