1 /*-------------------------------------------------------------------------
4 * PostgreSQL commit timestamp manager
6 * This module is a pg_clog-like system that stores the commit timestamp
7 * for each transaction.
9 * XLOG interactions: this module generates an XLOG record whenever a new
10 * CommitTs page is initialized to zeroes. Also, one XLOG record is
11 * generated for setting of values when the caller requests it; this allows
12 * us to support values coming from places other than transaction commit.
13 * Other writes of CommitTS come from recording of transaction commit in
14 * xact.c, which generates its own XLOG records for these events and will
15 * re-perform the status update on redo; so we need make no additional XLOG
18 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
19 * Portions Copyright (c) 1994, Regents of the University of California
21 * src/backend/access/transam/commit_ts.c
23 *-------------------------------------------------------------------------
27 #include "access/commit_ts.h"
28 #include "access/htup_details.h"
29 #include "access/slru.h"
30 #include "access/transam.h"
31 #include "catalog/pg_type.h"
33 #include "miscadmin.h"
35 #include "utils/builtins.h"
36 #include "utils/snapmgr.h"
37 #include "utils/timestamp.h"
40 * Defines for CommitTs page sizes. A page is the same BLCKSZ as is used
41 * everywhere else in Postgres.
43 * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
44 * CommitTs page numbering also wraps around at
45 * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE, and CommitTs segment numbering at
46 * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need take no
47 * explicit notice of that fact in this module, except when comparing segment
48 * and page numbers in TruncateCommitTs (see CommitTsPagePrecedes).
52 * We need 8+2 bytes per xact. Note that enlarging this struct might mean
53 * the largest possible file name is more than 5 chars long; see
56 typedef struct CommitTimestampEntry
60 } CommitTimestampEntry;
62 #define SizeOfCommitTimestampEntry (offsetof(CommitTimestampEntry, nodeid) + \
65 #define COMMIT_TS_XACTS_PER_PAGE \
66 (BLCKSZ / SizeOfCommitTimestampEntry)
68 #define TransactionIdToCTsPage(xid) \
69 ((xid) / (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
70 #define TransactionIdToCTsEntry(xid) \
71 ((xid) % (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
74 * Link to shared-memory data structures for CommitTs control
76 static SlruCtlData CommitTsCtlData;
78 #define CommitTsCtl (&CommitTsCtlData)
81 * We keep a cache of the last value set in shared memory. This is protected
84 typedef struct CommitTimestampShared
86 TransactionId xidLastCommit;
87 CommitTimestampEntry dataLastCommit;
88 } CommitTimestampShared;
90 CommitTimestampShared *commitTsShared;
94 bool track_commit_timestamp;
96 static void SetXidCommitTsInPage(TransactionId xid, int nsubxids,
97 TransactionId *subxids, TimestampTz ts,
98 RepOriginId nodeid, int pageno);
99 static void TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
100 RepOriginId nodeid, int slotno);
101 static int ZeroCommitTsPage(int pageno, bool writeXlog);
102 static bool CommitTsPagePrecedes(int page1, int page2);
103 static void WriteZeroPageXlogRec(int pageno);
104 static void WriteTruncateXlogRec(int pageno);
105 static void WriteSetTimestampXlogRec(TransactionId mainxid, int nsubxids,
106 TransactionId *subxids, TimestampTz timestamp,
110 * TransactionTreeSetCommitTsData
112 * Record the final commit timestamp of transaction entries in the commit log
113 * for a transaction and its subtransaction tree, as efficiently as possible.
115 * xid is the top level transaction id.
117 * subxids is an array of xids of length nsubxids, representing subtransactions
118 * in the tree of xid. In various cases nsubxids may be zero.
119 * The reason why tracking just the parent xid commit timestamp is not enough
120 * is that the subtrans SLRU does not stay valid across crashes (it's not
121 * permanent) so we need to keep the information about them here. If the
122 * subtrans implementation changes in the future, we might want to revisit the
123 * decision of storing timestamp info for each subxid.
125 * The do_xlog parameter tells us whether to include a XLog record of this
126 * or not. Normal path through RecordTransactionCommit() will be related
127 * to a transaction commit XLog record, and so should pass "false" here.
128 * Other callers probably want to pass true, so that the given values persist
129 * in case of crashes.
132 TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids,
133 TransactionId *subxids, TimestampTz timestamp,
134 RepOriginId nodeid, bool do_xlog)
137 TransactionId headxid;
138 TransactionId newestXact;
140 if (!track_commit_timestamp)
144 * Comply with the WAL-before-data rule: if caller specified it wants
145 * this value to be recorded in WAL, do so before touching the data.
148 WriteSetTimestampXlogRec(xid, nsubxids, subxids, timestamp, nodeid);
151 * Figure out the latest Xid in this batch: either the last subxid if
152 * there's any, otherwise the parent xid.
155 newestXact = subxids[nsubxids - 1];
160 * We split the xids to set the timestamp to in groups belonging to the
161 * same SLRU page; the first element in each such set is its head. The
162 * first group has the main XID as the head; subsequent sets use the
163 * first subxid not on the previous page as head. This way, we only have
164 * to lock/modify each SLRU page once.
166 for (i = 0, headxid = xid;;)
168 int pageno = TransactionIdToCTsPage(headxid);
171 for (j = i; j < nsubxids; j++)
173 if (TransactionIdToCTsPage(subxids[j]) != pageno)
176 /* subxids[i..j] are on the same page as the head */
178 SetXidCommitTsInPage(headxid, j - i, subxids + i, timestamp, nodeid,
181 /* if we wrote out all subxids, we're done. */
182 if (j + 1 >= nsubxids)
186 * Set the new head and skip over it, as well as over the subxids
189 headxid = subxids[j];
193 /* update the cached value in shared memory */
194 LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
195 commitTsShared->xidLastCommit = xid;
196 commitTsShared->dataLastCommit.time = timestamp;
197 commitTsShared->dataLastCommit.nodeid = nodeid;
199 /* and move forwards our endpoint, if needed */
200 if (TransactionIdPrecedes(ShmemVariableCache->newestCommitTs, newestXact))
201 ShmemVariableCache->newestCommitTs = newestXact;
202 LWLockRelease(CommitTsLock);
206 * Record the commit timestamp of transaction entries in the commit log for all
207 * entries on a single page. Atomic only on this page.
210 SetXidCommitTsInPage(TransactionId xid, int nsubxids,
211 TransactionId *subxids, TimestampTz ts,
212 RepOriginId nodeid, int pageno)
217 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
219 slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, xid);
221 TransactionIdSetCommitTs(xid, ts, nodeid, slotno);
222 for (i = 0; i < nsubxids; i++)
223 TransactionIdSetCommitTs(subxids[i], ts, nodeid, slotno);
225 CommitTsCtl->shared->page_dirty[slotno] = true;
227 LWLockRelease(CommitTsControlLock);
231 * Sets the commit timestamp of a single transaction.
233 * Must be called with CommitTsControlLock held
236 TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
237 RepOriginId nodeid, int slotno)
239 int entryno = TransactionIdToCTsEntry(xid);
240 CommitTimestampEntry entry;
242 Assert(TransactionIdIsNormal(xid));
245 entry.nodeid = nodeid;
247 memcpy(CommitTsCtl->shared->page_buffer[slotno] +
248 SizeOfCommitTimestampEntry * entryno,
249 &entry, SizeOfCommitTimestampEntry);
253 * Interrogate the commit timestamp of a transaction.
255 * Return value indicates whether commit timestamp record was found for
259 TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
262 int pageno = TransactionIdToCTsPage(xid);
263 int entryno = TransactionIdToCTsEntry(xid);
265 CommitTimestampEntry entry;
266 TransactionId oldestCommitTs;
267 TransactionId newestCommitTs;
269 /* Error if module not enabled */
270 if (!track_commit_timestamp)
272 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
273 errmsg("could not get commit timestamp data"),
274 errhint("Make sure the configuration parameter \"%s\" is set.",
275 "track_commit_timestamp")));
277 /* error if the given Xid doesn't normally commit */
278 if (!TransactionIdIsNormal(xid))
280 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
281 errmsg("cannot retrieve commit timestamp for transaction %u", xid)));
284 * Return empty if the requested value is outside our valid range.
286 LWLockAcquire(CommitTsLock, LW_SHARED);
287 oldestCommitTs = ShmemVariableCache->oldestCommitTs;
288 newestCommitTs = ShmemVariableCache->newestCommitTs;
289 /* neither is invalid, or both are */
290 Assert(TransactionIdIsValid(oldestCommitTs) == TransactionIdIsValid(newestCommitTs));
291 LWLockRelease(CommitTsLock);
293 if (!TransactionIdIsValid(oldestCommitTs) ||
294 TransactionIdPrecedes(xid, oldestCommitTs) ||
295 TransactionIdPrecedes(newestCommitTs, xid))
300 *nodeid = InvalidRepOriginId;
305 * Use an unlocked atomic read on our cached value in shared memory; if
306 * it's a hit, acquire a lock and read the data, after verifying that it's
307 * still what we initially read. Otherwise, fall through to read from
310 if (commitTsShared->xidLastCommit == xid)
312 LWLockAcquire(CommitTsLock, LW_SHARED);
313 if (commitTsShared->xidLastCommit == xid)
316 *ts = commitTsShared->dataLastCommit.time;
318 *nodeid = commitTsShared->dataLastCommit.nodeid;
320 LWLockRelease(CommitTsLock);
323 LWLockRelease(CommitTsLock);
326 /* lock is acquired by SimpleLruReadPage_ReadOnly */
327 slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, xid);
329 CommitTsCtl->shared->page_buffer[slotno] +
330 SizeOfCommitTimestampEntry * entryno,
331 SizeOfCommitTimestampEntry);
336 *nodeid = entry.nodeid;
338 LWLockRelease(CommitTsControlLock);
343 * Return the Xid of the latest committed transaction. (As far as this module
344 * is concerned, anyway; it's up to the caller to ensure the value is useful
347 * ts and extra are filled with the corresponding data; they can be passed
348 * as NULL if not wanted.
351 GetLatestCommitTsData(TimestampTz *ts, RepOriginId *nodeid)
355 /* Error if module not enabled */
356 if (!track_commit_timestamp)
358 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
359 errmsg("could not get commit timestamp data"),
360 errhint("Make sure the configuration parameter \"%s\" is set.",
361 "track_commit_timestamp")));
363 LWLockAcquire(CommitTsLock, LW_SHARED);
364 xid = commitTsShared->xidLastCommit;
366 *ts = commitTsShared->dataLastCommit.time;
368 *nodeid = commitTsShared->dataLastCommit.nodeid;
369 LWLockRelease(CommitTsLock);
375 * SQL-callable wrapper to obtain commit time of a transaction
378 pg_xact_commit_timestamp(PG_FUNCTION_ARGS)
380 TransactionId xid = PG_GETARG_UINT32(0);
384 found = TransactionIdGetCommitTsData(xid, &ts, NULL);
389 PG_RETURN_TIMESTAMPTZ(ts);
394 pg_last_committed_xact(PG_FUNCTION_ARGS)
403 /* and construct a tuple with our data */
404 xid = GetLatestCommitTsData(&ts, NULL);
407 * Construct a tuple descriptor for the result row. This must match this
408 * function's pg_proc entry!
410 tupdesc = CreateTemplateTupleDesc(2, false);
411 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xid",
413 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "timestamp",
414 TIMESTAMPTZOID, -1, 0);
415 tupdesc = BlessTupleDesc(tupdesc);
417 if (!TransactionIdIsNormal(xid))
419 memset(nulls, true, sizeof(nulls));
423 values[0] = TransactionIdGetDatum(xid);
426 values[1] = TimestampTzGetDatum(ts);
430 htup = heap_form_tuple(tupdesc, values, nulls);
432 PG_RETURN_DATUM(HeapTupleGetDatum(htup));
437 * Number of shared CommitTS buffers.
439 * We use a very similar logic as for the number of CLOG buffers; see comments
440 * in CLOGShmemBuffers.
443 CommitTsShmemBuffers(void)
445 return Min(16, Max(4, NBuffers / 1024));
449 * Shared memory sizing for CommitTs
452 CommitTsShmemSize(void)
454 return SimpleLruShmemSize(CommitTsShmemBuffers(), 0) +
455 sizeof(CommitTimestampShared);
459 * Initialize CommitTs at system startup (postmaster start or standalone
463 CommitTsShmemInit(void)
467 CommitTsCtl->PagePrecedes = CommitTsPagePrecedes;
468 SimpleLruInit(CommitTsCtl, "CommitTs Ctl", CommitTsShmemBuffers(), 0,
469 CommitTsControlLock, "pg_commit_ts");
471 commitTsShared = ShmemInitStruct("CommitTs shared",
472 sizeof(CommitTimestampShared),
475 if (!IsUnderPostmaster)
479 commitTsShared->xidLastCommit = InvalidTransactionId;
480 TIMESTAMP_NOBEGIN(commitTsShared->dataLastCommit.time);
481 commitTsShared->dataLastCommit.nodeid = InvalidRepOriginId;
488 * This function must be called ONCE on system install.
490 * (The CommitTs directory is assumed to have been created by initdb, and
491 * CommitTsShmemInit must have been called already.)
494 BootStrapCommitTs(void)
497 * Nothing to do here at present, unlike most other SLRU modules; segments
498 * are created when the server is started with this module enabled.
499 * See StartupCommitTs.
504 * Initialize (or reinitialize) a page of CommitTs to zeroes.
505 * If writeXlog is TRUE, also emit an XLOG record saying we did this.
507 * The page is not actually written, just set up in shared memory.
508 * The slot number of the new page is returned.
510 * Control lock must be held at entry, and will be held at exit.
513 ZeroCommitTsPage(int pageno, bool writeXlog)
517 slotno = SimpleLruZeroPage(CommitTsCtl, pageno);
520 WriteZeroPageXlogRec(pageno);
526 * This must be called ONCE during postmaster or standalone-backend startup,
527 * after StartupXLOG has initialized ShmemVariableCache->nextXid.
530 StartupCommitTs(void)
532 TransactionId xid = ShmemVariableCache->nextXid;
533 int pageno = TransactionIdToCTsPage(xid);
535 if (track_commit_timestamp)
541 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
544 * Initialize our idea of the latest page number.
546 CommitTsCtl->shared->latest_page_number = pageno;
548 LWLockRelease(CommitTsControlLock);
552 * This must be called ONCE during postmaster or standalone-backend startup,
553 * when commit timestamp is enabled, after recovery has finished.
556 CompleteCommitTsInitialization(void)
558 if (!track_commit_timestamp)
559 DeactivateCommitTs(true);
563 * Activate this module whenever necessary.
564 * This must happen during postmaster or standalong-backend startup,
565 * or during WAL replay anytime the track_commit_timestamp setting is
566 * changed in the master.
568 * The reason why this SLRU needs separate activation/deactivation functions is
569 * that it can be enabled/disabled during start and the activation/deactivation
570 * on master is propagated to slave via replay. Other SLRUs don't have this
571 * property and they can be just initialized during normal startup.
573 * This is in charge of creating the currently active segment, if it's not
574 * already there. The reason for this is that the server might have been
575 * running with this module disabled for a while and thus might have skipped
576 * the normal creation point.
579 ActivateCommitTs(void)
581 TransactionId xid = ShmemVariableCache->nextXid;
582 int pageno = TransactionIdToCTsPage(xid);
585 * Re-Initialize our idea of the latest page number.
587 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
588 CommitTsCtl->shared->latest_page_number = pageno;
589 LWLockRelease(CommitTsControlLock);
592 * If CommitTs is enabled, but it wasn't in the previous server run, we
593 * need to set the oldest and newest values to the next Xid; that way, we
594 * will not try to read data that might not have been set.
596 * XXX does this have a problem if a server is started with commitTs
597 * enabled, then started with commitTs disabled, then restarted with it
598 * enabled again? It doesn't look like it does, because there should be a
599 * checkpoint that sets the value to InvalidTransactionId at end of
600 * recovery; and so any chance of injecting new transactions without
601 * CommitTs values would occur after the oldestCommitTs has been set to
602 * Invalid temporarily.
604 LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
605 if (ShmemVariableCache->oldestCommitTs == InvalidTransactionId)
607 ShmemVariableCache->oldestCommitTs =
608 ShmemVariableCache->newestCommitTs = ReadNewTransactionId();
610 LWLockRelease(CommitTsLock);
612 /* Finally, create the current segment file, if necessary */
613 if (!SimpleLruDoesPhysicalPageExist(CommitTsCtl, pageno))
617 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
618 slotno = ZeroCommitTsPage(pageno, false);
619 SimpleLruWritePage(CommitTsCtl, slotno);
620 Assert(!CommitTsCtl->shared->page_dirty[slotno]);
621 LWLockRelease(CommitTsControlLock);
626 * Deactivate this module.
628 * This must be called when the track_commit_timestamp parameter is turned off.
629 * This happens during postmaster or standalone-backend startup, or during WAL
632 * Resets CommitTs into invalid state to make sure we don't hand back
633 * possibly-invalid data; also removes segments of old data.
636 DeactivateCommitTs(bool do_wal)
638 TransactionId xid = ShmemVariableCache->nextXid;
639 int pageno = TransactionIdToCTsPage(xid);
642 * Re-Initialize our idea of the latest page number.
644 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
645 CommitTsCtl->shared->latest_page_number = pageno;
646 LWLockRelease(CommitTsControlLock);
648 LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
649 ShmemVariableCache->oldestCommitTs = InvalidTransactionId;
650 ShmemVariableCache->newestCommitTs = InvalidTransactionId;
651 LWLockRelease(CommitTsLock);
653 TruncateCommitTs(ReadNewTransactionId(), do_wal);
657 * This must be called ONCE during postmaster or standalone-backend shutdown
660 ShutdownCommitTs(void)
662 /* Flush dirty CommitTs pages to disk */
663 SimpleLruFlush(CommitTsCtl, false);
667 * Perform a checkpoint --- either during shutdown, or on-the-fly
670 CheckPointCommitTs(void)
672 /* Flush dirty CommitTs pages to disk */
673 SimpleLruFlush(CommitTsCtl, true);
677 * Make sure that CommitTs has room for a newly-allocated XID.
679 * NB: this is called while holding XidGenLock. We want it to be very fast
680 * most of the time; even when it's not so fast, no actual I/O need happen
681 * unless we're forced to write out a dirty CommitTs or xlog page to make room
684 * NB: the current implementation relies on track_commit_timestamp being
688 ExtendCommitTs(TransactionId newestXact)
692 /* nothing to do if module not enabled */
693 if (!track_commit_timestamp)
697 * No work except at first XID of a page. But beware: just after
698 * wraparound, the first XID of page zero is FirstNormalTransactionId.
700 if (TransactionIdToCTsEntry(newestXact) != 0 &&
701 !TransactionIdEquals(newestXact, FirstNormalTransactionId))
704 pageno = TransactionIdToCTsPage(newestXact);
706 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
708 /* Zero the page and make an XLOG entry about it */
709 ZeroCommitTsPage(pageno, !InRecovery);
711 LWLockRelease(CommitTsControlLock);
715 * Remove all CommitTs segments before the one holding the passed
718 * Note that we don't need to flush XLOG here.
721 TruncateCommitTs(TransactionId oldestXact, bool do_wal)
726 * The cutoff point is the start of the segment containing oldestXact. We
727 * pass the *page* containing oldestXact to SimpleLruTruncate.
729 cutoffPage = TransactionIdToCTsPage(oldestXact);
731 /* Check to see if there's any files that could be removed */
732 if (!SlruScanDirectory(CommitTsCtl, SlruScanDirCbReportPresence,
734 return; /* nothing to remove */
736 /* Write XLOG record */
738 WriteTruncateXlogRec(cutoffPage);
740 /* Now we can remove the old CommitTs segment(s) */
741 SimpleLruTruncate(CommitTsCtl, cutoffPage);
745 * Set the limit values between which commit TS can be consulted.
748 SetCommitTsLimit(TransactionId oldestXact, TransactionId newestXact)
751 * Be careful not to overwrite values that are either further into the
752 * "future" or signal a disabled committs.
754 LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
755 if (ShmemVariableCache->oldestCommitTs != InvalidTransactionId)
757 if (TransactionIdPrecedes(ShmemVariableCache->oldestCommitTs, oldestXact))
758 ShmemVariableCache->oldestCommitTs = oldestXact;
759 if (TransactionIdPrecedes(newestXact, ShmemVariableCache->newestCommitTs))
760 ShmemVariableCache->newestCommitTs = newestXact;
764 Assert(ShmemVariableCache->newestCommitTs == InvalidTransactionId);
766 LWLockRelease(CommitTsLock);
770 * Move forwards the oldest commitTS value that can be consulted
773 AdvanceOldestCommitTs(TransactionId oldestXact)
775 LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
776 if (ShmemVariableCache->oldestCommitTs != InvalidTransactionId &&
777 TransactionIdPrecedes(ShmemVariableCache->oldestCommitTs, oldestXact))
778 ShmemVariableCache->oldestCommitTs = oldestXact;
779 LWLockRelease(CommitTsLock);
784 * Decide which of two CLOG page numbers is "older" for truncation purposes.
786 * We need to use comparison of TransactionIds here in order to do the right
787 * thing with wraparound XID arithmetic. However, if we are asked about
788 * page number zero, we don't want to hand InvalidTransactionId to
789 * TransactionIdPrecedes: it'll get weird about permanent xact IDs. So,
790 * offset both xids by FirstNormalTransactionId to avoid that.
793 CommitTsPagePrecedes(int page1, int page2)
798 xid1 = ((TransactionId) page1) * COMMIT_TS_XACTS_PER_PAGE;
799 xid1 += FirstNormalTransactionId;
800 xid2 = ((TransactionId) page2) * COMMIT_TS_XACTS_PER_PAGE;
801 xid2 += FirstNormalTransactionId;
803 return TransactionIdPrecedes(xid1, xid2);
808 * Write a ZEROPAGE xlog record
811 WriteZeroPageXlogRec(int pageno)
814 XLogRegisterData((char *) (&pageno), sizeof(int));
815 (void) XLogInsert(RM_COMMIT_TS_ID, COMMIT_TS_ZEROPAGE);
819 * Write a TRUNCATE xlog record
822 WriteTruncateXlogRec(int pageno)
825 XLogRegisterData((char *) (&pageno), sizeof(int));
826 (void) XLogInsert(RM_COMMIT_TS_ID, COMMIT_TS_TRUNCATE);
830 * Write a SETTS xlog record
833 WriteSetTimestampXlogRec(TransactionId mainxid, int nsubxids,
834 TransactionId *subxids, TimestampTz timestamp,
837 xl_commit_ts_set record;
839 record.timestamp = timestamp;
840 record.nodeid = nodeid;
841 record.mainxid = mainxid;
844 XLogRegisterData((char *) &record,
845 offsetof(xl_commit_ts_set, mainxid) +
846 sizeof(TransactionId));
847 XLogRegisterData((char *) subxids, nsubxids * sizeof(TransactionId));
848 XLogInsert(RM_COMMIT_TS_ID, COMMIT_TS_SETTS);
852 * CommitTS resource manager's routines
855 commit_ts_redo(XLogReaderState *record)
857 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
859 /* Backup blocks are not used in commit_ts records */
860 Assert(!XLogRecHasAnyBlockRefs(record));
862 if (info == COMMIT_TS_ZEROPAGE)
867 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
869 LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
871 slotno = ZeroCommitTsPage(pageno, false);
872 SimpleLruWritePage(CommitTsCtl, slotno);
873 Assert(!CommitTsCtl->shared->page_dirty[slotno]);
875 LWLockRelease(CommitTsControlLock);
877 else if (info == COMMIT_TS_TRUNCATE)
881 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
884 * During XLOG replay, latest_page_number isn't set up yet; insert a
885 * suitable value to bypass the sanity test in SimpleLruTruncate.
887 CommitTsCtl->shared->latest_page_number = pageno;
889 SimpleLruTruncate(CommitTsCtl, pageno);
891 else if (info == COMMIT_TS_SETTS)
893 xl_commit_ts_set *setts = (xl_commit_ts_set *) XLogRecGetData(record);
895 TransactionId *subxids;
897 nsubxids = ((XLogRecGetDataLen(record) - SizeOfCommitTsSet) /
898 sizeof(TransactionId));
901 subxids = palloc(sizeof(TransactionId) * nsubxids);
903 XLogRecGetData(record) + SizeOfCommitTsSet,
904 sizeof(TransactionId) * nsubxids);
909 TransactionTreeSetCommitTsData(setts->mainxid, nsubxids, subxids,
910 setts->timestamp, setts->nodeid, false);
915 elog(PANIC, "commit_ts_redo: unknown op code %u", info);