PostgreSQL Source Code git master
clog.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * clog.c
4 * PostgreSQL transaction-commit-log manager
5 *
6 * This module stores two bits per transaction regarding its commit/abort
7 * status; the status for four transactions fit in a byte.
8 *
9 * This would be a pretty simple abstraction on top of slru.c, except that
10 * for performance reasons we allow multiple transactions that are
11 * committing concurrently to form a queue, so that a single process can
12 * update the status for all of them within a single lock acquisition run.
13 *
14 * XLOG interactions: this module generates an XLOG record whenever a new
15 * CLOG page is initialized to zeroes. Other writes of CLOG come from
16 * recording of transaction commit or abort in xact.c, which generates its
17 * own XLOG records for these events and will re-perform the status update
18 * on redo; so we need make no additional XLOG entry here. For synchronous
19 * transaction commits, the XLOG is guaranteed flushed through the XLOG commit
20 * record before we are called to log a commit, so the WAL rule "write xlog
21 * before data" is satisfied automatically. However, for async commits we
22 * must track the latest LSN affecting each CLOG page, so that we can flush
23 * XLOG that far and satisfy the WAL rule. We don't have to worry about this
24 * for aborts (whether sync or async), since the post-crash assumption would
25 * be that such transactions failed anyway.
26 *
27 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
28 * Portions Copyright (c) 1994, Regents of the University of California
29 *
30 * src/backend/access/transam/clog.c
31 *
32 *-------------------------------------------------------------------------
33 */
34#include "postgres.h"
35
36#include "access/clog.h"
37#include "access/slru.h"
38#include "access/transam.h"
39#include "access/xlog.h"
40#include "access/xloginsert.h"
41#include "access/xlogutils.h"
42#include "miscadmin.h"
43#include "pg_trace.h"
44#include "pgstat.h"
45#include "storage/proc.h"
46#include "storage/sync.h"
47#include "utils/guc_hooks.h"
48
49/*
50 * Defines for CLOG page sizes. A page is the same BLCKSZ as is used
51 * everywhere else in Postgres.
52 *
53 * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
54 * CLOG page numbering also wraps around at 0xFFFFFFFF/CLOG_XACTS_PER_PAGE,
55 * and CLOG segment numbering at
56 * 0xFFFFFFFF/CLOG_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need take no
57 * explicit notice of that fact in this module, except when comparing segment
58 * and page numbers in TruncateCLOG (see CLOGPagePrecedes).
59 */
60
61/* We need two bits per xact, so four xacts fit in a byte */
62#define CLOG_BITS_PER_XACT 2
63#define CLOG_XACTS_PER_BYTE 4
64#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
65#define CLOG_XACT_BITMASK ((1 << CLOG_BITS_PER_XACT) - 1)
66
67/*
68 * Because space used in CLOG by each transaction is so small, we place a
69 * smaller limit on the number of CLOG buffers than SLRU allows. No other
70 * SLRU needs this.
71 */
72#define CLOG_MAX_ALLOWED_BUFFERS \
73 Min(SLRU_MAX_ALLOWED_BUFFERS, \
74 (((MaxTransactionId / 2) + (CLOG_XACTS_PER_PAGE - 1)) / CLOG_XACTS_PER_PAGE))
75
76
77/*
78 * Although we return an int64 the actual value can't currently exceed
79 * 0xFFFFFFFF/CLOG_XACTS_PER_PAGE.
80 */
81static inline int64
83{
84 return xid / (int64) CLOG_XACTS_PER_PAGE;
85}
86
87#define TransactionIdToPgIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_PAGE)
88#define TransactionIdToByte(xid) (TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE)
89#define TransactionIdToBIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_BYTE)
90
91/* We store the latest async LSN for each group of transactions */
92#define CLOG_XACTS_PER_LSN_GROUP 32 /* keep this a power of 2 */
93#define CLOG_LSNS_PER_PAGE (CLOG_XACTS_PER_PAGE / CLOG_XACTS_PER_LSN_GROUP)
94
95#define GetLSNIndex(slotno, xid) ((slotno) * CLOG_LSNS_PER_PAGE + \
96 ((xid) % (TransactionId) CLOG_XACTS_PER_PAGE) / CLOG_XACTS_PER_LSN_GROUP)
97
98/*
99 * The number of subtransactions below which we consider to apply clog group
100 * update optimization. Testing reveals that the number higher than this can
101 * hurt performance.
102 */
103#define THRESHOLD_SUBTRANS_CLOG_OPT 5
104
105/*
106 * Link to shared-memory data structures for CLOG control
107 */
109
110#define XactCtl (&XactCtlData)
111
112
113static int ZeroCLOGPage(int64 pageno, bool writeXlog);
114static bool CLOGPagePrecedes(int64 page1, int64 page2);
115static void WriteZeroPageXlogRec(int64 pageno);
116static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXact,
117 Oid oldestXactDb);
118static void TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
119 TransactionId *subxids, XidStatus status,
120 XLogRecPtr lsn, int64 pageno,
121 bool all_xact_same_page);
123 XLogRecPtr lsn, int slotno);
124static void set_status_by_pages(int nsubxids, TransactionId *subxids,
125 XidStatus status, XLogRecPtr lsn);
127 XidStatus status, XLogRecPtr lsn, int64 pageno);
128static void TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
129 TransactionId *subxids, XidStatus status,
130 XLogRecPtr lsn, int64 pageno);
131
132
133/*
134 * TransactionIdSetTreeStatus
135 *
136 * Record the final state of transaction entries in the commit log for
137 * a transaction and its subtransaction tree. Take care to ensure this is
138 * efficient, and as atomic as possible.
139 *
140 * xid is a single xid to set status for. This will typically be
141 * the top level transactionid for a top level commit or abort. It can
142 * also be a subtransaction when we record transaction aborts.
143 *
144 * subxids is an array of xids of length nsubxids, representing subtransactions
145 * in the tree of xid. In various cases nsubxids may be zero.
146 *
147 * lsn must be the WAL location of the commit record when recording an async
148 * commit. For a synchronous commit it can be InvalidXLogRecPtr, since the
149 * caller guarantees the commit record is already flushed in that case. It
150 * should be InvalidXLogRecPtr for abort cases, too.
151 *
152 * In the commit case, atomicity is limited by whether all the subxids are in
153 * the same CLOG page as xid. If they all are, then the lock will be grabbed
154 * only once, and the status will be set to committed directly. Otherwise
155 * we must
156 * 1. set sub-committed all subxids that are not on the same page as the
157 * main xid
158 * 2. atomically set committed the main xid and the subxids on the same page
159 * 3. go over the first bunch again and set them committed
160 * Note that as far as concurrent checkers are concerned, main transaction
161 * commit as a whole is still atomic.
162 *
163 * Example:
164 * TransactionId t commits and has subxids t1, t2, t3, t4
165 * t is on page p1, t1 is also on p1, t2 and t3 are on p2, t4 is on p3
166 * 1. update pages2-3:
167 * page2: set t2,t3 as sub-committed
168 * page3: set t4 as sub-committed
169 * 2. update page1:
170 * page1: set t,t1 as committed
171 * 3. update pages2-3:
172 * page2: set t2,t3 as committed
173 * page3: set t4 as committed
174 *
175 * NB: this is a low-level routine and is NOT the preferred entry point
176 * for most uses; functions in transam.c are the intended callers.
177 *
178 * XXX Think about issuing POSIX_FADV_WILLNEED on pages that we will need,
179 * but aren't yet in cache, as well as hinting pages not to fall out of
180 * cache yet.
181 */
182void
184 TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
185{
186 int64 pageno = TransactionIdToPage(xid); /* get page of parent */
187 int i;
188
191
192 /*
193 * See how many subxids, if any, are on the same page as the parent, if
194 * any.
195 */
196 for (i = 0; i < nsubxids; i++)
197 {
198 if (TransactionIdToPage(subxids[i]) != pageno)
199 break;
200 }
201
202 /*
203 * Do all items fit on a single page?
204 */
205 if (i == nsubxids)
206 {
207 /*
208 * Set the parent and all subtransactions in a single call
209 */
210 TransactionIdSetPageStatus(xid, nsubxids, subxids, status, lsn,
211 pageno, true);
212 }
213 else
214 {
215 int nsubxids_on_first_page = i;
216
217 /*
218 * If this is a commit then we care about doing this correctly (i.e.
219 * using the subcommitted intermediate status). By here, we know
220 * we're updating more than one page of clog, so we must mark entries
221 * that are *not* on the first page so that they show as subcommitted
222 * before we then return to update the status to fully committed.
223 *
224 * To avoid touching the first page twice, skip marking subcommitted
225 * for the subxids on that first page.
226 */
227 if (status == TRANSACTION_STATUS_COMMITTED)
228 set_status_by_pages(nsubxids - nsubxids_on_first_page,
229 subxids + nsubxids_on_first_page,
231
232 /*
233 * Now set the parent and subtransactions on same page as the parent,
234 * if any
235 */
236 pageno = TransactionIdToPage(xid);
237 TransactionIdSetPageStatus(xid, nsubxids_on_first_page, subxids, status,
238 lsn, pageno, false);
239
240 /*
241 * Now work through the rest of the subxids one clog page at a time,
242 * starting from the second page onwards, like we did above.
243 */
244 set_status_by_pages(nsubxids - nsubxids_on_first_page,
245 subxids + nsubxids_on_first_page,
246 status, lsn);
247 }
248}
249
250/*
251 * Helper for TransactionIdSetTreeStatus: set the status for a bunch of
252 * transactions, chunking in the separate CLOG pages involved. We never
253 * pass the whole transaction tree to this function, only subtransactions
254 * that are on different pages to the top level transaction id.
255 */
256static void
257set_status_by_pages(int nsubxids, TransactionId *subxids,
258 XidStatus status, XLogRecPtr lsn)
259{
260 int64 pageno = TransactionIdToPage(subxids[0]);
261 int offset = 0;
262 int i = 0;
263
264 Assert(nsubxids > 0); /* else the pageno fetch above is unsafe */
265
266 while (i < nsubxids)
267 {
268 int num_on_page = 0;
269 int64 nextpageno;
270
271 do
272 {
273 nextpageno = TransactionIdToPage(subxids[i]);
274 if (nextpageno != pageno)
275 break;
276 num_on_page++;
277 i++;
278 } while (i < nsubxids);
279
281 num_on_page, subxids + offset,
282 status, lsn, pageno, false);
283 offset = i;
284 pageno = nextpageno;
285 }
286}
287
288/*
289 * Record the final state of transaction entries in the commit log for all
290 * entries on a single page. Atomic only on this page.
291 */
292static void
294 TransactionId *subxids, XidStatus status,
295 XLogRecPtr lsn, int64 pageno,
296 bool all_xact_same_page)
297{
298 LWLock *lock;
299
300 /* Can't use group update when PGPROC overflows. */
302 "group clog threshold less than PGPROC cached subxids");
303
304 /* Get the SLRU bank lock for the page we are going to access. */
305 lock = SimpleLruGetBankLock(XactCtl, pageno);
306
307 /*
308 * When there is contention on the SLRU bank lock we need, we try to group
309 * multiple updates; a single leader process will perform transaction
310 * status updates for multiple backends so that the number of times the
311 * bank lock needs to be acquired is reduced.
312 *
313 * For this optimization to be safe, the XID and subxids in MyProc must be
314 * the same as the ones for which we're setting the status. Check that
315 * this is the case.
316 *
317 * For this optimization to be efficient, we shouldn't have too many
318 * sub-XIDs and all of the XIDs for which we're adjusting clog should be
319 * on the same page. Check those conditions, too.
320 */
321 if (all_xact_same_page && xid == MyProc->xid &&
322 nsubxids <= THRESHOLD_SUBTRANS_CLOG_OPT &&
323 nsubxids == MyProc->subxidStatus.count &&
324 (nsubxids == 0 ||
325 memcmp(subxids, MyProc->subxids.xids,
326 nsubxids * sizeof(TransactionId)) == 0))
327 {
328 /*
329 * If we can immediately acquire the lock, we update the status of our
330 * own XID and release the lock. If not, try use group XID update. If
331 * that doesn't work out, fall back to waiting for the lock to perform
332 * an update for this transaction only.
333 */
335 {
336 /* Got the lock without waiting! Do the update. */
337 TransactionIdSetPageStatusInternal(xid, nsubxids, subxids, status,
338 lsn, pageno);
339 LWLockRelease(lock);
340 return;
341 }
342 else if (TransactionGroupUpdateXidStatus(xid, status, lsn, pageno))
343 {
344 /* Group update mechanism has done the work. */
345 return;
346 }
347
348 /* Fall through only if update isn't done yet. */
349 }
350
351 /* Group update not applicable, or couldn't accept this page number. */
353 TransactionIdSetPageStatusInternal(xid, nsubxids, subxids, status,
354 lsn, pageno);
355 LWLockRelease(lock);
356}
357
358/*
359 * Record the final state of transaction entry in the commit log
360 *
361 * We don't do any locking here; caller must handle that.
362 */
363static void
365 TransactionId *subxids, XidStatus status,
366 XLogRecPtr lsn, int64 pageno)
367{
368 int slotno;
369 int i;
370
372 status == TRANSACTION_STATUS_ABORTED ||
375 LW_EXCLUSIVE));
376
377 /*
378 * If we're doing an async commit (ie, lsn is valid), then we must wait
379 * for any active write on the page slot to complete. Otherwise our
380 * update could reach disk in that write, which will not do since we
381 * mustn't let it reach disk until we've done the appropriate WAL flush.
382 * But when lsn is invalid, it's OK to scribble on a page while it is
383 * write-busy, since we don't care if the update reaches disk sooner than
384 * we think.
385 */
386 slotno = SimpleLruReadPage(XactCtl, pageno, XLogRecPtrIsInvalid(lsn), xid);
387
388 /*
389 * Set the main transaction id, if any.
390 *
391 * If we update more than one xid on this page while it is being written
392 * out, we might find that some of the bits go to disk and others don't.
393 * If we are updating commits on the page with the top-level xid that
394 * could break atomicity, so we subcommit the subxids first before we mark
395 * the top-level commit.
396 */
397 if (TransactionIdIsValid(xid))
398 {
399 /* Subtransactions first, if needed ... */
400 if (status == TRANSACTION_STATUS_COMMITTED)
401 {
402 for (i = 0; i < nsubxids; i++)
403 {
404 Assert(XactCtl->shared->page_number[slotno] == TransactionIdToPage(subxids[i]));
407 lsn, slotno);
408 }
409 }
410
411 /* ... then the main transaction */
412 TransactionIdSetStatusBit(xid, status, lsn, slotno);
413 }
414
415 /* Set the subtransactions */
416 for (i = 0; i < nsubxids; i++)
417 {
418 Assert(XactCtl->shared->page_number[slotno] == TransactionIdToPage(subxids[i]));
419 TransactionIdSetStatusBit(subxids[i], status, lsn, slotno);
420 }
421
422 XactCtl->shared->page_dirty[slotno] = true;
423}
424
425/*
426 * Subroutine for TransactionIdSetPageStatus, q.v.
427 *
428 * When we cannot immediately acquire the SLRU bank lock in exclusive mode at
429 * commit time, add ourselves to a list of processes that need their XIDs
430 * status update. The first process to add itself to the list will acquire
431 * the lock in exclusive mode and set transaction status as required on behalf
432 * of all group members. This avoids a great deal of contention when many
433 * processes are trying to commit at once, since the lock need not be
434 * repeatedly handed off from one committing process to the next.
435 *
436 * Returns true when transaction status has been updated in clog; returns
437 * false if we decided against applying the optimization because the page
438 * number we need to update differs from those processes already waiting.
439 */
440static bool
442 XLogRecPtr lsn, int64 pageno)
443{
444 volatile PROC_HDR *procglobal = ProcGlobal;
445 PGPROC *proc = MyProc;
446 uint32 nextidx;
447 uint32 wakeidx;
448 int64 prevpageno;
449 LWLock *prevlock = NULL;
450
451 /* We should definitely have an XID whose status needs to be updated. */
453
454 /*
455 * Prepare to add ourselves to the list of processes needing a group XID
456 * status update.
457 */
458 proc->clogGroupMember = true;
459 proc->clogGroupMemberXid = xid;
460 proc->clogGroupMemberXidStatus = status;
461 proc->clogGroupMemberPage = pageno;
462 proc->clogGroupMemberLsn = lsn;
463
464 /*
465 * We put ourselves in the queue by writing MyProcNumber to
466 * ProcGlobal->clogGroupFirst. However, if there's already a process
467 * listed there, we compare our pageno with that of that process; if it
468 * differs, we cannot participate in the group, so we return for caller to
469 * update pg_xact in the normal way.
470 *
471 * If we're not the first process in the list, we must follow the leader.
472 * We do this by storing the data we want updated in our PGPROC entry
473 * where the leader can find it, then going to sleep.
474 *
475 * If no process is already in the list, we're the leader; our first step
476 * is to lock the SLRU bank to which our page belongs, then we close out
477 * the group by resetting the list pointer from ProcGlobal->clogGroupFirst
478 * (this lets other processes set up other groups later); finally we do
479 * the SLRU updates, release the SLRU bank lock, and wake up the sleeping
480 * processes.
481 *
482 * If another group starts to update a page in a different SLRU bank, they
483 * can proceed concurrently, since the bank lock they're going to use is
484 * different from ours. If another group starts to update a page in the
485 * same bank as ours, they wait until we release the lock.
486 */
487 nextidx = pg_atomic_read_u32(&procglobal->clogGroupFirst);
488
489 while (true)
490 {
491 /*
492 * Add the proc to list, if the clog page where we need to update the
493 * current transaction status is same as group leader's clog page.
494 *
495 * There is a race condition here, which is that after doing the below
496 * check and before adding this proc's clog update to a group, the
497 * group leader might have already finished the group update for this
498 * page and becomes group leader of another group, updating a
499 * different page. This will lead to a situation where a single group
500 * can have different clog page updates. This isn't likely and will
501 * still work, just less efficiently -- we handle this case by
502 * switching to a different bank lock in the loop below.
503 */
504 if (nextidx != INVALID_PROC_NUMBER &&
505 GetPGProcByNumber(nextidx)->clogGroupMemberPage != proc->clogGroupMemberPage)
506 {
507 /*
508 * Ensure that this proc is not a member of any clog group that
509 * needs an XID status update.
510 */
511 proc->clogGroupMember = false;
513 return false;
514 }
515
516 pg_atomic_write_u32(&proc->clogGroupNext, nextidx);
517
519 &nextidx,
521 break;
522 }
523
524 /*
525 * If the list was not empty, the leader will update the status of our
526 * XID. It is impossible to have followers without a leader because the
527 * first process that has added itself to the list will always have
528 * nextidx as INVALID_PROC_NUMBER.
529 */
530 if (nextidx != INVALID_PROC_NUMBER)
531 {
532 int extraWaits = 0;
533
534 /* Sleep until the leader updates our XID status. */
535 pgstat_report_wait_start(WAIT_EVENT_XACT_GROUP_UPDATE);
536 for (;;)
537 {
538 /* acts as a read barrier */
539 PGSemaphoreLock(proc->sem);
540 if (!proc->clogGroupMember)
541 break;
542 extraWaits++;
543 }
545
547
548 /* Fix semaphore count for any absorbed wakeups */
549 while (extraWaits-- > 0)
550 PGSemaphoreUnlock(proc->sem);
551 return true;
552 }
553
554 /*
555 * By here, we know we're the leader process. Acquire the SLRU bank lock
556 * that corresponds to the page we originally wanted to modify.
557 */
558 prevpageno = proc->clogGroupMemberPage;
559 prevlock = SimpleLruGetBankLock(XactCtl, prevpageno);
560 LWLockAcquire(prevlock, LW_EXCLUSIVE);
561
562 /*
563 * Now that we've got the lock, clear the list of processes waiting for
564 * group XID status update, saving a pointer to the head of the list.
565 * (Trying to pop elements one at a time could lead to an ABA problem.)
566 *
567 * At this point, any processes trying to do this would create a separate
568 * group.
569 */
570 nextidx = pg_atomic_exchange_u32(&procglobal->clogGroupFirst,
572
573 /* Remember head of list so we can perform wakeups after dropping lock. */
574 wakeidx = nextidx;
575
576 /* Walk the list and update the status of all XIDs. */
577 while (nextidx != INVALID_PROC_NUMBER)
578 {
579 PGPROC *nextproc = &ProcGlobal->allProcs[nextidx];
580 int64 thispageno = nextproc->clogGroupMemberPage;
581
582 /*
583 * If the page to update belongs to a different bank than the previous
584 * one, exchange bank lock to the new one. This should be quite rare,
585 * as described above.
586 *
587 * (We could try to optimize this by waking up the processes for which
588 * we have already updated the status while we exchange the lock, but
589 * the code doesn't do that at present. I think it'd require
590 * additional bookkeeping, making the common path slower in order to
591 * improve an infrequent case.)
592 */
593 if (thispageno != prevpageno)
594 {
595 LWLock *lock = SimpleLruGetBankLock(XactCtl, thispageno);
596
597 if (prevlock != lock)
598 {
599 LWLockRelease(prevlock);
601 }
602 prevlock = lock;
603 prevpageno = thispageno;
604 }
605
606 /*
607 * Transactions with more than THRESHOLD_SUBTRANS_CLOG_OPT sub-XIDs
608 * should not use group XID status update mechanism.
609 */
611
613 nextproc->subxidStatus.count,
614 nextproc->subxids.xids,
615 nextproc->clogGroupMemberXidStatus,
616 nextproc->clogGroupMemberLsn,
617 nextproc->clogGroupMemberPage);
618
619 /* Move to next proc in list. */
620 nextidx = pg_atomic_read_u32(&nextproc->clogGroupNext);
621 }
622
623 /* We're done with the lock now. */
624 if (prevlock != NULL)
625 LWLockRelease(prevlock);
626
627 /*
628 * Now that we've released the lock, go back and wake everybody up. We
629 * don't do this under the lock so as to keep lock hold times to a
630 * minimum.
631 *
632 * (Perhaps we could do this in two passes, the first setting
633 * clogGroupNext to invalid while saving the semaphores to an array, then
634 * a single write barrier, then another pass unlocking the semaphores.)
635 */
636 while (wakeidx != INVALID_PROC_NUMBER)
637 {
638 PGPROC *wakeproc = &ProcGlobal->allProcs[wakeidx];
639
640 wakeidx = pg_atomic_read_u32(&wakeproc->clogGroupNext);
642
643 /* ensure all previous writes are visible before follower continues. */
645
646 wakeproc->clogGroupMember = false;
647
648 if (wakeproc != MyProc)
649 PGSemaphoreUnlock(wakeproc->sem);
650 }
651
652 return true;
653}
654
655/*
656 * Sets the commit status of a single transaction.
657 *
658 * Caller must hold the corresponding SLRU bank lock, will be held at exit.
659 */
660static void
662{
663 int byteno = TransactionIdToByte(xid);
664 int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
665 char *byteptr;
666 char byteval;
667 char curval;
668
669 Assert(XactCtl->shared->page_number[slotno] == TransactionIdToPage(xid));
671 XactCtl->shared->page_number[slotno]),
672 LW_EXCLUSIVE));
673
674 byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
675 curval = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
676
677 /*
678 * When replaying transactions during recovery we still need to perform
679 * the two phases of subcommit and then commit. However, some transactions
680 * are already correctly marked, so we just treat those as a no-op which
681 * allows us to keep the following Assert as restrictive as possible.
682 */
685 return;
686
687 /*
688 * Current state change should be from 0 or subcommitted to target state
689 * or we should already be there when replaying changes during recovery.
690 */
691 Assert(curval == 0 ||
694 curval == status);
695
696 /* note this assumes exclusive access to the clog page */
697 byteval = *byteptr;
698 byteval &= ~(((1 << CLOG_BITS_PER_XACT) - 1) << bshift);
699 byteval |= (status << bshift);
700 *byteptr = byteval;
701
702 /*
703 * Update the group LSN if the transaction completion LSN is higher.
704 *
705 * Note: lsn will be invalid when supplied during InRecovery processing,
706 * so we don't need to do anything special to avoid LSN updates during
707 * recovery. After recovery completes the next clog change will set the
708 * LSN correctly.
709 */
710 if (!XLogRecPtrIsInvalid(lsn))
711 {
712 int lsnindex = GetLSNIndex(slotno, xid);
713
714 if (XactCtl->shared->group_lsn[lsnindex] < lsn)
715 XactCtl->shared->group_lsn[lsnindex] = lsn;
716 }
717}
718
719/*
720 * Interrogate the state of a transaction in the commit log.
721 *
722 * Aside from the actual commit status, this function returns (into *lsn)
723 * an LSN that is late enough to be able to guarantee that if we flush up to
724 * that LSN then we will have flushed the transaction's commit record to disk.
725 * The result is not necessarily the exact LSN of the transaction's commit
726 * record! For example, for long-past transactions (those whose clog pages
727 * already migrated to disk), we'll return InvalidXLogRecPtr. Also, because
728 * we group transactions on the same clog page to conserve storage, we might
729 * return the LSN of a later transaction that falls into the same group.
730 *
731 * NB: this is a low-level routine and is NOT the preferred entry point
732 * for most uses; TransactionLogFetch() in transam.c is the intended caller.
733 */
736{
737 int64 pageno = TransactionIdToPage(xid);
738 int byteno = TransactionIdToByte(xid);
739 int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
740 int slotno;
741 int lsnindex;
742 char *byteptr;
743 XidStatus status;
744
745 /* lock is acquired by SimpleLruReadPage_ReadOnly */
746
747 slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, xid);
748 byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
749
750 status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
751
752 lsnindex = GetLSNIndex(slotno, xid);
753 *lsn = XactCtl->shared->group_lsn[lsnindex];
754
756
757 return status;
758}
759
760/*
761 * Number of shared CLOG buffers.
762 *
763 * If asked to autotune, use 2MB for every 1GB of shared buffers, up to 8MB.
764 * Otherwise just cap the configured amount to be between 16 and the maximum
765 * allowed.
766 */
767static int
769{
770 /* auto-tune based on shared buffers */
771 if (transaction_buffers == 0)
772 return SimpleLruAutotuneBuffers(512, 1024);
773
775}
776
777/*
778 * Initialization of shared memory for CLOG
779 */
780Size
782{
784}
785
786void
788{
789 /* If auto-tuning is requested, now is the time to do it */
790 if (transaction_buffers == 0)
791 {
792 char buf[32];
793
794 snprintf(buf, sizeof(buf), "%d", CLOGShmemBuffers());
795 SetConfigOption("transaction_buffers", buf, PGC_POSTMASTER,
797
798 /*
799 * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
800 * However, if the DBA explicitly set transaction_buffers = 0 in the
801 * config file, then PGC_S_DYNAMIC_DEFAULT will fail to override that
802 * and we must force the matter with PGC_S_OVERRIDE.
803 */
804 if (transaction_buffers == 0) /* failed to apply it? */
805 SetConfigOption("transaction_buffers", buf, PGC_POSTMASTER,
807 }
809
810 XactCtl->PagePrecedes = CLOGPagePrecedes;
812 "pg_xact", LWTRANCHE_XACT_BUFFER,
815}
816
817/*
818 * GUC check_hook for transaction_buffers
819 */
820bool
822{
823 return check_slru_buffers("transaction_buffers", newval);
824}
825
826/*
827 * This func must be called ONCE on system install. It creates
828 * the initial CLOG segment. (The CLOG directory is assumed to
829 * have been created by initdb, and CLOGShmemInit must have been
830 * called already.)
831 */
832void
834{
835 int slotno;
837
839
840 /* Create and zero the first page of the commit log */
841 slotno = ZeroCLOGPage(0, false);
842
843 /* Make sure it's written out */
845 Assert(!XactCtl->shared->page_dirty[slotno]);
846
847 LWLockRelease(lock);
848}
849
850/*
851 * Initialize (or reinitialize) a page of CLOG to zeroes.
852 * If writeXlog is true, also emit an XLOG record saying we did this.
853 *
854 * The page is not actually written, just set up in shared memory.
855 * The slot number of the new page is returned.
856 *
857 * Control lock must be held at entry, and will be held at exit.
858 */
859static int
860ZeroCLOGPage(int64 pageno, bool writeXlog)
861{
862 int slotno;
863
864 slotno = SimpleLruZeroPage(XactCtl, pageno);
865
866 if (writeXlog)
867 WriteZeroPageXlogRec(pageno);
868
869 return slotno;
870}
871
872/*
873 * This must be called ONCE during postmaster or standalone-backend startup,
874 * after StartupXLOG has initialized TransamVariables->nextXid.
875 */
876void
878{
880 int64 pageno = TransactionIdToPage(xid);
881
882 /*
883 * Initialize our idea of the latest page number.
884 */
885 pg_atomic_write_u64(&XactCtl->shared->latest_page_number, pageno);
886}
887
888/*
889 * This must be called ONCE at the end of startup/recovery.
890 */
891void
893{
895 int64 pageno = TransactionIdToPage(xid);
896 LWLock *lock = SimpleLruGetBankLock(XactCtl, pageno);
897
899
900 /*
901 * Zero out the remainder of the current clog page. Under normal
902 * circumstances it should be zeroes already, but it seems at least
903 * theoretically possible that XLOG replay will have settled on a nextXID
904 * value that is less than the last XID actually used and marked by the
905 * previous database lifecycle (since subtransaction commit writes clog
906 * but makes no WAL entry). Let's just be safe. (We need not worry about
907 * pages beyond the current one, since those will be zeroed when first
908 * used. For the same reason, there is no need to do anything when
909 * nextXid is exactly at a page boundary; and it's likely that the
910 * "current" page doesn't exist yet in that case.)
911 */
912 if (TransactionIdToPgIndex(xid) != 0)
913 {
914 int byteno = TransactionIdToByte(xid);
915 int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
916 int slotno;
917 char *byteptr;
918
919 slotno = SimpleLruReadPage(XactCtl, pageno, false, xid);
920 byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
921
922 /* Zero so-far-unused positions in the current byte */
923 *byteptr &= (1 << bshift) - 1;
924 /* Zero the rest of the page */
925 MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
926
927 XactCtl->shared->page_dirty[slotno] = true;
928 }
929
930 LWLockRelease(lock);
931}
932
933/*
934 * Perform a checkpoint --- either during shutdown, or on-the-fly
935 */
936void
938{
939 /*
940 * Write dirty CLOG pages to disk. This may result in sync requests
941 * queued for later handling by ProcessSyncRequests(), as part of the
942 * checkpoint.
943 */
944 TRACE_POSTGRESQL_CLOG_CHECKPOINT_START(true);
946 TRACE_POSTGRESQL_CLOG_CHECKPOINT_DONE(true);
947}
948
949
950/*
951 * Make sure that CLOG has room for a newly-allocated XID.
952 *
953 * NB: this is called while holding XidGenLock. We want it to be very fast
954 * most of the time; even when it's not so fast, no actual I/O need happen
955 * unless we're forced to write out a dirty clog or xlog page to make room
956 * in shared memory.
957 */
958void
960{
961 int64 pageno;
962 LWLock *lock;
963
964 /*
965 * No work except at first XID of a page. But beware: just after
966 * wraparound, the first XID of page zero is FirstNormalTransactionId.
967 */
968 if (TransactionIdToPgIndex(newestXact) != 0 &&
970 return;
971
972 pageno = TransactionIdToPage(newestXact);
973 lock = SimpleLruGetBankLock(XactCtl, pageno);
974
976
977 /* Zero the page and make an XLOG entry about it */
978 ZeroCLOGPage(pageno, true);
979
980 LWLockRelease(lock);
981}
982
983
984/*
985 * Remove all CLOG segments before the one holding the passed transaction ID
986 *
987 * Before removing any CLOG data, we must flush XLOG to disk, to ensure that
988 * any recently-emitted records with freeze plans have reached disk; otherwise
989 * a crash and restart might leave us with some unfrozen tuples referencing
990 * removed CLOG data. We choose to emit a special TRUNCATE XLOG record too.
991 * Replaying the deletion from XLOG is not critical, since the files could
992 * just as well be removed later, but doing so prevents a long-running hot
993 * standby server from acquiring an unreasonably bloated CLOG directory.
994 *
995 * Since CLOG segments hold a large number of transactions, the opportunity to
996 * actually remove a segment is fairly rare, and so it seems best not to do
997 * the XLOG flush unless we have confirmed that there is a removable segment.
998 */
999void
1000TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
1001{
1002 int64 cutoffPage;
1003
1004 /*
1005 * The cutoff point is the start of the segment containing oldestXact. We
1006 * pass the *page* containing oldestXact to SimpleLruTruncate.
1007 */
1008 cutoffPage = TransactionIdToPage(oldestXact);
1009
1010 /* Check to see if there's any files that could be removed */
1012 return; /* nothing to remove */
1013
1014 /*
1015 * Advance oldestClogXid before truncating clog, so concurrent xact status
1016 * lookups can ensure they don't attempt to access truncated-away clog.
1017 *
1018 * It's only necessary to do this if we will actually truncate away clog
1019 * pages.
1020 */
1021 AdvanceOldestClogXid(oldestXact);
1022
1023 /*
1024 * Write XLOG record and flush XLOG to disk. We record the oldest xid
1025 * we're keeping information about here so we can ensure that it's always
1026 * ahead of clog truncation in case we crash, and so a standby finds out
1027 * the new valid xid before the next checkpoint.
1028 */
1029 WriteTruncateXlogRec(cutoffPage, oldestXact, oldestxid_datoid);
1030
1031 /* Now we can remove the old CLOG segment(s) */
1032 SimpleLruTruncate(XactCtl, cutoffPage);
1033}
1034
1035
1036/*
1037 * Decide whether a CLOG page number is "older" for truncation purposes.
1038 *
1039 * We need to use comparison of TransactionIds here in order to do the right
1040 * thing with wraparound XID arithmetic. However, TransactionIdPrecedes()
1041 * would get weird about permanent xact IDs. So, offset both such that xid1,
1042 * xid2, and xid2 + CLOG_XACTS_PER_PAGE - 1 are all normal XIDs; this offset
1043 * is relevant to page 0 and to the page preceding page 0.
1044 *
1045 * The page containing oldestXact-2^31 is the important edge case. The
1046 * portion of that page equaling or following oldestXact-2^31 is expendable,
1047 * but the portion preceding oldestXact-2^31 is not. When oldestXact-2^31 is
1048 * the first XID of a page and segment, the entire page and segment is
1049 * expendable, and we could truncate the segment. Recognizing that case would
1050 * require making oldestXact, not just the page containing oldestXact,
1051 * available to this callback. The benefit would be rare and small, so we
1052 * don't optimize that edge case.
1053 */
1054static bool
1056{
1057 TransactionId xid1;
1058 TransactionId xid2;
1059
1060 xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
1061 xid1 += FirstNormalTransactionId + 1;
1062 xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
1063 xid2 += FirstNormalTransactionId + 1;
1064
1065 return (TransactionIdPrecedes(xid1, xid2) &&
1066 TransactionIdPrecedes(xid1, xid2 + CLOG_XACTS_PER_PAGE - 1));
1067}
1068
1069
1070/*
1071 * Write a ZEROPAGE xlog record
1072 */
1073static void
1075{
1077 XLogRegisterData((char *) (&pageno), sizeof(pageno));
1078 (void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE);
1079}
1080
1081/*
1082 * Write a TRUNCATE xlog record
1083 *
1084 * We must flush the xlog record to disk before returning --- see notes
1085 * in TruncateCLOG().
1086 */
1087static void
1088WriteTruncateXlogRec(int64 pageno, TransactionId oldestXact, Oid oldestXactDb)
1089{
1090 XLogRecPtr recptr;
1091 xl_clog_truncate xlrec;
1092
1093 xlrec.pageno = pageno;
1094 xlrec.oldestXact = oldestXact;
1095 xlrec.oldestXactDb = oldestXactDb;
1096
1098 XLogRegisterData((char *) (&xlrec), sizeof(xl_clog_truncate));
1099 recptr = XLogInsert(RM_CLOG_ID, CLOG_TRUNCATE);
1100 XLogFlush(recptr);
1101}
1102
1103/*
1104 * CLOG resource manager's routines
1105 */
1106void
1108{
1109 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1110
1111 /* Backup blocks are not used in clog records */
1113
1114 if (info == CLOG_ZEROPAGE)
1115 {
1116 int64 pageno;
1117 int slotno;
1118 LWLock *lock;
1119
1120 memcpy(&pageno, XLogRecGetData(record), sizeof(pageno));
1121
1122 lock = SimpleLruGetBankLock(XactCtl, pageno);
1124
1125 slotno = ZeroCLOGPage(pageno, false);
1126 SimpleLruWritePage(XactCtl, slotno);
1127 Assert(!XactCtl->shared->page_dirty[slotno]);
1128
1129 LWLockRelease(lock);
1130 }
1131 else if (info == CLOG_TRUNCATE)
1132 {
1133 xl_clog_truncate xlrec;
1134
1135 memcpy(&xlrec, XLogRecGetData(record), sizeof(xl_clog_truncate));
1136
1138
1140 }
1141 else
1142 elog(PANIC, "clog_redo: unknown op code %u", info);
1143}
1144
1145/*
1146 * Entrypoint for sync.c to sync clog files.
1147 */
1148int
1149clogsyncfiletag(const FileTag *ftag, char *path)
1150{
1151 return SlruSyncFileTag(XactCtl, ftag, path);
1152}
static bool pg_atomic_compare_exchange_u32(volatile pg_atomic_uint32 *ptr, uint32 *expected, uint32 newval)
Definition: atomics.h:349
static void pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition: atomics.h:485
#define pg_write_barrier()
Definition: atomics.h:157
static void pg_atomic_write_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:276
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:239
static uint32 pg_atomic_exchange_u32(volatile pg_atomic_uint32 *ptr, uint32 newval)
Definition: atomics.h:330
#define Min(x, y)
Definition: c.h:961
uint8_t uint8
Definition: c.h:486
#define Max(x, y)
Definition: c.h:955
#define Assert(condition)
Definition: c.h:815
int64_t int64
Definition: c.h:485
uint32_t uint32
Definition: c.h:488
#define MemSet(start, val, len)
Definition: c.h:977
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:893
uint32 TransactionId
Definition: c.h:609
size_t Size
Definition: c.h:562
#define CLOG_MAX_ALLOWED_BUFFERS
Definition: clog.c:72
static void WriteZeroPageXlogRec(int64 pageno)
Definition: clog.c:1074
static int ZeroCLOGPage(int64 pageno, bool writeXlog)
Definition: clog.c:860
#define CLOG_XACT_BITMASK
Definition: clog.c:65
#define CLOG_XACTS_PER_PAGE
Definition: clog.c:64
#define THRESHOLD_SUBTRANS_CLOG_OPT
Definition: clog.c:103
static void TransactionIdSetStatusBit(TransactionId xid, XidStatus status, XLogRecPtr lsn, int slotno)
Definition: clog.c:661
XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
Definition: clog.c:735
void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
Definition: clog.c:183
void CLOGShmemInit(void)
Definition: clog.c:787
#define TransactionIdToBIndex(xid)
Definition: clog.c:89
static int CLOGShmemBuffers(void)
Definition: clog.c:768
void ExtendCLOG(TransactionId newestXact)
Definition: clog.c:959
void clog_redo(XLogReaderState *record)
Definition: clog.c:1107
static bool CLOGPagePrecedes(int64 page1, int64 page2)
Definition: clog.c:1055
void TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
Definition: clog.c:1000
Size CLOGShmemSize(void)
Definition: clog.c:781
bool check_transaction_buffers(int *newval, void **extra, GucSource source)
Definition: clog.c:821
static void TransactionIdSetPageStatus(TransactionId xid, int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn, int64 pageno, bool all_xact_same_page)
Definition: clog.c:293
int clogsyncfiletag(const FileTag *ftag, char *path)
Definition: clog.c:1149
void BootStrapCLOG(void)
Definition: clog.c:833
#define CLOG_BITS_PER_XACT
Definition: clog.c:62
#define CLOG_LSNS_PER_PAGE
Definition: clog.c:93
static int64 TransactionIdToPage(TransactionId xid)
Definition: clog.c:82
#define TransactionIdToByte(xid)
Definition: clog.c:88
#define TransactionIdToPgIndex(xid)
Definition: clog.c:87
void StartupCLOG(void)
Definition: clog.c:877
static void set_status_by_pages(int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
Definition: clog.c:257
static bool TransactionGroupUpdateXidStatus(TransactionId xid, XidStatus status, XLogRecPtr lsn, int64 pageno)
Definition: clog.c:441
static void TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn, int64 pageno)
Definition: clog.c:364
#define GetLSNIndex(slotno, xid)
Definition: clog.c:95
void CheckPointCLOG(void)
Definition: clog.c:937
static SlruCtlData XactCtlData
Definition: clog.c:108
#define XactCtl
Definition: clog.c:110
void TrimCLOG(void)
Definition: clog.c:892
static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXact, Oid oldestXactDb)
Definition: clog.c:1088
#define TRANSACTION_STATUS_IN_PROGRESS
Definition: clog.h:27
int XidStatus
Definition: clog.h:25
#define CLOG_ZEROPAGE
Definition: clog.h:55
#define TRANSACTION_STATUS_ABORTED
Definition: clog.h:29
#define TRANSACTION_STATUS_SUB_COMMITTED
Definition: clog.h:30
#define CLOG_TRUNCATE
Definition: clog.h:56
#define TRANSACTION_STATUS_COMMITTED
Definition: clog.h:28
#define PANIC
Definition: elog.h:42
#define elog(elevel,...)
Definition: elog.h:225
int transaction_buffers
Definition: globals.c:166
ProcNumber MyProcNumber
Definition: globals.c:89
void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source)
Definition: guc.c:4332
#define newval
GucSource
Definition: guc.h:112
@ PGC_S_DYNAMIC_DEFAULT
Definition: guc.h:114
@ PGC_S_OVERRIDE
Definition: guc.h:123
@ PGC_POSTMASTER
Definition: guc.h:74
int i
Definition: isn.c:72
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1168
bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1937
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1781
bool LWLockConditionalAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1339
@ LWTRANCHE_XACT_BUFFER
Definition: lwlock.h:179
@ LWTRANCHE_XACT_SLRU
Definition: lwlock.h:216
@ LW_EXCLUSIVE
Definition: lwlock.h:114
static rewind_source * source
Definition: pg_rewind.c:89
static char * buf
Definition: pg_test_fsync.c:72
#define snprintf
Definition: port.h:239
void PGSemaphoreUnlock(PGSemaphore sema)
Definition: posix_sema.c:339
void PGSemaphoreLock(PGSemaphore sema)
Definition: posix_sema.c:319
unsigned int Oid
Definition: postgres_ext.h:32
#define GetPGProcByNumber(n)
Definition: proc.h:423
#define PGPROC_MAX_CACHED_SUBXIDS
Definition: proc.h:39
#define INVALID_PROC_NUMBER
Definition: procnumber.h:26
void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, const char *subdir, int buffer_tranche_id, int bank_tranche_id, SyncRequestHandler sync_handler, bool long_segment_names)
Definition: slru.c:252
int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
Definition: slru.c:605
void SimpleLruWritePage(SlruCtl ctl, int slotno)
Definition: slru.c:732
void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
Definition: slru.c:1322
int SimpleLruAutotuneBuffers(int divisor, int max)
Definition: slru.c:232
bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
Definition: slru.c:1791
int SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok, TransactionId xid)
Definition: slru.c:502
int SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path)
Definition: slru.c:1831
int SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
Definition: slru.c:375
void SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
Definition: slru.c:1408
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition: slru.c:199
bool SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int64 segpage, void *data)
Definition: slru.c:1712
bool check_slru_buffers(const char *name, int *newval)
Definition: slru.c:355
static LWLock * SimpleLruGetBankLock(SlruCtl ctl, int64 pageno)
Definition: slru.h:175
#define SlruPagePrecedesUnitTests(ctl, per_page)
Definition: slru.h:199
PGPROC * MyProc
Definition: proc.c:66
PROC_HDR * ProcGlobal
Definition: proc.c:78
Definition: sync.h:51
Definition: lwlock.h:42
Definition: proc.h:162
XLogRecPtr clogGroupMemberLsn
Definition: proc.h:289
TransactionId clogGroupMemberXid
Definition: proc.h:284
int64 clogGroupMemberPage
Definition: proc.h:287
bool clogGroupMember
Definition: proc.h:282
pg_atomic_uint32 clogGroupNext
Definition: proc.h:283
XidStatus clogGroupMemberXidStatus
Definition: proc.h:285
XidCacheStatus subxidStatus
Definition: proc.h:263
TransactionId xid
Definition: proc.h:172
struct XidCache subxids
Definition: proc.h:265
PGSemaphore sem
Definition: proc.h:166
Definition: proc.h:369
PGPROC * allProcs
Definition: proc.h:371
pg_atomic_uint32 clogGroupFirst
Definition: proc.h:401
FullTransactionId nextXid
Definition: transam.h:220
uint8 count
Definition: proc.h:44
TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS]
Definition: proc.h:51
int64 pageno
Definition: clog.h:34
Oid oldestXactDb
Definition: clog.h:36
TransactionId oldestXact
Definition: clog.h:35
@ SYNC_HANDLER_CLOG
Definition: sync.h:38
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define XidFromFullTransactionId(x)
Definition: transam.h:48
#define FirstNormalTransactionId
Definition: transam.h:34
#define TransactionIdIsValid(xid)
Definition: transam.h:41
void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid)
Definition: varsup.c:355
TransamVariablesData * TransamVariables
Definition: varsup.c:34
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:85
static void pgstat_report_wait_end(void)
Definition: wait_event.h:101
void XLogFlush(XLogRecPtr record)
Definition: xlog.c:2805
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
uint64 XLogRecPtr
Definition: xlogdefs.h:21
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterData(const char *data, uint32 len)
Definition: xloginsert.c:364
void XLogBeginInsert(void)
Definition: xloginsert.c:149
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:410
#define XLogRecGetData(decoder)
Definition: xlogreader.h:415
#define XLogRecHasAnyBlockRefs(decoder)
Definition: xlogreader.h:417
bool InRecovery
Definition: xlogutils.c:50