Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * commit_ts.c
4 : : * PostgreSQL commit timestamp manager
5 : : *
6 : : * This module is a pg_xact-like system that stores the commit timestamp
7 : : * for each transaction.
8 : : *
9 : : * XLOG interactions: this module generates an XLOG record whenever a new
10 : : * CommitTs page is initialized to zeroes. Other writes of CommitTS come
11 : : * from recording of transaction commit in xact.c, which generates its own
12 : : * XLOG records for these events and will re-perform the status update on
13 : : * redo; so we need make no additional XLOG entry here.
14 : : *
15 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
16 : : * Portions Copyright (c) 1994, Regents of the University of California
17 : : *
18 : : * src/backend/access/transam/commit_ts.c
19 : : *
20 : : *-------------------------------------------------------------------------
21 : : */
22 : : #include "postgres.h"
23 : :
24 : : #include "access/commit_ts.h"
25 : : #include "access/htup_details.h"
26 : : #include "access/slru.h"
27 : : #include "access/transam.h"
28 : : #include "access/xloginsert.h"
29 : : #include "access/xlogutils.h"
30 : : #include "funcapi.h"
31 : : #include "miscadmin.h"
32 : : #include "storage/shmem.h"
33 : : #include "storage/subsystems.h"
34 : : #include "utils/fmgrprotos.h"
35 : : #include "utils/guc_hooks.h"
36 : : #include "utils/timestamp.h"
37 : :
38 : : /*
39 : : * Defines for CommitTs 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 : : * CommitTs page numbering also wraps around at
44 : : * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE, and CommitTs segment numbering at
45 : : * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need take no
46 : : * explicit notice of that fact in this module, except when comparing segment
47 : : * and page numbers in TruncateCommitTs (see CommitTsPagePrecedes).
48 : : */
49 : :
50 : : /*
51 : : * We need 8+2 bytes per xact. Note that enlarging this struct might mean
52 : : * the largest possible file name is more than 5 chars long; see
53 : : * SlruScanDirectory.
54 : : */
55 : : typedef struct CommitTimestampEntry
56 : : {
57 : : TimestampTz time;
58 : : ReplOriginId nodeid;
59 : : } CommitTimestampEntry;
60 : :
61 : : #define SizeOfCommitTimestampEntry (offsetof(CommitTimestampEntry, nodeid) + \
62 : : sizeof(ReplOriginId))
63 : :
64 : : #define COMMIT_TS_XACTS_PER_PAGE \
65 : : (BLCKSZ / SizeOfCommitTimestampEntry)
66 : :
67 : :
68 : : /*
69 : : * Although we return an int64 the actual value can't currently exceed
70 : : * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE.
71 : : */
72 : : static inline int64
888 akorotkov@postgresql 73 :CBC 1068 : TransactionIdToCTsPage(TransactionId xid)
74 : : {
75 : 1068 : return xid / (int64) COMMIT_TS_XACTS_PER_PAGE;
76 : : }
77 : :
78 : : #define TransactionIdToCTsEntry(xid) \
79 : : ((xid) % (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
80 : :
81 : : /*
82 : : * Link to shared-memory data structures for CommitTs control
83 : : */
84 : : static void CommitTsShmemRequest(void *arg);
85 : : static void CommitTsShmemInit(void *arg);
86 : : static bool CommitTsPagePrecedes(int64 page1, int64 page2);
87 : : static int commit_ts_errdetail_for_io_error(const void *opaque_data);
88 : :
89 : : const ShmemCallbacks CommitTsShmemCallbacks = {
90 : : .request_fn = CommitTsShmemRequest,
91 : : .init_fn = CommitTsShmemInit,
92 : : };
93 : :
94 : : static SlruDesc CommitTsSlruDesc;
95 : :
96 : : #define CommitTsCtl (&CommitTsSlruDesc)
97 : :
98 : : /*
99 : : * We keep a cache of the last value set in shared memory.
100 : : *
101 : : * This is also good place to keep the activation status. We keep this
102 : : * separate from the GUC so that the standby can activate the module if the
103 : : * primary has it active independently of the value of the GUC.
104 : : *
105 : : * This is protected by CommitTsLock. In some places, we use commitTsActive
106 : : * without acquiring the lock; where this happens, a comment explains the
107 : : * rationale for it.
108 : : */
109 : : typedef struct CommitTimestampShared
110 : : {
111 : : TransactionId xidLastCommit;
112 : : CommitTimestampEntry dataLastCommit;
113 : : bool commitTsActive;
114 : : } CommitTimestampShared;
115 : :
116 : : static CommitTimestampShared *commitTsShared;
117 : :
118 : : static void CommitTsShmemInit(void *arg);
119 : :
120 : : /* GUC variable */
121 : : bool track_commit_timestamp;
122 : :
123 : : static void SetXidCommitTsInPage(TransactionId xid, int nsubxids,
124 : : TransactionId *subxids, TimestampTz ts,
125 : : ReplOriginId nodeid, int64 pageno);
126 : : static void TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
127 : : ReplOriginId nodeid, int slotno);
128 : : static void error_commit_ts_disabled(void);
129 : : static void ActivateCommitTs(void);
130 : : static void DeactivateCommitTs(void);
131 : : static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXid);
132 : :
133 : : /*
134 : : * TransactionTreeSetCommitTsData
135 : : *
136 : : * Record the final commit timestamp of transaction entries in the commit log
137 : : * for a transaction and its subtransaction tree, as efficiently as possible.
138 : : *
139 : : * xid is the top level transaction id.
140 : : *
141 : : * subxids is an array of xids of length nsubxids, representing subtransactions
142 : : * in the tree of xid. In various cases nsubxids may be zero.
143 : : * The reason why tracking just the parent xid commit timestamp is not enough
144 : : * is that the subtrans SLRU does not stay valid across crashes (it's not
145 : : * permanent) so we need to keep the information about them here. If the
146 : : * subtrans implementation changes in the future, we might want to revisit the
147 : : * decision of storing timestamp info for each subxid.
148 : : */
149 : : void
4171 alvherre@alvh.no-ip. 150 : 180702 : TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids,
151 : : TransactionId *subxids, TimestampTz timestamp,
152 : : ReplOriginId nodeid)
153 : : {
154 : : int i;
155 : : TransactionId headxid;
156 : : TransactionId newestXact;
157 : :
158 : : /*
159 : : * No-op if the module is not active.
160 : : *
161 : : * An unlocked read here is fine, because in a standby (the only place
162 : : * where the flag can change in flight) this routine is only called by the
163 : : * recovery process, which is also the only process which can change the
164 : : * flag.
165 : : */
3843 166 [ + + ]: 180702 : if (!commitTsShared->commitTsActive)
4171 167 : 179863 : return;
168 : :
169 : : /*
170 : : * Figure out the latest Xid in this batch: either the last subxid if
171 : : * there's any, otherwise the parent xid.
172 : : */
173 [ - + ]: 839 : if (nsubxids > 0)
4171 alvherre@alvh.no-ip. 174 :UBC 0 : newestXact = subxids[nsubxids - 1];
175 : : else
4171 alvherre@alvh.no-ip. 176 :CBC 839 : newestXact = xid;
177 : :
178 : : /*
179 : : * We split the xids to set the timestamp to in groups belonging to the
180 : : * same SLRU page; the first element in each such set is its head. The
181 : : * first group has the main XID as the head; subsequent sets use the first
182 : : * subxid not on the previous page as head. This way, we only have to
183 : : * lock/modify each SLRU page once.
184 : : */
1565 michael@paquier.xyz 185 : 839 : headxid = xid;
186 : 839 : i = 0;
187 : : for (;;)
4171 alvherre@alvh.no-ip. 188 :UBC 0 : {
888 akorotkov@postgresql 189 :CBC 839 : int64 pageno = TransactionIdToCTsPage(headxid);
190 : : int j;
191 : :
4171 alvherre@alvh.no-ip. 192 [ - + ]: 839 : for (j = i; j < nsubxids; j++)
193 : : {
4171 alvherre@alvh.no-ip. 194 [ # # ]:UBC 0 : if (TransactionIdToCTsPage(subxids[j]) != pageno)
195 : 0 : break;
196 : : }
197 : : /* subxids[i..j] are on the same page as the head */
198 : :
4171 alvherre@alvh.no-ip. 199 :CBC 839 : SetXidCommitTsInPage(headxid, j - i, subxids + i, timestamp, nodeid,
200 : : pageno);
201 : :
202 : : /* if we wrote out all subxids, we're done. */
1565 michael@paquier.xyz 203 [ + - ]: 839 : if (j >= nsubxids)
4171 alvherre@alvh.no-ip. 204 : 839 : break;
205 : :
206 : : /*
207 : : * Set the new head and skip over it, as well as over the subxids we
208 : : * just wrote.
209 : : */
4171 alvherre@alvh.no-ip. 210 :UBC 0 : headxid = subxids[j];
1565 michael@paquier.xyz 211 : 0 : i = j + 1;
212 : : }
213 : :
214 : : /* update the cached value in shared memory */
4171 alvherre@alvh.no-ip. 215 :CBC 839 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
216 : 839 : commitTsShared->xidLastCommit = xid;
217 : 839 : commitTsShared->dataLastCommit.time = timestamp;
218 : 839 : commitTsShared->dataLastCommit.nodeid = nodeid;
219 : :
220 : : /* and move forwards our endpoint, if needed */
879 heikki.linnakangas@i 221 [ + + ]: 839 : if (TransactionIdPrecedes(TransamVariables->newestCommitTsXid, newestXact))
222 : 827 : TransamVariables->newestCommitTsXid = newestXact;
4171 alvherre@alvh.no-ip. 223 : 839 : LWLockRelease(CommitTsLock);
224 : : }
225 : :
226 : : /*
227 : : * Record the commit timestamp of transaction entries in the commit log for all
228 : : * entries on a single page. Atomic only on this page.
229 : : */
230 : : static void
231 : 839 : SetXidCommitTsInPage(TransactionId xid, int nsubxids,
232 : : TransactionId *subxids, TimestampTz ts,
233 : : ReplOriginId nodeid, int64 pageno)
234 : : {
797 235 : 839 : LWLock *lock = SimpleLruGetBankLock(CommitTsCtl, pageno);
236 : : int slotno;
237 : : int i;
238 : :
239 : 839 : LWLockAcquire(lock, LW_EXCLUSIVE);
240 : :
53 heikki.linnakangas@i 241 :GNC 839 : slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, &xid);
242 : :
4171 alvherre@alvh.no-ip. 243 :CBC 839 : TransactionIdSetCommitTs(xid, ts, nodeid, slotno);
244 [ - + ]: 839 : for (i = 0; i < nsubxids; i++)
4171 alvherre@alvh.no-ip. 245 :UBC 0 : TransactionIdSetCommitTs(subxids[i], ts, nodeid, slotno);
246 : :
4171 alvherre@alvh.no-ip. 247 :CBC 839 : CommitTsCtl->shared->page_dirty[slotno] = true;
248 : :
797 249 : 839 : LWLockRelease(lock);
4171 250 : 839 : }
251 : :
252 : : /*
253 : : * Sets the commit timestamp of a single transaction.
254 : : *
255 : : * Caller must hold the correct SLRU bank lock, will be held at exit
256 : : */
257 : : static void
258 : 839 : TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
259 : : ReplOriginId nodeid, int slotno)
260 : : {
261 : 839 : int entryno = TransactionIdToCTsEntry(xid);
262 : : CommitTimestampEntry entry;
263 : :
264 [ - + ]: 839 : Assert(TransactionIdIsNormal(xid));
265 : :
266 : 839 : entry.time = ts;
267 : 839 : entry.nodeid = nodeid;
268 : :
269 : 839 : memcpy(CommitTsCtl->shared->page_buffer[slotno] +
270 : 839 : SizeOfCommitTimestampEntry * entryno,
271 : : &entry, SizeOfCommitTimestampEntry);
272 : 839 : }
273 : :
274 : : /*
275 : : * Interrogate the commit timestamp of a transaction.
276 : : *
277 : : * The return value indicates whether a commit timestamp record was found for
278 : : * the given xid. The timestamp value is returned in *ts (which may not be
279 : : * null), and the origin node for the Xid is returned in *nodeid, if it's not
280 : : * null.
281 : : */
282 : : bool
283 : 87 : TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
284 : : ReplOriginId *nodeid)
285 : : {
888 akorotkov@postgresql 286 : 87 : int64 pageno = TransactionIdToCTsPage(xid);
4171 alvherre@alvh.no-ip. 287 : 87 : int entryno = TransactionIdToCTsEntry(xid);
288 : : int slotno;
289 : : CommitTimestampEntry entry;
290 : : TransactionId oldestCommitTsXid;
291 : : TransactionId newestCommitTsXid;
292 : :
3449 293 [ + + ]: 87 : if (!TransactionIdIsValid(xid))
3843 294 [ + - ]: 3 : ereport(ERROR,
295 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
296 : : errmsg("cannot retrieve commit timestamp for transaction %u", xid)));
3449 297 [ + + ]: 84 : else if (!TransactionIdIsNormal(xid))
298 : : {
299 : : /* frozen and bootstrap xids are always committed far in the past */
300 : 6 : *ts = 0;
301 [ + + ]: 6 : if (nodeid)
14 michael@paquier.xyz 302 :GNC 2 : *nodeid = InvalidReplOriginId;
3449 alvherre@alvh.no-ip. 303 :CBC 6 : return false;
304 : : }
305 : :
3843 306 : 78 : LWLockAcquire(CommitTsLock, LW_SHARED);
307 : :
308 : : /* Error if module not enabled */
309 [ + + ]: 78 : if (!commitTsShared->commitTsActive)
3806 310 : 6 : error_commit_ts_disabled();
311 : :
312 : : /*
313 : : * If we're asked for the cached value, return that. Otherwise, fall
314 : : * through to read from SLRU.
315 : : */
3843 316 [ + + ]: 72 : if (commitTsShared->xidLastCommit == xid)
317 : : {
318 : 13 : *ts = commitTsShared->dataLastCommit.time;
319 [ + + ]: 13 : if (nodeid)
320 : 8 : *nodeid = commitTsShared->dataLastCommit.nodeid;
321 : :
322 : 13 : LWLockRelease(CommitTsLock);
323 : 13 : return *ts != 0;
324 : : }
325 : :
879 heikki.linnakangas@i 326 : 59 : oldestCommitTsXid = TransamVariables->oldestCommitTsXid;
327 : 59 : newestCommitTsXid = TransamVariables->newestCommitTsXid;
328 : : /* neither is invalid, or both are */
3781 mail@joeconway.com 329 [ - + ]: 59 : Assert(TransactionIdIsValid(oldestCommitTsXid) == TransactionIdIsValid(newestCommitTsXid));
4171 alvherre@alvh.no-ip. 330 : 59 : LWLockRelease(CommitTsLock);
331 : :
332 : : /*
333 : : * Return empty if the requested value is outside our valid range.
334 : : */
3781 mail@joeconway.com 335 [ + - + + ]: 118 : if (!TransactionIdIsValid(oldestCommitTsXid) ||
336 [ - + ]: 67 : TransactionIdPrecedes(xid, oldestCommitTsXid) ||
337 : 8 : TransactionIdPrecedes(newestCommitTsXid, xid))
338 : : {
3910 alvherre@alvh.no-ip. 339 : 51 : *ts = 0;
4171 340 [ + + ]: 51 : if (nodeid)
97 msawada@postgresql.o 341 :GNC 48 : *nodeid = InvalidReplOriginId;
4171 alvherre@alvh.no-ip. 342 :CBC 51 : return false;
343 : : }
344 : :
345 : : /* lock is acquired by SimpleLruReadPage_ReadOnly */
53 heikki.linnakangas@i 346 :GNC 8 : slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, &xid);
4171 alvherre@alvh.no-ip. 347 :CBC 8 : memcpy(&entry,
348 : 8 : CommitTsCtl->shared->page_buffer[slotno] +
349 : 8 : SizeOfCommitTimestampEntry * entryno,
350 : : SizeOfCommitTimestampEntry);
351 : :
3910 352 : 8 : *ts = entry.time;
4171 353 [ + + ]: 8 : if (nodeid)
354 : 7 : *nodeid = entry.nodeid;
355 : :
797 356 : 8 : LWLockRelease(SimpleLruGetBankLock(CommitTsCtl, pageno));
4171 357 : 8 : return *ts != 0;
358 : : }
359 : :
360 : : /*
361 : : * Return the Xid of the latest committed transaction. (As far as this module
362 : : * is concerned, anyway; it's up to the caller to ensure the value is useful
363 : : * for its purposes.)
364 : : *
365 : : * ts and nodeid are filled with the corresponding data; they can be passed
366 : : * as NULL if not wanted.
367 : : */
368 : : TransactionId
97 msawada@postgresql.o 369 :GNC 4 : GetLatestCommitTsData(TimestampTz *ts, ReplOriginId *nodeid)
370 : : {
371 : : TransactionId xid;
372 : :
3843 alvherre@alvh.no-ip. 373 :CBC 4 : LWLockAcquire(CommitTsLock, LW_SHARED);
374 : :
375 : : /* Error if module not enabled */
376 [ + + ]: 4 : if (!commitTsShared->commitTsActive)
3806 377 : 3 : error_commit_ts_disabled();
378 : :
4171 alvherre@alvh.no-ip. 379 :GBC 1 : xid = commitTsShared->xidLastCommit;
380 [ + - ]: 1 : if (ts)
381 : 1 : *ts = commitTsShared->dataLastCommit.time;
382 [ + - ]: 1 : if (nodeid)
383 : 1 : *nodeid = commitTsShared->dataLastCommit.nodeid;
384 : 1 : LWLockRelease(CommitTsLock);
385 : :
386 : 1 : return xid;
387 : : }
388 : :
389 : : static void
3806 alvherre@alvh.no-ip. 390 :CBC 9 : error_commit_ts_disabled(void)
391 : : {
392 [ + - + + ]: 9 : ereport(ERROR,
393 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
394 : : errmsg("could not get commit timestamp data"),
395 : : RecoveryInProgress() ?
396 : : errhint("Make sure the configuration parameter \"%s\" is set on the primary server.",
397 : : "track_commit_timestamp") :
398 : : errhint("Make sure the configuration parameter \"%s\" is set.",
399 : : "track_commit_timestamp")));
400 : : }
401 : :
402 : : /*
403 : : * SQL-callable wrapper to obtain commit time of a transaction
404 : : */
405 : : Datum
4171 406 : 19 : pg_xact_commit_timestamp(PG_FUNCTION_ARGS)
407 : : {
2010 peter@eisentraut.org 408 : 19 : TransactionId xid = PG_GETARG_TRANSACTIONID(0);
409 : : TimestampTz ts;
410 : : bool found;
411 : :
4171 alvherre@alvh.no-ip. 412 : 19 : found = TransactionIdGetCommitTsData(xid, &ts, NULL);
413 : :
414 [ + + ]: 13 : if (!found)
415 : 7 : PG_RETURN_NULL();
416 : :
417 : 6 : PG_RETURN_TIMESTAMPTZ(ts);
418 : : }
419 : :
420 : :
421 : : /*
422 : : * pg_last_committed_xact
423 : : *
424 : : * SQL-callable wrapper to obtain some information about the latest
425 : : * committed transaction: transaction ID, timestamp and replication
426 : : * origin.
427 : : */
428 : : Datum
429 : 4 : pg_last_committed_xact(PG_FUNCTION_ARGS)
430 : : {
431 : : TransactionId xid;
432 : : ReplOriginId nodeid;
433 : : TimestampTz ts;
434 : : Datum values[3];
435 : : bool nulls[3];
436 : : TupleDesc tupdesc;
437 : : HeapTuple htup;
438 : :
439 : : /* and construct a tuple with our data */
2123 michael@paquier.xyz 440 : 4 : xid = GetLatestCommitTsData(&ts, &nodeid);
441 : :
1231 michael@paquier.xyz 442 [ - + ]:GBC 1 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1231 michael@paquier.xyz 443 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
444 : :
4171 alvherre@alvh.no-ip. 445 [ - + ]:GBC 1 : if (!TransactionIdIsNormal(xid))
446 : : {
4171 alvherre@alvh.no-ip. 447 :UBC 0 : memset(nulls, true, sizeof(nulls));
448 : : }
449 : : else
450 : : {
4171 alvherre@alvh.no-ip. 451 :GBC 1 : values[0] = TransactionIdGetDatum(xid);
452 : 1 : nulls[0] = false;
453 : :
454 : 1 : values[1] = TimestampTzGetDatum(ts);
455 : 1 : nulls[1] = false;
456 : :
2123 michael@paquier.xyz 457 : 1 : values[2] = ObjectIdGetDatum((Oid) nodeid);
458 : 1 : nulls[2] = false;
459 : : }
460 : :
4171 alvherre@alvh.no-ip. 461 : 1 : htup = heap_form_tuple(tupdesc, values, nulls);
462 : :
463 : 1 : PG_RETURN_DATUM(HeapTupleGetDatum(htup));
464 : : }
465 : :
466 : : /*
467 : : * pg_xact_commit_timestamp_origin
468 : : *
469 : : * SQL-callable wrapper to obtain commit timestamp and replication origin
470 : : * of a given transaction.
471 : : */
472 : : Datum
2123 michael@paquier.xyz 473 :CBC 5 : pg_xact_commit_timestamp_origin(PG_FUNCTION_ARGS)
474 : : {
2010 peter@eisentraut.org 475 : 5 : TransactionId xid = PG_GETARG_TRANSACTIONID(0);
476 : : ReplOriginId nodeid;
477 : : TimestampTz ts;
478 : : Datum values[2];
479 : : bool nulls[2];
480 : : TupleDesc tupdesc;
481 : : HeapTuple htup;
482 : : bool found;
483 : :
2123 michael@paquier.xyz 484 : 5 : found = TransactionIdGetCommitTsData(xid, &ts, &nodeid);
485 : :
1231 486 [ - + ]: 2 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1231 michael@paquier.xyz 487 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
488 : :
2123 michael@paquier.xyz 489 [ + - ]:CBC 2 : if (!found)
490 : : {
491 : 2 : memset(nulls, true, sizeof(nulls));
492 : : }
493 : : else
494 : : {
2123 michael@paquier.xyz 495 :UBC 0 : values[0] = TimestampTzGetDatum(ts);
496 : 0 : nulls[0] = false;
497 : :
498 : 0 : values[1] = ObjectIdGetDatum((Oid) nodeid);
499 : 0 : nulls[1] = false;
500 : : }
501 : :
2123 michael@paquier.xyz 502 :CBC 2 : htup = heap_form_tuple(tupdesc, values, nulls);
503 : :
504 : 2 : PG_RETURN_DATUM(HeapTupleGetDatum(htup));
505 : : }
506 : :
507 : : /*
508 : : * Number of shared CommitTS buffers.
509 : : *
510 : : * If asked to autotune, use 2MB for every 1GB of shared buffers, up to 8MB.
511 : : * Otherwise just cap the configured amount to be between 16 and the maximum
512 : : * allowed.
513 : : */
514 : : static int
4171 alvherre@alvh.no-ip. 515 : 2483 : CommitTsShmemBuffers(void)
516 : : {
517 : : /* auto-tune based on shared buffers */
797 518 [ + + ]: 2483 : if (commit_timestamp_buffers == 0)
519 : 1239 : return SimpleLruAutotuneBuffers(512, 1024);
520 : :
521 [ + - ]: 1244 : return Min(Max(16, commit_timestamp_buffers), SLRU_MAX_ALLOWED_BUFFERS);
522 : : }
523 : :
524 : : /*
525 : : * Register CommitTs shared memory needs at system startup (postmaster start
526 : : * or standalone backend)
527 : : */
528 : : static void
29 heikki.linnakangas@i 529 :GNC 1244 : CommitTsShmemRequest(void *arg)
530 : : {
531 : : /* If auto-tuning is requested, now is the time to do it */
797 alvherre@alvh.no-ip. 532 [ + + ]:CBC 1244 : if (commit_timestamp_buffers == 0)
533 : : {
534 : : char buf[32];
535 : :
536 : 1239 : snprintf(buf, sizeof(buf), "%d", CommitTsShmemBuffers());
537 : 1239 : SetConfigOption("commit_timestamp_buffers", buf, PGC_POSTMASTER,
538 : : PGC_S_DYNAMIC_DEFAULT);
539 : :
540 : : /*
541 : : * We prefer to report this value's source as PGC_S_DYNAMIC_DEFAULT.
542 : : * However, if the DBA explicitly set commit_timestamp_buffers = 0 in
543 : : * the config file, then PGC_S_DYNAMIC_DEFAULT will fail to override
544 : : * that and we must force the matter with PGC_S_OVERRIDE.
545 : : */
546 [ - + ]: 1239 : if (commit_timestamp_buffers == 0) /* failed to apply it? */
797 alvherre@alvh.no-ip. 547 :UBC 0 : SetConfigOption("commit_timestamp_buffers", buf, PGC_POSTMASTER,
548 : : PGC_S_OVERRIDE);
549 : : }
797 alvherre@alvh.no-ip. 550 [ - + ]:CBC 1244 : Assert(commit_timestamp_buffers != 0);
29 heikki.linnakangas@i 551 :GNC 1244 : SimpleLruRequest(.desc = &CommitTsSlruDesc,
552 : : .name = "commit_timestamp",
553 : : .Dir = "pg_commit_ts",
554 : : .long_segment_names = false,
555 : :
556 : : .nslots = CommitTsShmemBuffers(),
557 : :
558 : : .PagePrecedes = CommitTsPagePrecedes,
559 : : .errdetail_for_io_error = commit_ts_errdetail_for_io_error,
560 : :
561 : : .sync_handler = SYNC_HANDLER_COMMIT_TS,
562 : : .buffer_tranche_id = LWTRANCHE_COMMITTS_BUFFER,
563 : : .bank_tranche_id = LWTRANCHE_COMMITTS_SLRU,
564 : : );
565 : :
566 : 1244 : ShmemRequestStruct(.name = "CommitTs shared",
567 : : .size = sizeof(CommitTimestampShared),
568 : : .ptr = (void **) &commitTsShared,
569 : : );
570 : 1244 : }
571 : :
572 : : static void
573 : 1241 : CommitTsShmemInit(void *arg)
574 : : {
575 : 1241 : commitTsShared->xidLastCommit = InvalidTransactionId;
576 : 1241 : TIMESTAMP_NOBEGIN(commitTsShared->dataLastCommit.time);
577 : 1241 : commitTsShared->dataLastCommit.nodeid = InvalidReplOriginId;
578 : 1241 : commitTsShared->commitTsActive = false;
579 : :
580 : 1241 : SlruPagePrecedesUnitTests(CommitTsCtl, COMMIT_TS_XACTS_PER_PAGE);
4171 alvherre@alvh.no-ip. 581 :CBC 1241 : }
582 : :
583 : : /*
584 : : * GUC check_hook for commit_timestamp_buffers
585 : : */
586 : : bool
797 587 : 2525 : check_commit_ts_buffers(int *newval, void **extra, GucSource source)
588 : : {
589 : 2525 : return check_slru_buffers("commit_timestamp_buffers", newval);
590 : : }
591 : :
592 : : /*
593 : : * This function must be called ONCE on system install.
594 : : *
595 : : * (The CommitTs directory is assumed to have been created by initdb, and
596 : : * CommitTsShmemInit must have been called already.)
597 : : */
598 : : void
4171 599 : 57 : BootStrapCommitTs(void)
600 : : {
601 : : /*
602 : : * Nothing to do here at present, unlike most other SLRU modules; segments
603 : : * are created when the server is started with this module enabled. See
604 : : * ActivateCommitTs.
605 : : */
606 : 57 : }
607 : :
608 : : /*
609 : : * This must be called ONCE during postmaster or standalone-backend startup,
610 : : * after StartupXLOG has initialized TransamVariables->nextXid.
611 : : */
612 : : void
3798 613 : 14 : StartupCommitTs(void)
614 : : {
615 : 14 : ActivateCommitTs();
4171 616 : 14 : }
617 : :
618 : : /*
619 : : * This must be called ONCE during postmaster or standalone-backend startup,
620 : : * after recovery has finished.
621 : : */
622 : : void
4075 623 : 1010 : CompleteCommitTsInitialization(void)
624 : : {
625 : : /*
626 : : * If the feature is not enabled, turn it off for good. This also removes
627 : : * any leftover data.
628 : : *
629 : : * Conversely, we activate the module if the feature is enabled. This is
630 : : * necessary for primary and standby as the activation depends on the
631 : : * control file contents at the beginning of recovery or when a
632 : : * XLOG_PARAMETER_CHANGE is replayed.
633 : : */
634 [ + + ]: 1010 : if (!track_commit_timestamp)
3843 635 : 989 : DeactivateCommitTs();
636 : : else
3798 637 : 21 : ActivateCommitTs();
4075 638 : 1010 : }
639 : :
640 : : /*
641 : : * Activate or deactivate CommitTs' upon reception of a XLOG_PARAMETER_CHANGE
642 : : * XLog record during recovery.
643 : : */
644 : : void
3869 645 : 38 : CommitTsParameterChange(bool newvalue, bool oldvalue)
646 : : {
647 : : /*
648 : : * If the commit_ts module is disabled in this server and we get word from
649 : : * the primary server that it is enabled there, activate it so that we can
650 : : * replay future WAL records involving it; also mark it as active on
651 : : * pg_control. If the old value was already set, we already did this, so
652 : : * don't do anything.
653 : : *
654 : : * If the module is disabled in the primary, disable it here too, unless
655 : : * the module is enabled locally.
656 : : *
657 : : * Note this only runs in the recovery process, so an unlocked read is
658 : : * fine.
659 : : */
660 [ + + ]: 38 : if (newvalue)
661 : : {
3843 662 [ - + ]: 2 : if (!commitTsShared->commitTsActive)
3869 alvherre@alvh.no-ip. 663 :UBC 0 : ActivateCommitTs();
664 : : }
3843 alvherre@alvh.no-ip. 665 [ + + ]:CBC 36 : else if (commitTsShared->commitTsActive)
666 : 1 : DeactivateCommitTs();
3869 667 : 38 : }
668 : :
669 : : /*
670 : : * Activate this module whenever necessary.
671 : : * This must happen during postmaster or standalone-backend startup,
672 : : * or during WAL replay anytime the track_commit_timestamp setting is
673 : : * changed in the primary.
674 : : *
675 : : * The reason why this SLRU needs separate activation/deactivation functions is
676 : : * that it can be enabled/disabled during start and the activation/deactivation
677 : : * on the primary is propagated to the standby via replay. Other SLRUs don't
678 : : * have this property and they can be just initialized during normal startup.
679 : : *
680 : : * This is in charge of creating the currently active segment, if it's not
681 : : * already there. The reason for this is that the server might have been
682 : : * running with this module disabled for a while and thus might have skipped
683 : : * the normal creation point.
684 : : */
685 : : static void
4075 686 : 35 : ActivateCommitTs(void)
687 : : {
688 : : TransactionId xid;
689 : : int64 pageno;
690 : :
691 : : /*
692 : : * During bootstrap, we should not register commit timestamps so skip the
693 : : * activation in this case.
694 : : */
305 michael@paquier.xyz 695 [ + + ]: 35 : if (IsBootstrapProcessingMode())
696 : 2 : return;
697 : :
698 : : /* If we've done this already, there's nothing to do */
3798 alvherre@alvh.no-ip. 699 : 33 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
700 [ + + ]: 33 : if (commitTsShared->commitTsActive)
701 : : {
702 : 6 : LWLockRelease(CommitTsLock);
703 : 6 : return;
704 : : }
705 : 27 : LWLockRelease(CommitTsLock);
706 : :
879 heikki.linnakangas@i 707 : 27 : xid = XidFromFullTransactionId(TransamVariables->nextXid);
3798 alvherre@alvh.no-ip. 708 : 27 : pageno = TransactionIdToCTsPage(xid);
709 : :
710 : : /*
711 : : * Re-Initialize our idea of the latest page number.
712 : : */
819 713 : 27 : pg_atomic_write_u64(&CommitTsCtl->shared->latest_page_number, pageno);
714 : :
715 : : /*
716 : : * If CommitTs is enabled, but it wasn't in the previous server run, we
717 : : * need to set the oldest and newest values to the next Xid; that way, we
718 : : * will not try to read data that might not have been set.
719 : : *
720 : : * XXX does this have a problem if a server is started with commitTs
721 : : * enabled, then started with commitTs disabled, then restarted with it
722 : : * enabled again? It doesn't look like it does, because there should be a
723 : : * checkpoint that sets the value to InvalidTransactionId at end of
724 : : * recovery; and so any chance of injecting new transactions without
725 : : * CommitTs values would occur after the oldestCommitTsXid has been set to
726 : : * Invalid temporarily.
727 : : */
4171 728 : 27 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 729 [ + + ]: 27 : if (TransamVariables->oldestCommitTsXid == InvalidTransactionId)
730 : : {
731 : 15 : TransamVariables->oldestCommitTsXid =
732 : 15 : TransamVariables->newestCommitTsXid = ReadNextTransactionId();
733 : : }
4171 alvherre@alvh.no-ip. 734 : 27 : LWLockRelease(CommitTsLock);
735 : :
736 : : /* Create the current segment file, if necessary */
737 [ + + ]: 27 : if (!SimpleLruDoesPhysicalPageExist(CommitTsCtl, pageno))
302 alvherre@kurilemu.de 738 :GNC 13 : SimpleLruZeroAndWritePage(CommitTsCtl, pageno);
739 : :
740 : : /* Change the activation status in shared memory. */
3843 alvherre@alvh.no-ip. 741 :CBC 27 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
742 : 27 : commitTsShared->commitTsActive = true;
743 : 27 : LWLockRelease(CommitTsLock);
744 : : }
745 : :
746 : : /*
747 : : * Deactivate this module.
748 : : *
749 : : * This must be called when the track_commit_timestamp parameter is turned off.
750 : : * This happens during postmaster or standalone-backend startup, or during WAL
751 : : * replay.
752 : : *
753 : : * Resets CommitTs into invalid state to make sure we don't hand back
754 : : * possibly-invalid data; also removes segments of old data.
755 : : */
756 : : static void
757 : 990 : DeactivateCommitTs(void)
758 : : {
759 : : /*
760 : : * Cleanup the status in the shared memory.
761 : : *
762 : : * We reset everything in the commitTsShared record to prevent user from
763 : : * getting confusing data about last committed transaction on the standby
764 : : * when the module was activated repeatedly on the primary.
765 : : */
4075 766 : 990 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
767 : :
3843 768 : 990 : commitTsShared->commitTsActive = false;
769 : 990 : commitTsShared->xidLastCommit = InvalidTransactionId;
770 : 990 : TIMESTAMP_NOBEGIN(commitTsShared->dataLastCommit.time);
97 msawada@postgresql.o 771 :GNC 990 : commitTsShared->dataLastCommit.nodeid = InvalidReplOriginId;
772 : :
879 heikki.linnakangas@i 773 :CBC 990 : TransamVariables->oldestCommitTsXid = InvalidTransactionId;
774 : 990 : TransamVariables->newestCommitTsXid = InvalidTransactionId;
775 : :
776 : : /*
777 : : * Remove *all* files. This is necessary so that there are no leftover
778 : : * files; in the case where this feature is later enabled after running
779 : : * with it disabled for some time there may be a gap in the file sequence.
780 : : * (We can probably tolerate out-of-sequence files, as they are going to
781 : : * be overwritten anyway when we wrap around, but it seems better to be
782 : : * tidy.)
783 : : *
784 : : * Note that we do this with CommitTsLock acquired in exclusive mode. This
785 : : * is very heavy-handed, but since this routine can only be called in the
786 : : * replica and should happen very rarely, we don't worry too much about
787 : : * it. Note also that no process should be consulting this SLRU if we
788 : : * have just deactivated it.
789 : : */
3869 alvherre@alvh.no-ip. 790 : 990 : (void) SlruScanDirectory(CommitTsCtl, SlruScanDirCbDeleteAll, NULL);
791 : :
797 792 : 990 : LWLockRelease(CommitTsLock);
4075 793 : 990 : }
794 : :
795 : : /*
796 : : * Perform a checkpoint --- either during shutdown, or on-the-fly
797 : : */
798 : : void
4171 799 : 1944 : CheckPointCommitTs(void)
800 : : {
801 : : /*
802 : : * Write dirty CommitTs pages to disk. This may result in sync requests
803 : : * queued for later handling by ProcessSyncRequests(), as part of the
804 : : * checkpoint.
805 : : */
2048 tmunro@postgresql.or 806 : 1944 : SimpleLruWriteAll(CommitTsCtl, true);
4171 alvherre@alvh.no-ip. 807 : 1944 : }
808 : :
809 : : /*
810 : : * Make sure that CommitTs has room for a newly-allocated XID.
811 : : *
812 : : * NB: this is called while holding XidGenLock. We want it to be very fast
813 : : * most of the time; even when it's not so fast, no actual I/O need happen
814 : : * unless we're forced to write out a dirty CommitTs or xlog page to make room
815 : : * in shared memory.
816 : : *
817 : : * NB: the current implementation relies on track_commit_timestamp being
818 : : * PGC_POSTMASTER.
819 : : */
820 : : void
821 : 171470 : ExtendCommitTs(TransactionId newestXact)
822 : : {
823 : : int64 pageno;
824 : : LWLock *lock;
825 : :
826 : : /*
827 : : * Nothing to do if module not enabled. Note we do an unlocked read of
828 : : * the flag here, which is okay because this routine is only called from
829 : : * GetNewTransactionId, which is never called in a standby.
830 : : */
3843 831 [ - + ]: 171470 : Assert(!InRecovery);
832 [ + + ]: 171470 : if (!commitTsShared->commitTsActive)
4171 833 : 170616 : return;
834 : :
835 : : /*
836 : : * No work except at first XID of a page. But beware: just after
837 : : * wraparound, the first XID of page zero is FirstNormalTransactionId.
838 : : */
839 [ + - + + ]: 854 : if (TransactionIdToCTsEntry(newestXact) != 0 &&
840 : : !TransactionIdEquals(newestXact, FirstNormalTransactionId))
841 : 853 : return;
842 : :
843 : 1 : pageno = TransactionIdToCTsPage(newestXact);
844 : :
797 845 : 1 : lock = SimpleLruGetBankLock(CommitTsCtl, pageno);
846 : :
847 : 1 : LWLockAcquire(lock, LW_EXCLUSIVE);
848 : :
849 : : /* Zero the page ... */
302 alvherre@kurilemu.de 850 :GNC 1 : SimpleLruZeroPage(CommitTsCtl, pageno);
851 : :
852 : : /* and make a WAL entry about that, unless we're in REDO */
853 [ + - ]: 1 : if (!InRecovery)
854 : 1 : XLogSimpleInsertInt64(RM_COMMIT_TS_ID, COMMIT_TS_ZEROPAGE, pageno);
855 : :
797 alvherre@alvh.no-ip. 856 :CBC 1 : LWLockRelease(lock);
857 : : }
858 : :
859 : : /*
860 : : * Remove all CommitTs segments before the one holding the passed
861 : : * transaction ID.
862 : : *
863 : : * Note that we don't need to flush XLOG here.
864 : : */
865 : : void
3843 866 : 114 : TruncateCommitTs(TransactionId oldestXact)
867 : : {
868 : : int64 cutoffPage;
869 : :
870 : : /*
871 : : * The cutoff point is the start of the segment containing oldestXact. We
872 : : * pass the *page* containing oldestXact to SimpleLruTruncate.
873 : : */
4171 874 : 114 : cutoffPage = TransactionIdToCTsPage(oldestXact);
875 : :
876 : : /* Check to see if there's any files that could be removed */
877 [ + - ]: 114 : if (!SlruScanDirectory(CommitTsCtl, SlruScanDirCbReportPresence,
878 : : &cutoffPage))
879 : 114 : return; /* nothing to remove */
880 : :
881 : : /* Write XLOG record */
3393 alvherre@alvh.no-ip. 882 :UBC 0 : WriteTruncateXlogRec(cutoffPage, oldestXact);
883 : :
884 : : /* Now we can remove the old CommitTs segment(s) */
4171 885 : 0 : SimpleLruTruncate(CommitTsCtl, cutoffPage);
886 : : }
887 : :
888 : : /*
889 : : * Set the limit values between which commit TS can be consulted.
890 : : */
891 : : void
4171 alvherre@alvh.no-ip. 892 :CBC 1136 : SetCommitTsLimit(TransactionId oldestXact, TransactionId newestXact)
893 : : {
894 : : /*
895 : : * Be careful not to overwrite values that are either further into the
896 : : * "future" or signal a disabled committs.
897 : : */
898 : 1136 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 899 [ - + ]: 1136 : if (TransamVariables->oldestCommitTsXid != InvalidTransactionId)
900 : : {
879 heikki.linnakangas@i 901 [ # # ]:UBC 0 : if (TransactionIdPrecedes(TransamVariables->oldestCommitTsXid, oldestXact))
902 : 0 : TransamVariables->oldestCommitTsXid = oldestXact;
903 [ # # ]: 0 : if (TransactionIdPrecedes(newestXact, TransamVariables->newestCommitTsXid))
904 : 0 : TransamVariables->newestCommitTsXid = newestXact;
905 : : }
906 : : else
907 : : {
879 heikki.linnakangas@i 908 [ - + ]:CBC 1136 : Assert(TransamVariables->newestCommitTsXid == InvalidTransactionId);
909 : 1136 : TransamVariables->oldestCommitTsXid = oldestXact;
910 : 1136 : TransamVariables->newestCommitTsXid = newestXact;
911 : : }
4171 alvherre@alvh.no-ip. 912 : 1136 : LWLockRelease(CommitTsLock);
913 : 1136 : }
914 : :
915 : : /*
916 : : * Move forwards the oldest commitTS value that can be consulted
917 : : */
918 : : void
3781 mail@joeconway.com 919 : 114 : AdvanceOldestCommitTsXid(TransactionId oldestXact)
920 : : {
4171 alvherre@alvh.no-ip. 921 : 114 : LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
879 heikki.linnakangas@i 922 [ + + + - ]: 115 : if (TransamVariables->oldestCommitTsXid != InvalidTransactionId &&
923 : 1 : TransactionIdPrecedes(TransamVariables->oldestCommitTsXid, oldestXact))
924 : 1 : TransamVariables->oldestCommitTsXid = oldestXact;
4171 alvherre@alvh.no-ip. 925 : 114 : LWLockRelease(CommitTsLock);
926 : 114 : }
927 : :
928 : :
929 : : /*
930 : : * Decide whether a commitTS page number is "older" for truncation purposes.
931 : : * Analogous to CLOGPagePrecedes().
932 : : *
933 : : * At default BLCKSZ, (1 << 31) % COMMIT_TS_XACTS_PER_PAGE == 128. This
934 : : * introduces differences compared to CLOG and the other SLRUs having (1 <<
935 : : * 31) % per_page == 0. This function never tests exactly
936 : : * TransactionIdPrecedes(x-2^31, x). When the system reaches xidStopLimit,
937 : : * there are two possible counts of page boundaries between oldestXact and the
938 : : * latest XID assigned, depending on whether oldestXact is within the first
939 : : * 128 entries of its page. Since this function doesn't know the location of
940 : : * oldestXact within page2, it returns false for one page that actually is
941 : : * expendable. This is a wider (yet still negligible) version of the
942 : : * truncation opportunity that CLOGPagePrecedes() cannot recognize.
943 : : *
944 : : * For the sake of a worked example, number entries with decimal values such
945 : : * that page1==1 entries range from 1.0 to 1.999. Let N+0.15 be the number of
946 : : * pages that 2^31 entries will span (N is an integer). If oldestXact=N+2.1,
947 : : * then the final safe XID assignment leaves newestXact=1.95. We keep page 2,
948 : : * because entry=2.85 is the border that toggles whether entries precede the
949 : : * last entry of the oldestXact page. While page 2 is expendable at
950 : : * oldestXact=N+2.1, it would be precious at oldestXact=N+2.9.
951 : : */
952 : : static bool
888 akorotkov@postgresql 953 : 48400 : CommitTsPagePrecedes(int64 page1, int64 page2)
954 : : {
955 : : TransactionId xid1;
956 : : TransactionId xid2;
957 : :
4171 alvherre@alvh.no-ip. 958 : 48400 : xid1 = ((TransactionId) page1) * COMMIT_TS_XACTS_PER_PAGE;
1935 noah@leadboat.com 959 : 48400 : xid1 += FirstNormalTransactionId + 1;
4171 alvherre@alvh.no-ip. 960 : 48400 : xid2 = ((TransactionId) page2) * COMMIT_TS_XACTS_PER_PAGE;
1935 noah@leadboat.com 961 : 48400 : xid2 += FirstNormalTransactionId + 1;
962 : :
963 [ + + + + ]: 76943 : return (TransactionIdPrecedes(xid1, xid2) &&
964 : 28543 : TransactionIdPrecedes(xid1, xid2 + COMMIT_TS_XACTS_PER_PAGE - 1));
965 : : }
966 : :
967 : : static int
53 heikki.linnakangas@i 968 :UNC 0 : commit_ts_errdetail_for_io_error(const void *opaque_data)
969 : : {
970 : 0 : TransactionId xid = *(const TransactionId *) opaque_data;
971 : :
972 : 0 : return errdetail("Could not access commit timestamp of transaction %u.", xid);
53 heikki.linnakangas@i 973 :ECB (1) : }
974 : :
975 : : /*
976 : : * Write a TRUNCATE xlog record
977 : : */
978 : : static void
888 akorotkov@postgresql 979 :UBC 0 : WriteTruncateXlogRec(int64 pageno, TransactionId oldestXid)
980 : : {
981 : : xl_commit_ts_truncate xlrec;
982 : :
3393 alvherre@alvh.no-ip. 983 : 0 : xlrec.pageno = pageno;
984 : 0 : xlrec.oldestXid = oldestXid;
985 : :
4171 986 : 0 : XLogBeginInsert();
448 peter@eisentraut.org 987 : 0 : XLogRegisterData(&xlrec, SizeOfCommitTsTruncate);
4171 alvherre@alvh.no-ip. 988 : 0 : (void) XLogInsert(RM_COMMIT_TS_ID, COMMIT_TS_TRUNCATE);
989 : 0 : }
990 : :
991 : : /*
992 : : * CommitTS resource manager's routines
993 : : */
994 : : void
995 : 0 : commit_ts_redo(XLogReaderState *record)
996 : : {
997 : 0 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
998 : :
999 : : /* Backup blocks are not used in commit_ts records */
1000 [ # # ]: 0 : Assert(!XLogRecHasAnyBlockRefs(record));
1001 : :
1002 [ # # ]: 0 : if (info == COMMIT_TS_ZEROPAGE)
1003 : : {
1004 : : int64 pageno;
1005 : :
888 akorotkov@postgresql 1006 : 0 : memcpy(&pageno, XLogRecGetData(record), sizeof(pageno));
302 alvherre@kurilemu.de 1007 :UNC 0 : SimpleLruZeroAndWritePage(CommitTsCtl, pageno);
1008 : : }
4171 alvherre@alvh.no-ip. 1009 [ # # ]:UBC 0 : else if (info == COMMIT_TS_TRUNCATE)
1010 : : {
3393 1011 : 0 : xl_commit_ts_truncate *trunc = (xl_commit_ts_truncate *) XLogRecGetData(record);
1012 : :
1013 : 0 : AdvanceOldestCommitTsXid(trunc->oldestXid);
1014 : :
1015 : : /*
1016 : : * During XLOG replay, latest_page_number isn't set up yet; insert a
1017 : : * suitable value to bypass the sanity test in SimpleLruTruncate.
1018 : : */
819 1019 : 0 : pg_atomic_write_u64(&CommitTsCtl->shared->latest_page_number,
1020 : 0 : trunc->pageno);
1021 : :
3393 1022 : 0 : SimpleLruTruncate(CommitTsCtl, trunc->pageno);
1023 : : }
1024 : : else
4171 1025 [ # # ]: 0 : elog(PANIC, "commit_ts_redo: unknown op code %u", info);
1026 : 0 : }
1027 : :
1028 : : /*
1029 : : * Entrypoint for sync.c to sync commit_ts files.
1030 : : */
1031 : : int
2048 tmunro@postgresql.or 1032 : 0 : committssyncfiletag(const FileTag *ftag, char *path)
1033 : : {
1034 : 0 : return SlruSyncFileTag(CommitTsCtl, ftag, path);
1035 : : }
|