1 /*-------------------------------------------------------------------------
4 * PostgreSQL transaction-commit-log manager
6 * This module replaces the old "pg_log" access code, which treated pg_log
7 * essentially like a relation, in that it went through the regular buffer
8 * manager. The problem with that was that there wasn't any good way to
9 * recycle storage space for transactions so old that they'll never be
10 * looked up again. Now we use specialized access code so that the commit
11 * log can be broken into relatively small, independent segments.
13 * XLOG interactions: this module generates an XLOG record whenever a new
14 * CLOG page is initialized to zeroes. Other writes of CLOG come from
15 * recording of transaction commit or abort in xact.c, which generates its
16 * own XLOG records for these events and will re-perform the status update
17 * on redo; so we need make no additional XLOG entry here. Also, the XLOG
18 * is guaranteed flushed through the XLOG commit record before we are called
19 * to log a commit, so the WAL rule "write xlog before data" is satisfied
20 * automatically for commits, and we don't really care for aborts. Therefore,
21 * we don't need to mark CLOG pages with LSN information; we have enough
22 * synchronization already.
24 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
25 * Portions Copyright (c) 1994, Regents of the University of California
27 * $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.29 2005/06/06 17:01:22 tgl Exp $
29 *-------------------------------------------------------------------------
33 #include "access/clog.h"
34 #include "access/slru.h"
35 #include "postmaster/bgwriter.h"
39 * Defines for CLOG page sizes. A page is the same BLCKSZ as is used
40 * everywhere else in Postgres.
42 * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
43 * CLOG page numbering also wraps around at 0xFFFFFFFF/CLOG_XACTS_PER_PAGE,
44 * and CLOG segment numbering at 0xFFFFFFFF/CLOG_XACTS_PER_SEGMENT. We need
45 * take no explicit notice of that fact in this module, except when comparing
46 * segment and page numbers in TruncateCLOG (see CLOGPagePrecedes).
49 /* We need two bits per xact, so four xacts fit in a byte */
50 #define CLOG_BITS_PER_XACT 2
51 #define CLOG_XACTS_PER_BYTE 4
52 #define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
53 #define CLOG_XACT_BITMASK ((1 << CLOG_BITS_PER_XACT) - 1)
55 #define TransactionIdToPage(xid) ((xid) / (TransactionId) CLOG_XACTS_PER_PAGE)
56 #define TransactionIdToPgIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_PAGE)
57 #define TransactionIdToByte(xid) (TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE)
58 #define TransactionIdToBIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_BYTE)
62 * Link to shared-memory data structures for CLOG control
64 static SlruCtlData ClogCtlData;
66 #define ClogCtl (&ClogCtlData)
69 static int ZeroCLOGPage(int pageno, bool writeXlog);
70 static bool CLOGPagePrecedes(int page1, int page2);
71 static void WriteZeroPageXlogRec(int pageno);
75 * Record the final state of a transaction in the commit log.
77 * NB: this is a low-level routine and is NOT the preferred entry point
78 * for most uses; TransactionLogUpdate() in transam.c is the intended caller.
81 TransactionIdSetStatus(TransactionId xid, XidStatus status)
83 int pageno = TransactionIdToPage(xid);
84 int byteno = TransactionIdToByte(xid);
85 int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
90 Assert(status == TRANSACTION_STATUS_COMMITTED ||
91 status == TRANSACTION_STATUS_ABORTED ||
92 status == TRANSACTION_STATUS_SUB_COMMITTED);
94 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
96 slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
97 byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
99 /* Current state should be 0, subcommitted or target state */
100 Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
101 ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
102 ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
104 /* note this assumes exclusive access to the clog page */
106 byteval &= ~(((1 << CLOG_BITS_PER_XACT) - 1) << bshift);
107 byteval |= (status << bshift);
110 ClogCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
112 LWLockRelease(CLogControlLock);
116 * Interrogate the state of a transaction in the commit log.
118 * NB: this is a low-level routine and is NOT the preferred entry point
119 * for most uses; TransactionLogFetch() in transam.c is the intended caller.
122 TransactionIdGetStatus(TransactionId xid)
124 int pageno = TransactionIdToPage(xid);
125 int byteno = TransactionIdToByte(xid);
126 int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
131 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
133 slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
134 byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
136 status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
138 LWLockRelease(CLogControlLock);
145 * Initialization of shared memory for CLOG
151 return SimpleLruShmemSize();
157 ClogCtl->PagePrecedes = CLOGPagePrecedes;
158 SimpleLruInit(ClogCtl, "CLOG Ctl", CLogControlLock, "pg_clog");
162 * This func must be called ONCE on system install. It creates
163 * the initial CLOG segment. (The CLOG directory is assumed to
164 * have been created by the initdb shell script, and CLOGShmemInit
165 * must have been called already.)
172 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
174 /* Create and zero the first page of the commit log */
175 slotno = ZeroCLOGPage(0, false);
177 /* Make sure it's written out */
178 SimpleLruWritePage(ClogCtl, slotno, NULL);
179 Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
181 LWLockRelease(CLogControlLock);
185 * Initialize (or reinitialize) a page of CLOG to zeroes.
186 * If writeXlog is TRUE, also emit an XLOG record saying we did this.
188 * The page is not actually written, just set up in shared memory.
189 * The slot number of the new page is returned.
191 * Control lock must be held at entry, and will be held at exit.
194 ZeroCLOGPage(int pageno, bool writeXlog)
198 slotno = SimpleLruZeroPage(ClogCtl, pageno);
201 WriteZeroPageXlogRec(pageno);
207 * This must be called ONCE during postmaster or standalone-backend startup,
208 * after StartupXLOG has initialized ShmemVariableCache->nextXid.
213 TransactionId xid = ShmemVariableCache->nextXid;
214 int pageno = TransactionIdToPage(xid);
216 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
219 * Initialize our idea of the latest page number.
221 ClogCtl->shared->latest_page_number = pageno;
224 * Zero out the remainder of the current clog page. Under normal
225 * circumstances it should be zeroes already, but it seems at least
226 * theoretically possible that XLOG replay will have settled on a
227 * nextXID value that is less than the last XID actually used and
228 * marked by the previous database lifecycle (since subtransaction
229 * commit writes clog but makes no WAL entry). Let's just be safe.
230 * (We need not worry about pages beyond the current one, since those
231 * will be zeroed when first used. For the same reason, there is no
232 * need to do anything when nextXid is exactly at a page boundary; and
233 * it's likely that the "current" page doesn't exist yet in that case.)
235 if (TransactionIdToPgIndex(xid) != 0)
237 int byteno = TransactionIdToByte(xid);
238 int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
242 slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
243 byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
245 /* Zero so-far-unused positions in the current byte */
246 *byteptr &= (1 << bshift) - 1;
247 /* Zero the rest of the page */
248 MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
250 ClogCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
253 LWLockRelease(CLogControlLock);
257 * This must be called ONCE during postmaster or standalone-backend shutdown
262 /* Flush dirty CLOG pages to disk */
263 SimpleLruFlush(ClogCtl, false);
267 * Perform a checkpoint --- either during shutdown, or on-the-fly
272 /* Flush dirty CLOG pages to disk */
273 SimpleLruFlush(ClogCtl, true);
278 * Make sure that CLOG has room for a newly-allocated XID.
280 * NB: this is called while holding XidGenLock. We want it to be very fast
281 * most of the time; even when it's not so fast, no actual I/O need happen
282 * unless we're forced to write out a dirty clog or xlog page to make room
286 ExtendCLOG(TransactionId newestXact)
291 * No work except at first XID of a page. But beware: just after
292 * wraparound, the first XID of page zero is FirstNormalTransactionId.
294 if (TransactionIdToPgIndex(newestXact) != 0 &&
295 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
298 pageno = TransactionIdToPage(newestXact);
300 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
302 /* Zero the page and make an XLOG entry about it */
303 ZeroCLOGPage(pageno, true);
305 LWLockRelease(CLogControlLock);
310 * Remove all CLOG segments before the one holding the passed transaction ID
312 * When this is called, we know that the database logically contains no
313 * reference to transaction IDs older than oldestXact. However, we must
314 * not truncate the CLOG until we have performed a checkpoint, to ensure
315 * that no such references remain on disk either; else a crash just after
316 * the truncation might leave us with a problem. Since CLOG segments hold
317 * a large number of transactions, the opportunity to actually remove a
318 * segment is fairly rare, and so it seems best not to do the checkpoint
319 * unless we have confirmed that there is a removable segment. Therefore
320 * we issue the checkpoint command here, not in higher-level code as might
324 TruncateCLOG(TransactionId oldestXact)
329 * The cutoff point is the start of the segment containing oldestXact.
330 * We pass the *page* containing oldestXact to SimpleLruTruncate.
332 cutoffPage = TransactionIdToPage(oldestXact);
334 /* Check to see if there's any files that could be removed */
335 if (!SlruScanDirectory(ClogCtl, cutoffPage, false))
336 return; /* nothing to remove */
338 /* Perform a CHECKPOINT */
339 RequestCheckpoint(true);
341 /* Now we can remove the old CLOG segment(s) */
342 SimpleLruTruncate(ClogCtl, cutoffPage);
347 * Decide which of two CLOG page numbers is "older" for truncation purposes.
349 * We need to use comparison of TransactionIds here in order to do the right
350 * thing with wraparound XID arithmetic. However, if we are asked about
351 * page number zero, we don't want to hand InvalidTransactionId to
352 * TransactionIdPrecedes: it'll get weird about permanent xact IDs. So,
353 * offset both xids by FirstNormalTransactionId to avoid that.
356 CLOGPagePrecedes(int page1, int page2)
361 xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
362 xid1 += FirstNormalTransactionId;
363 xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
364 xid2 += FirstNormalTransactionId;
366 return TransactionIdPrecedes(xid1, xid2);
371 * Write a ZEROPAGE xlog record
373 * Note: xlog record is marked as outside transaction control, since we
374 * want it to be redone whether the invoking transaction commits or not.
375 * (Besides which, this is normally done just before entering a transaction.)
378 WriteZeroPageXlogRec(int pageno)
382 rdata.buffer = InvalidBuffer;
383 rdata.data = (char *) (&pageno);
384 rdata.len = sizeof(int);
386 (void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
390 * CLOG resource manager's routines
393 clog_redo(XLogRecPtr lsn, XLogRecord *record)
395 uint8 info = record->xl_info & ~XLR_INFO_MASK;
397 if (info == CLOG_ZEROPAGE)
402 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
404 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
406 slotno = ZeroCLOGPage(pageno, false);
407 SimpleLruWritePage(ClogCtl, slotno, NULL);
408 Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
410 LWLockRelease(CLogControlLock);
415 clog_desc(char *buf, uint8 xl_info, char *rec)
417 uint8 info = xl_info & ~XLR_INFO_MASK;
419 if (info == CLOG_ZEROPAGE)
423 memcpy(&pageno, rec, sizeof(int));
424 sprintf(buf + strlen(buf), "zeropage: %d", pageno);
427 strcat(buf, "UNKNOWN");