]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/clog.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[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-2007, 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.42 2007/01/05 22:19:23 momjian Exp $
28  *
29  *-------------------------------------------------------------------------
30  */
31 #include "postgres.h"
32
33 #include "access/clog.h"
34 #include "access/slru.h"
35 #include "access/transam.h"
36 #include "postmaster/bgwriter.h"
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 static void WriteTruncateXlogRec(int pageno);
73
74
75 /*
76  * Record the final state of a transaction in the commit log.
77  *
78  * NB: this is a low-level routine and is NOT the preferred entry point
79  * for most uses; TransactionLogUpdate() in transam.c is the intended caller.
80  */
81 void
82 TransactionIdSetStatus(TransactionId xid, XidStatus status)
83 {
84         int                     pageno = TransactionIdToPage(xid);
85         int                     byteno = TransactionIdToByte(xid);
86         int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
87         int                     slotno;
88         char       *byteptr;
89         char            byteval;
90
91         Assert(status == TRANSACTION_STATUS_COMMITTED ||
92                    status == TRANSACTION_STATUS_ABORTED ||
93                    status == TRANSACTION_STATUS_SUB_COMMITTED);
94
95         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
96
97         slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
98         byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
99
100         /* Current state should be 0, subcommitted or target state */
101         Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
102                    ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
103                    ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
104
105         /* note this assumes exclusive access to the clog page */
106         byteval = *byteptr;
107         byteval &= ~(((1 << CLOG_BITS_PER_XACT) - 1) << bshift);
108         byteval |= (status << bshift);
109         *byteptr = byteval;
110
111         ClogCtl->shared->page_dirty[slotno] = true;
112
113         LWLockRelease(CLogControlLock);
114 }
115
116 /*
117  * Interrogate the state of a transaction in the commit log.
118  *
119  * NB: this is a low-level routine and is NOT the preferred entry point
120  * for most uses; TransactionLogFetch() in transam.c is the intended caller.
121  */
122 XidStatus
123 TransactionIdGetStatus(TransactionId xid)
124 {
125         int                     pageno = TransactionIdToPage(xid);
126         int                     byteno = TransactionIdToByte(xid);
127         int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
128         int                     slotno;
129         char       *byteptr;
130         XidStatus       status;
131
132         /* lock is acquired by SimpleLruReadPage_ReadOnly */
133
134         slotno = SimpleLruReadPage_ReadOnly(ClogCtl, pageno, xid);
135         byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
136
137         status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
138
139         LWLockRelease(CLogControlLock);
140
141         return status;
142 }
143
144
145 /*
146  * Initialization of shared memory for CLOG
147  */
148 Size
149 CLOGShmemSize(void)
150 {
151         return SimpleLruShmemSize(NUM_CLOG_BUFFERS);
152 }
153
154 void
155 CLOGShmemInit(void)
156 {
157         ClogCtl->PagePrecedes = CLOGPagePrecedes;
158         SimpleLruInit(ClogCtl, "CLOG Ctl", NUM_CLOG_BUFFERS,
159                                   CLogControlLock, "pg_clog");
160 }
161
162 /*
163  * This func must be called ONCE on system install.  It creates
164  * the initial CLOG segment.  (The CLOG directory is assumed to
165  * have been created by the initdb shell script, and CLOGShmemInit
166  * must have been called already.)
167  */
168 void
169 BootStrapCLOG(void)
170 {
171         int                     slotno;
172
173         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
174
175         /* Create and zero the first page of the commit log */
176         slotno = ZeroCLOGPage(0, false);
177
178         /* Make sure it's written out */
179         SimpleLruWritePage(ClogCtl, slotno, NULL);
180         Assert(!ClogCtl->shared->page_dirty[slotno]);
181
182         LWLockRelease(CLogControlLock);
183 }
184
185 /*
186  * Initialize (or reinitialize) a page of CLOG to zeroes.
187  * If writeXlog is TRUE, also emit an XLOG record saying we did this.
188  *
189  * The page is not actually written, just set up in shared memory.
190  * The slot number of the new page is returned.
191  *
192  * Control lock must be held at entry, and will be held at exit.
193  */
194 static int
195 ZeroCLOGPage(int pageno, bool writeXlog)
196 {
197         int                     slotno;
198
199         slotno = SimpleLruZeroPage(ClogCtl, pageno);
200
201         if (writeXlog)
202                 WriteZeroPageXlogRec(pageno);
203
204         return slotno;
205 }
206
207 /*
208  * This must be called ONCE during postmaster or standalone-backend startup,
209  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
210  */
211 void
212 StartupCLOG(void)
213 {
214         TransactionId xid = ShmemVariableCache->nextXid;
215         int                     pageno = TransactionIdToPage(xid);
216
217         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
218
219         /*
220          * Initialize our idea of the latest page number.
221          */
222         ClogCtl->shared->latest_page_number = pageno;
223
224         /*
225          * Zero out the remainder of the current clog page.  Under normal
226          * circumstances it should be zeroes already, but it seems at least
227          * theoretically possible that XLOG replay will have settled on a nextXID
228          * value that is less than the last XID actually used and marked by the
229          * previous database lifecycle (since subtransaction commit writes clog
230          * but makes no WAL entry).  Let's just be safe. (We need not worry about
231          * pages beyond the current one, since those will be zeroed when first
232          * used.  For the same reason, there is no need to do anything when
233          * nextXid is exactly at a page boundary; and it's likely that the
234          * "current" page doesn't exist yet in that case.)
235          */
236         if (TransactionIdToPgIndex(xid) != 0)
237         {
238                 int                     byteno = TransactionIdToByte(xid);
239                 int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
240                 int                     slotno;
241                 char       *byteptr;
242
243                 slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
244                 byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
245
246                 /* Zero so-far-unused positions in the current byte */
247                 *byteptr &= (1 << bshift) - 1;
248                 /* Zero the rest of the page */
249                 MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
250
251                 ClogCtl->shared->page_dirty[slotno] = true;
252         }
253
254         LWLockRelease(CLogControlLock);
255 }
256
257 /*
258  * This must be called ONCE during postmaster or standalone-backend shutdown
259  */
260 void
261 ShutdownCLOG(void)
262 {
263         /* Flush dirty CLOG pages to disk */
264         SimpleLruFlush(ClogCtl, false);
265 }
266
267 /*
268  * Perform a checkpoint --- either during shutdown, or on-the-fly
269  */
270 void
271 CheckPointCLOG(void)
272 {
273         /* Flush dirty CLOG pages to disk */
274         SimpleLruFlush(ClogCtl, true);
275 }
276
277
278 /*
279  * Make sure that CLOG has room for a newly-allocated XID.
280  *
281  * NB: this is called while holding XidGenLock.  We want it to be very fast
282  * most of the time; even when it's not so fast, no actual I/O need happen
283  * unless we're forced to write out a dirty clog or xlog page to make room
284  * in shared memory.
285  */
286 void
287 ExtendCLOG(TransactionId newestXact)
288 {
289         int                     pageno;
290
291         /*
292          * No work except at first XID of a page.  But beware: just after
293          * wraparound, the first XID of page zero is FirstNormalTransactionId.
294          */
295         if (TransactionIdToPgIndex(newestXact) != 0 &&
296                 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
297                 return;
298
299         pageno = TransactionIdToPage(newestXact);
300
301         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
302
303         /* Zero the page and make an XLOG entry about it */
304         ZeroCLOGPage(pageno, true);
305
306         LWLockRelease(CLogControlLock);
307 }
308
309
310 /*
311  * Remove all CLOG segments before the one holding the passed transaction ID
312  *
313  * Before removing any CLOG data, we must flush XLOG to disk, to ensure
314  * that any recently-emitted HEAP_FREEZE records have reached disk; otherwise
315  * a crash and restart might leave us with some unfrozen tuples referencing
316  * removed CLOG data.  We choose to emit a special TRUNCATE XLOG record too.
317  * Replaying the deletion from XLOG is not critical, since the files could
318  * just as well be removed later, but doing so prevents a long-running hot
319  * standby server from acquiring an unreasonably bloated CLOG directory.
320  *
321  * Since CLOG segments hold a large number of transactions, the opportunity to
322  * actually remove a segment is fairly rare, and so it seems best not to do
323  * the XLOG flush unless we have confirmed that there is a removable segment.
324  */
325 void
326 TruncateCLOG(TransactionId oldestXact)
327 {
328         int                     cutoffPage;
329
330         /*
331          * The cutoff point is the start of the segment containing oldestXact. We
332          * pass the *page* containing oldestXact to SimpleLruTruncate.
333          */
334         cutoffPage = TransactionIdToPage(oldestXact);
335
336         /* Check to see if there's any files that could be removed */
337         if (!SlruScanDirectory(ClogCtl, cutoffPage, false))
338                 return;                                 /* nothing to remove */
339
340         /* Write XLOG record and flush XLOG to disk */
341         WriteTruncateXlogRec(cutoffPage);
342
343         /* Now we can remove the old CLOG segment(s) */
344         SimpleLruTruncate(ClogCtl, cutoffPage);
345 }
346
347
348 /*
349  * Decide which of two CLOG page numbers is "older" for truncation purposes.
350  *
351  * We need to use comparison of TransactionIds here in order to do the right
352  * thing with wraparound XID arithmetic.  However, if we are asked about
353  * page number zero, we don't want to hand InvalidTransactionId to
354  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
355  * offset both xids by FirstNormalTransactionId to avoid that.
356  */
357 static bool
358 CLOGPagePrecedes(int page1, int page2)
359 {
360         TransactionId xid1;
361         TransactionId xid2;
362
363         xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
364         xid1 += FirstNormalTransactionId;
365         xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
366         xid2 += FirstNormalTransactionId;
367
368         return TransactionIdPrecedes(xid1, xid2);
369 }
370
371
372 /*
373  * Write a ZEROPAGE xlog record
374  *
375  * Note: xlog record is marked as outside transaction control, since we
376  * want it to be redone whether the invoking transaction commits or not.
377  * (Besides which, this is normally done just before entering a transaction.)
378  */
379 static void
380 WriteZeroPageXlogRec(int pageno)
381 {
382         XLogRecData rdata;
383
384         rdata.data = (char *) (&pageno);
385         rdata.len = sizeof(int);
386         rdata.buffer = InvalidBuffer;
387         rdata.next = NULL;
388         (void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
389 }
390
391 /*
392  * Write a TRUNCATE xlog record
393  *
394  * We must flush the xlog record to disk before returning --- see notes
395  * in TruncateCLOG().
396  *
397  * Note: xlog record is marked as outside transaction control, since we
398  * want it to be redone whether the invoking transaction commits or not.
399  */
400 static void
401 WriteTruncateXlogRec(int pageno)
402 {
403         XLogRecData rdata;
404         XLogRecPtr      recptr;
405
406         rdata.data = (char *) (&pageno);
407         rdata.len = sizeof(int);
408         rdata.buffer = InvalidBuffer;
409         rdata.next = NULL;
410         recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE | XLOG_NO_TRAN, &rdata);
411         XLogFlush(recptr);
412 }
413
414 /*
415  * CLOG resource manager's routines
416  */
417 void
418 clog_redo(XLogRecPtr lsn, XLogRecord *record)
419 {
420         uint8           info = record->xl_info & ~XLR_INFO_MASK;
421
422         if (info == CLOG_ZEROPAGE)
423         {
424                 int                     pageno;
425                 int                     slotno;
426
427                 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
428
429                 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
430
431                 slotno = ZeroCLOGPage(pageno, false);
432                 SimpleLruWritePage(ClogCtl, slotno, NULL);
433                 Assert(!ClogCtl->shared->page_dirty[slotno]);
434
435                 LWLockRelease(CLogControlLock);
436         }
437         else if (info == CLOG_TRUNCATE)
438         {
439                 int                     pageno;
440
441                 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
442
443                 /*
444                  * During XLOG replay, latest_page_number isn't set up yet; insert
445                  * a suitable value to bypass the sanity test in SimpleLruTruncate.
446                  */
447                 ClogCtl->shared->latest_page_number = pageno;
448
449                 SimpleLruTruncate(ClogCtl, pageno);
450         }
451         else
452                 elog(PANIC, "clog_redo: unknown op code %u", info);
453 }
454
455 void
456 clog_desc(StringInfo buf, uint8 xl_info, char *rec)
457 {
458         uint8           info = xl_info & ~XLR_INFO_MASK;
459
460         if (info == CLOG_ZEROPAGE)
461         {
462                 int                     pageno;
463
464                 memcpy(&pageno, rec, sizeof(int));
465                 appendStringInfo(buf, "zeropage: %d", pageno);
466         }
467         else if (info == CLOG_TRUNCATE)
468         {
469                 int                     pageno;
470
471                 memcpy(&pageno, rec, sizeof(int));
472                 appendStringInfo(buf, "truncate before: %d", pageno);
473         }
474         else
475                 appendStringInfo(buf, "UNKNOWN");
476 }