]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/clog.c
Rearrange pg_subtrans handling as per recent discussion. pg_subtrans
[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-2003, 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.23 2004/08/23 23:22:44 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 #define ClogCtl (&ClogCtlData)
66
67
68 static int      ZeroCLOGPage(int pageno, bool writeXlog);
69 static bool CLOGPagePrecedes(int page1, int page2);
70 static void WriteZeroPageXlogRec(int pageno);
71
72
73 /*
74  * Record the final state of a transaction in the commit log.
75  *
76  * NB: this is a low-level routine and is NOT the preferred entry point
77  * for most uses; TransactionLogUpdate() in transam.c is the intended caller.
78  */
79 void
80 TransactionIdSetStatus(TransactionId xid, XidStatus status)
81 {
82         int                     pageno = TransactionIdToPage(xid);
83         int                     byteno = TransactionIdToByte(xid);
84         int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
85         int                     slotno;
86         char       *byteptr;
87         char            byteval;
88
89         Assert(status == TRANSACTION_STATUS_COMMITTED ||
90                    status == TRANSACTION_STATUS_ABORTED ||
91                    status == TRANSACTION_STATUS_SUB_COMMITTED);
92
93         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
94
95         slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
96         byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
97
98         /* Current state should be 0, subcommitted or target state */
99         Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
100                    ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
101                    ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
102
103         /* note this assumes exclusive access to the clog page */
104         byteval = *byteptr;
105         byteval &= ~(((1 << CLOG_BITS_PER_XACT) - 1) << bshift);
106         byteval |= (status << bshift);
107         *byteptr = byteval;
108
109         ClogCtl->shared->page_status[slotno] = SLRU_PAGE_DIRTY;
110
111         LWLockRelease(CLogControlLock);
112 }
113
114 /*
115  * Interrogate the state of a transaction in the commit log.
116  *
117  * NB: this is a low-level routine and is NOT the preferred entry point
118  * for most uses; TransactionLogFetch() in transam.c is the intended caller.
119  */
120 XidStatus
121 TransactionIdGetStatus(TransactionId xid)
122 {
123         int                     pageno = TransactionIdToPage(xid);
124         int                     byteno = TransactionIdToByte(xid);
125         int                     bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
126         int                     slotno;
127         char       *byteptr;
128         XidStatus       status;
129
130         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
131
132         slotno = SimpleLruReadPage(ClogCtl, pageno, xid);
133         byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
134
135         status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
136
137         LWLockRelease(CLogControlLock);
138
139         return status;
140 }
141
142
143 /*
144  * Initialization of shared memory for CLOG
145  */
146
147 int
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         /*
213          * Initialize our idea of the latest page number.
214          */
215         ClogCtl->shared->latest_page_number = TransactionIdToPage(ShmemVariableCache->nextXid);
216 }
217
218 /*
219  * This must be called ONCE during postmaster or standalone-backend shutdown
220  */
221 void
222 ShutdownCLOG(void)
223 {
224         /* Flush dirty CLOG pages to disk */
225         SimpleLruFlush(ClogCtl, false);
226 }
227
228 /*
229  * Perform a checkpoint --- either during shutdown, or on-the-fly
230  */
231 void
232 CheckPointCLOG(void)
233 {
234         /* Flush dirty CLOG pages to disk */
235         SimpleLruFlush(ClogCtl, true);
236 }
237
238
239 /*
240  * Make sure that CLOG has room for a newly-allocated XID.
241  *
242  * NB: this is called while holding XidGenLock.  We want it to be very fast
243  * most of the time; even when it's not so fast, no actual I/O need happen
244  * unless we're forced to write out a dirty clog or xlog page to make room
245  * in shared memory.
246  */
247 void
248 ExtendCLOG(TransactionId newestXact)
249 {
250         int                     pageno;
251
252         /*
253          * No work except at first XID of a page.  But beware: just after
254          * wraparound, the first XID of page zero is FirstNormalTransactionId.
255          */
256         if (TransactionIdToPgIndex(newestXact) != 0 &&
257                 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
258                 return;
259
260         pageno = TransactionIdToPage(newestXact);
261
262         LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
263
264         /* Zero the page and make an XLOG entry about it */
265         ZeroCLOGPage(pageno, true);
266
267         LWLockRelease(CLogControlLock);
268 }
269
270
271 /*
272  * Remove all CLOG segments before the one holding the passed transaction ID
273  *
274  * When this is called, we know that the database logically contains no
275  * reference to transaction IDs older than oldestXact.  However, we must
276  * not truncate the CLOG until we have performed a checkpoint, to ensure
277  * that no such references remain on disk either; else a crash just after
278  * the truncation might leave us with a problem.  Since CLOG segments hold
279  * a large number of transactions, the opportunity to actually remove a
280  * segment is fairly rare, and so it seems best not to do the checkpoint
281  * unless we have confirmed that there is a removable segment.  Therefore
282  * we issue the checkpoint command here, not in higher-level code as might
283  * seem cleaner.
284  */
285 void
286 TruncateCLOG(TransactionId oldestXact)
287 {
288         int                     cutoffPage;
289
290         /*
291          * The cutoff point is the start of the segment containing oldestXact.
292          * We pass the *page* containing oldestXact to SimpleLruTruncate.
293          */
294         cutoffPage = TransactionIdToPage(oldestXact);
295
296         /* Check to see if there's any files that could be removed */
297         if (!SlruScanDirectory(ClogCtl, cutoffPage, false))
298                 return;                                 /* nothing to remove */
299
300         /* Perform a CHECKPOINT */
301         RequestCheckpoint(true);
302
303         /* Now we can remove the old CLOG segment(s) */
304         SimpleLruTruncate(ClogCtl, cutoffPage);
305 }
306
307
308 /*
309  * Decide which of two CLOG page numbers is "older" for truncation purposes.
310  *
311  * We need to use comparison of TransactionIds here in order to do the right
312  * thing with wraparound XID arithmetic.  However, if we are asked about
313  * page number zero, we don't want to hand InvalidTransactionId to
314  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
315  * offset both xids by FirstNormalTransactionId to avoid that.
316  */
317 static bool
318 CLOGPagePrecedes(int page1, int page2)
319 {
320         TransactionId xid1;
321         TransactionId xid2;
322
323         xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
324         xid1 += FirstNormalTransactionId;
325         xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
326         xid2 += FirstNormalTransactionId;
327
328         return TransactionIdPrecedes(xid1, xid2);
329 }
330
331
332 /*
333  * Write a ZEROPAGE xlog record
334  *
335  * Note: xlog record is marked as outside transaction control, since we
336  * want it to be redone whether the invoking transaction commits or not.
337  * (Besides which, this is normally done just before entering a transaction.)
338  */
339 static void
340 WriteZeroPageXlogRec(int pageno)
341 {
342         XLogRecData rdata;
343
344         rdata.buffer = InvalidBuffer;
345         rdata.data = (char *) (&pageno);
346         rdata.len = sizeof(int);
347         rdata.next = NULL;
348         (void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
349 }
350
351 /*
352  * CLOG resource manager's routines
353  */
354 void
355 clog_redo(XLogRecPtr lsn, XLogRecord *record)
356 {
357         uint8           info = record->xl_info & ~XLR_INFO_MASK;
358
359         if (info == CLOG_ZEROPAGE)
360         {
361                 int                     pageno;
362                 int                     slotno;
363
364                 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
365
366                 LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
367
368                 slotno = ZeroCLOGPage(pageno, false);
369                 SimpleLruWritePage(ClogCtl, slotno, NULL);
370                 Assert(ClogCtl->shared->page_status[slotno] == SLRU_PAGE_CLEAN);
371
372                 LWLockRelease(CLogControlLock);
373         }
374 }
375
376 void
377 clog_undo(XLogRecPtr lsn, XLogRecord *record)
378 {
379 }
380
381 void
382 clog_desc(char *buf, uint8 xl_info, char *rec)
383 {
384         uint8           info = xl_info & ~XLR_INFO_MASK;
385
386         if (info == CLOG_ZEROPAGE)
387         {
388                 int                     pageno;
389
390                 memcpy(&pageno, rec, sizeof(int));
391                 sprintf(buf + strlen(buf), "zeropage: %d", pageno);
392         }
393         else
394                 strcat(buf, "UNKNOWN");
395 }