]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/clog.c
Remove the mostly-stubbed-out-anyway support routines for WAL UNDO.
[postgresql] / src / backend / access / transam / clog.c
1 /*-------------------------------------------------------------------------
2  *
3  * clog.c
4  *              PostgreSQL transaction-commit-log manager
5  *
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.
12  *
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.
23  *
24  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
25  * Portions Copyright (c) 1994, Regents of the University of California
26  *
27  * $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.29 2005/06/06 17:01:22 tgl Exp $
28  *
29  *-------------------------------------------------------------------------
30  */
31 #include "postgres.h"
32
33 #include "access/clog.h"
34 #include "access/slru.h"
35 #include "postmaster/bgwriter.h"
36
37
38 /*
39  * Defines for CLOG 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  * 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).
47  */
48
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)
54
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)
59
60
61 /*
62  * Link to shared-memory data structures for CLOG control
63  */
64 static SlruCtlData ClogCtlData;
65
66 #define ClogCtl (&ClogCtlData)
67
68
69 static int      ZeroCLOGPage(int pageno, bool writeXlog);
70 static bool CLOGPagePrecedes(int page1, int page2);
71 static void WriteZeroPageXlogRec(int pageno);
72
73
74 /*
75  * Record the final state of a transaction in the commit log.
76  *
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.
79  */
80 void
81 TransactionIdSetStatus(TransactionId xid, XidStatus status)
82 {
83         int                     pageno = TransactionIdToPage(xid);
84         int                     byteno = TransactionIdToByte(xid);
85         int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
86         int                     slotno;
87         char       *byteptr;
88         char            byteval;
89
90         Assert(status == TRANSACTION_STATUS_COMMITTED ||
91                    status == TRANSACTION_STATUS_ABORTED ||
92                    status == TRANSACTION_STATUS_SUB_COMMITTED);
93
94         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
95
96         slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
97         byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
98
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);
103
104         /* note this assumes exclusive access to the clog page */
105         byteval = *byteptr;
106         byteval &= ~(((1 << CLOG_BITS_PER_XACT) - 1) << bshift);
107         byteval |= (status << bshift);
108         *byteptr = byteval;
109
110         ClogCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
111
112         LWLockRelease(CLogControlLock);
113 }
114
115 /*
116  * Interrogate the state of a transaction in the commit log.
117  *
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.
120  */
121 XidStatus
122 TransactionIdGetStatus(TransactionId xid)
123 {
124         int                     pageno = TransactionIdToPage(xid);
125         int                     byteno = TransactionIdToByte(xid);
126         int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
127         int                     slotno;
128         char       *byteptr;
129         XidStatus       status;
130
131         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
132
133         slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
134         byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
135
136         status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
137
138         LWLockRelease(CLogControlLock);
139
140         return status;
141 }
142
143
144 /*
145  * Initialization of shared memory for CLOG
146  */
147
148 int
149 CLOGShmemSize(void)
150 {
151         return SimpleLruShmemSize();
152 }
153
154 void
155 CLOGShmemInit(void)
156 {
157         ClogCtl->PagePrecedes = CLOGPagePrecedes;
158         SimpleLruInit(ClogCtl, "CLOG Ctl", CLogControlLock, "pg_clog");
159 }
160
161 /*
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.)
166  */
167 void
168 BootStrapCLOG(void)
169 {
170         int                     slotno;
171
172         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
173
174         /* Create and zero the first page of the commit log */
175         slotno = ZeroCLOGPage(0, false);
176
177         /* Make sure it's written out */
178         SimpleLruWritePage(ClogCtl, slotno, NULL);
179         Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
180
181         LWLockRelease(CLogControlLock);
182 }
183
184 /*
185  * Initialize (or reinitialize) a page of CLOG to zeroes.
186  * If writeXlog is TRUE, also emit an XLOG record saying we did this.
187  *
188  * The page is not actually written, just set up in shared memory.
189  * The slot number of the new page is returned.
190  *
191  * Control lock must be held at entry, and will be held at exit.
192  */
193 static int
194 ZeroCLOGPage(int pageno, bool writeXlog)
195 {
196         int                     slotno;
197
198         slotno = SimpleLruZeroPage(ClogCtl, pageno);
199
200         if (writeXlog)
201                 WriteZeroPageXlogRec(pageno);
202
203         return slotno;
204 }
205
206 /*
207  * This must be called ONCE during postmaster or standalone-backend startup,
208  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
209  */
210 void
211 StartupCLOG(void)
212 {
213         TransactionId xid = ShmemVariableCache->nextXid;
214         int                     pageno = TransactionIdToPage(xid);
215
216         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
217
218         /*
219          * Initialize our idea of the latest page number.
220          */
221         ClogCtl->shared->latest_page_number = pageno;
222
223         /*
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.)
234          */
235         if (TransactionIdToPgIndex(xid) != 0)
236         {
237                 int                     byteno = TransactionIdToByte(xid);
238                 int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
239                 int                     slotno;
240                 char       *byteptr;
241
242                 slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
243                 byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
244
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);
249
250                 ClogCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
251         }
252
253         LWLockRelease(CLogControlLock);
254 }
255
256 /*
257  * This must be called ONCE during postmaster or standalone-backend shutdown
258  */
259 void
260 ShutdownCLOG(void)
261 {
262         /* Flush dirty CLOG pages to disk */
263         SimpleLruFlush(ClogCtl, false);
264 }
265
266 /*
267  * Perform a checkpoint --- either during shutdown, or on-the-fly
268  */
269 void
270 CheckPointCLOG(void)
271 {
272         /* Flush dirty CLOG pages to disk */
273         SimpleLruFlush(ClogCtl, true);
274 }
275
276
277 /*
278  * Make sure that CLOG has room for a newly-allocated XID.
279  *
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
283  * in shared memory.
284  */
285 void
286 ExtendCLOG(TransactionId newestXact)
287 {
288         int                     pageno;
289
290         /*
291          * No work except at first XID of a page.  But beware: just after
292          * wraparound, the first XID of page zero is FirstNormalTransactionId.
293          */
294         if (TransactionIdToPgIndex(newestXact) != 0 &&
295                 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
296                 return;
297
298         pageno = TransactionIdToPage(newestXact);
299
300         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
301
302         /* Zero the page and make an XLOG entry about it */
303         ZeroCLOGPage(pageno, true);
304
305         LWLockRelease(CLogControlLock);
306 }
307
308
309 /*
310  * Remove all CLOG segments before the one holding the passed transaction ID
311  *
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
321  * seem cleaner.
322  */
323 void
324 TruncateCLOG(TransactionId oldestXact)
325 {
326         int                     cutoffPage;
327
328         /*
329          * The cutoff point is the start of the segment containing oldestXact.
330          * We pass the *page* containing oldestXact to SimpleLruTruncate.
331          */
332         cutoffPage = TransactionIdToPage(oldestXact);
333
334         /* Check to see if there's any files that could be removed */
335         if (!SlruScanDirectory(ClogCtl, cutoffPage, false))
336                 return;                                 /* nothing to remove */
337
338         /* Perform a CHECKPOINT */
339         RequestCheckpoint(true);
340
341         /* Now we can remove the old CLOG segment(s) */
342         SimpleLruTruncate(ClogCtl, cutoffPage);
343 }
344
345
346 /*
347  * Decide which of two CLOG page numbers is "older" for truncation purposes.
348  *
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.
354  */
355 static bool
356 CLOGPagePrecedes(int page1, int page2)
357 {
358         TransactionId xid1;
359         TransactionId xid2;
360
361         xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
362         xid1 += FirstNormalTransactionId;
363         xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
364         xid2 += FirstNormalTransactionId;
365
366         return TransactionIdPrecedes(xid1, xid2);
367 }
368
369
370 /*
371  * Write a ZEROPAGE xlog record
372  *
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.)
376  */
377 static void
378 WriteZeroPageXlogRec(int pageno)
379 {
380         XLogRecData rdata;
381
382         rdata.buffer = InvalidBuffer;
383         rdata.data = (char *) (&pageno);
384         rdata.len = sizeof(int);
385         rdata.next = NULL;
386         (void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
387 }
388
389 /*
390  * CLOG resource manager's routines
391  */
392 void
393 clog_redo(XLogRecPtr lsn, XLogRecord *record)
394 {
395         uint8           info = record->xl_info & ~XLR_INFO_MASK;
396
397         if (info == CLOG_ZEROPAGE)
398         {
399                 int                     pageno;
400                 int                     slotno;
401
402                 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
403
404                 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
405
406                 slotno = ZeroCLOGPage(pageno, false);
407                 SimpleLruWritePage(ClogCtl, slotno, NULL);
408                 Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
409
410                 LWLockRelease(CLogControlLock);
411         }
412 }
413
414 void
415 clog_desc(char *buf, uint8 xl_info, char *rec)
416 {
417         uint8           info = xl_info & ~XLR_INFO_MASK;
418
419         if (info == CLOG_ZEROPAGE)
420         {
421                 int                     pageno;
422
423                 memcpy(&pageno, rec, sizeof(int));
424                 sprintf(buf + strlen(buf), "zeropage: %d", pageno);
425         }
426         else
427                 strcat(buf, "UNKNOWN");
428 }