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