]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/clog.c
Standard pgindent run for 8.1.
[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.33 2005/10/15 02:49:09 momjian 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 Size
148 CLOGShmemSize(void)
149 {
150         return SimpleLruShmemSize();
151 }
152
153 void
154 CLOGShmemInit(void)
155 {
156         ClogCtl->PagePrecedes = CLOGPagePrecedes;
157         SimpleLruInit(ClogCtl, "CLOG Ctl", CLogControlLock, "pg_clog");
158 }
159
160 /*
161  * This func must be called ONCE on system install.  It creates
162  * the initial CLOG segment.  (The CLOG directory is assumed to
163  * have been created by the initdb shell script, and CLOGShmemInit
164  * must have been called already.)
165  */
166 void
167 BootStrapCLOG(void)
168 {
169         int                     slotno;
170
171         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
172
173         /* Create and zero the first page of the commit log */
174         slotno = ZeroCLOGPage(0, false);
175
176         /* Make sure it's written out */
177         SimpleLruWritePage(ClogCtl, slotno, NULL);
178         Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
179
180         LWLockRelease(CLogControlLock);
181 }
182
183 /*
184  * Initialize (or reinitialize) a page of CLOG to zeroes.
185  * If writeXlog is TRUE, also emit an XLOG record saying we did this.
186  *
187  * The page is not actually written, just set up in shared memory.
188  * The slot number of the new page is returned.
189  *
190  * Control lock must be held at entry, and will be held at exit.
191  */
192 static int
193 ZeroCLOGPage(int pageno, bool writeXlog)
194 {
195         int                     slotno;
196
197         slotno = SimpleLruZeroPage(ClogCtl, pageno);
198
199         if (writeXlog)
200                 WriteZeroPageXlogRec(pageno);
201
202         return slotno;
203 }
204
205 /*
206  * This must be called ONCE during postmaster or standalone-backend startup,
207  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
208  */
209 void
210 StartupCLOG(void)
211 {
212         TransactionId xid = ShmemVariableCache->nextXid;
213         int                     pageno = TransactionIdToPage(xid);
214
215         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
216
217         /*
218          * Initialize our idea of the latest page number.
219          */
220         ClogCtl->shared->latest_page_number = pageno;
221
222         /*
223          * Zero out the remainder of the current clog page.  Under normal
224          * circumstances it should be zeroes already, but it seems at least
225          * theoretically possible that XLOG replay will have settled on a nextXID
226          * value that is less than the last XID actually used and marked by the
227          * previous database lifecycle (since subtransaction commit writes clog
228          * but makes no WAL entry).  Let's just be safe. (We need not worry about
229          * pages beyond the current one, since those will be zeroed when first
230          * used.  For the same reason, there is no need to do anything when
231          * nextXid is exactly at a page boundary; and it's likely that the
232          * "current" page doesn't exist yet in that case.)
233          */
234         if (TransactionIdToPgIndex(xid) != 0)
235         {
236                 int                     byteno = TransactionIdToByte(xid);
237                 int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
238                 int                     slotno;
239                 char       *byteptr;
240
241                 slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
242                 byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
243
244                 /* Zero so-far-unused positions in the current byte */
245                 *byteptr &= (1 << bshift) - 1;
246                 /* Zero the rest of the page */
247                 MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
248
249                 ClogCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
250         }
251
252         LWLockRelease(CLogControlLock);
253 }
254
255 /*
256  * This must be called ONCE during postmaster or standalone-backend shutdown
257  */
258 void
259 ShutdownCLOG(void)
260 {
261         /* Flush dirty CLOG pages to disk */
262         SimpleLruFlush(ClogCtl, false);
263 }
264
265 /*
266  * Perform a checkpoint --- either during shutdown, or on-the-fly
267  */
268 void
269 CheckPointCLOG(void)
270 {
271         /* Flush dirty CLOG pages to disk */
272         SimpleLruFlush(ClogCtl, true);
273 }
274
275
276 /*
277  * Make sure that CLOG has room for a newly-allocated XID.
278  *
279  * NB: this is called while holding XidGenLock.  We want it to be very fast
280  * most of the time; even when it's not so fast, no actual I/O need happen
281  * unless we're forced to write out a dirty clog or xlog page to make room
282  * in shared memory.
283  */
284 void
285 ExtendCLOG(TransactionId newestXact)
286 {
287         int                     pageno;
288
289         /*
290          * No work except at first XID of a page.  But beware: just after
291          * wraparound, the first XID of page zero is FirstNormalTransactionId.
292          */
293         if (TransactionIdToPgIndex(newestXact) != 0 &&
294                 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
295                 return;
296
297         pageno = TransactionIdToPage(newestXact);
298
299         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
300
301         /* Zero the page and make an XLOG entry about it */
302         ZeroCLOGPage(pageno, true);
303
304         LWLockRelease(CLogControlLock);
305 }
306
307
308 /*
309  * Remove all CLOG segments before the one holding the passed transaction ID
310  *
311  * When this is called, we know that the database logically contains no
312  * reference to transaction IDs older than oldestXact.  However, we must
313  * not truncate the CLOG until we have performed a checkpoint, to ensure
314  * that no such references remain on disk either; else a crash just after
315  * the truncation might leave us with a problem.  Since CLOG segments hold
316  * a large number of transactions, the opportunity to actually remove a
317  * segment is fairly rare, and so it seems best not to do the checkpoint
318  * unless we have confirmed that there is a removable segment.  Therefore
319  * we issue the checkpoint command here, not in higher-level code as might
320  * seem cleaner.
321  */
322 void
323 TruncateCLOG(TransactionId oldestXact)
324 {
325         int                     cutoffPage;
326
327         /*
328          * The cutoff point is the start of the segment containing oldestXact. We
329          * pass the *page* containing oldestXact to SimpleLruTruncate.
330          */
331         cutoffPage = TransactionIdToPage(oldestXact);
332
333         /* Check to see if there's any files that could be removed */
334         if (!SlruScanDirectory(ClogCtl, cutoffPage, false))
335                 return;                                 /* nothing to remove */
336
337         /* Perform a CHECKPOINT */
338         RequestCheckpoint(true, false);
339
340         /* Now we can remove the old CLOG segment(s) */
341         SimpleLruTruncate(ClogCtl, cutoffPage);
342 }
343
344
345 /*
346  * Decide which of two CLOG page numbers is "older" for truncation purposes.
347  *
348  * We need to use comparison of TransactionIds here in order to do the right
349  * thing with wraparound XID arithmetic.  However, if we are asked about
350  * page number zero, we don't want to hand InvalidTransactionId to
351  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
352  * offset both xids by FirstNormalTransactionId to avoid that.
353  */
354 static bool
355 CLOGPagePrecedes(int page1, int page2)
356 {
357         TransactionId xid1;
358         TransactionId xid2;
359
360         xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
361         xid1 += FirstNormalTransactionId;
362         xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
363         xid2 += FirstNormalTransactionId;
364
365         return TransactionIdPrecedes(xid1, xid2);
366 }
367
368
369 /*
370  * Write a ZEROPAGE xlog record
371  *
372  * Note: xlog record is marked as outside transaction control, since we
373  * want it to be redone whether the invoking transaction commits or not.
374  * (Besides which, this is normally done just before entering a transaction.)
375  */
376 static void
377 WriteZeroPageXlogRec(int pageno)
378 {
379         XLogRecData rdata;
380
381         rdata.data = (char *) (&pageno);
382         rdata.len = sizeof(int);
383         rdata.buffer = InvalidBuffer;
384         rdata.next = NULL;
385         (void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
386 }
387
388 /*
389  * CLOG resource manager's routines
390  */
391 void
392 clog_redo(XLogRecPtr lsn, XLogRecord *record)
393 {
394         uint8           info = record->xl_info & ~XLR_INFO_MASK;
395
396         if (info == CLOG_ZEROPAGE)
397         {
398                 int                     pageno;
399                 int                     slotno;
400
401                 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
402
403                 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
404
405                 slotno = ZeroCLOGPage(pageno, false);
406                 SimpleLruWritePage(ClogCtl, slotno, NULL);
407                 Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
408
409                 LWLockRelease(CLogControlLock);
410         }
411 }
412
413 void
414 clog_desc(char *buf, uint8 xl_info, char *rec)
415 {
416         uint8           info = xl_info & ~XLR_INFO_MASK;
417
418         if (info == CLOG_ZEROPAGE)
419         {
420                 int                     pageno;
421
422                 memcpy(&pageno, rec, sizeof(int));
423                 sprintf(buf + strlen(buf), "zeropage: %d", pageno);
424         }
425         else
426                 strcat(buf, "UNKNOWN");
427 }