PostgreSQL Source Code git master
timeline.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * timeline.c
4 * Functions for reading and writing timeline history files.
5 *
6 * A timeline history file lists the timeline changes of the timeline, in
7 * a simple text format. They are archived along with the WAL segments.
8 *
9 * The files are named like "<tli>.history". For example, if the database
10 * starts up and switches to timeline 5, the timeline history file would be
11 * called "00000005.history".
12 *
13 * Each line in the file represents a timeline switch:
14 *
15 * <parentTLI> <switchpoint> <reason>
16 *
17 * parentTLI ID of the parent timeline
18 * switchpoint XLogRecPtr of the WAL location where the switch happened
19 * reason human-readable explanation of why the timeline was changed
20 *
21 * The fields are separated by tabs. Lines beginning with # are comments, and
22 * are ignored. Empty lines are also ignored.
23 *
24 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
25 * Portions Copyright (c) 1994, Regents of the University of California
26 *
27 * src/backend/access/transam/timeline.c
28 *
29 *-------------------------------------------------------------------------
30 */
31
32#include "postgres.h"
33
34#include <sys/stat.h>
35#include <unistd.h>
36
37#include "access/timeline.h"
38#include "access/xlog.h"
40#include "access/xlogarchive.h"
41#include "access/xlogdefs.h"
42#include "pgstat.h"
43#include "storage/fd.h"
44
45/*
46 * Copies all timeline history files with id's between 'begin' and 'end'
47 * from archive to pg_wal.
48 */
49void
51{
52 char path[MAXPGPATH];
53 char histfname[MAXFNAMELEN];
54 TimeLineID tli;
55
56 for (tli = begin; tli < end; tli++)
57 {
58 if (tli == 1)
59 continue;
60
61 TLHistoryFileName(histfname, tli);
62 if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
63 KeepFileRestoredFromArchive(path, histfname);
64 }
65}
66
67/*
68 * Try to read a timeline's history file.
69 *
70 * If successful, return the list of component TLIs (the given TLI followed by
71 * its ancestor TLIs). If we can't find the history file, assume that the
72 * timeline has no parents, and return a list of just the specified timeline
73 * ID.
74 */
75List *
77{
78 List *result;
79 char path[MAXPGPATH];
80 char histfname[MAXFNAMELEN];
81 FILE *fd;
83 TimeLineID lasttli = 0;
84 XLogRecPtr prevend;
85 bool fromArchive = false;
86
87 /* Timeline 1 does not have a history file, so no need to check */
88 if (targetTLI == 1)
89 {
91 entry->tli = targetTLI;
92 entry->begin = entry->end = InvalidXLogRecPtr;
93 return list_make1(entry);
94 }
95
97 {
98 TLHistoryFileName(histfname, targetTLI);
99 fromArchive =
100 RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
101 }
102 else
103 TLHistoryFilePath(path, targetTLI);
104
105 fd = AllocateFile(path, "r");
106 if (fd == NULL)
107 {
108 if (errno != ENOENT)
111 errmsg("could not open file \"%s\": %m", path)));
112 /* Not there, so assume no parents */
114 entry->tli = targetTLI;
115 entry->begin = entry->end = InvalidXLogRecPtr;
116 return list_make1(entry);
117 }
118
119 result = NIL;
120
121 /*
122 * Parse the file...
123 */
124 prevend = InvalidXLogRecPtr;
125 for (;;)
126 {
127 char fline[MAXPGPATH];
128 char *res;
129 char *ptr;
130 TimeLineID tli;
131 uint32 switchpoint_hi;
132 uint32 switchpoint_lo;
133 int nfields;
134
135 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
136 res = fgets(fline, sizeof(fline), fd);
138 if (res == NULL)
139 {
140 if (ferror(fd))
143 errmsg("could not read file \"%s\": %m", path)));
144
145 break;
146 }
147
148 /* skip leading whitespace and check for # comment */
149 for (ptr = fline; *ptr; ptr++)
150 {
151 if (!isspace((unsigned char) *ptr))
152 break;
153 }
154 if (*ptr == '\0' || *ptr == '#')
155 continue;
156
157 nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);
158
159 if (nfields < 1)
160 {
161 /* expect a numeric timeline ID as first field of line */
163 (errmsg("syntax error in history file: %s", fline),
164 errhint("Expected a numeric timeline ID.")));
165 }
166 if (nfields != 3)
168 (errmsg("syntax error in history file: %s", fline),
169 errhint("Expected a write-ahead log switchpoint location.")));
170
171 if (result && tli <= lasttli)
173 (errmsg("invalid data in history file: %s", fline),
174 errhint("Timeline IDs must be in increasing sequence.")));
175
176 lasttli = tli;
177
179 entry->tli = tli;
180 entry->begin = prevend;
181 entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
182 prevend = entry->end;
183
184 /* Build list with newest item first */
185 result = lcons(entry, result);
186
187 /* we ignore the remainder of each line */
188 }
189
190 FreeFile(fd);
191
192 if (result && targetTLI <= lasttli)
194 (errmsg("invalid data in history file \"%s\"", path),
195 errhint("Timeline IDs must be less than child timeline's ID.")));
196
197 /*
198 * Create one more entry for the "tip" of the timeline, which has no entry
199 * in the history file.
200 */
202 entry->tli = targetTLI;
203 entry->begin = prevend;
204 entry->end = InvalidXLogRecPtr;
205
206 result = lcons(entry, result);
207
208 /*
209 * If the history file was fetched from archive, save it in pg_wal for
210 * future reference.
211 */
212 if (fromArchive)
213 KeepFileRestoredFromArchive(path, histfname);
214
215 return result;
216}
217
218/*
219 * Probe whether a timeline history file exists for the given timeline ID
220 */
221bool
223{
224 char path[MAXPGPATH];
225 char histfname[MAXFNAMELEN];
226 FILE *fd;
227
228 /* Timeline 1 does not have a history file, so no need to check */
229 if (probeTLI == 1)
230 return false;
231
233 {
234 TLHistoryFileName(histfname, probeTLI);
235 RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
236 }
237 else
238 TLHistoryFilePath(path, probeTLI);
239
240 fd = AllocateFile(path, "r");
241 if (fd != NULL)
242 {
243 FreeFile(fd);
244 return true;
245 }
246 else
247 {
248 if (errno != ENOENT)
251 errmsg("could not open file \"%s\": %m", path)));
252 return false;
253 }
254}
255
256/*
257 * Find the newest existing timeline, assuming that startTLI exists.
258 *
259 * Note: while this is somewhat heuristic, it does positively guarantee
260 * that (result + 1) is not a known timeline, and therefore it should
261 * be safe to assign that ID to a new timeline.
262 */
265{
266 TimeLineID newestTLI;
267 TimeLineID probeTLI;
268
269 /*
270 * The algorithm is just to probe for the existence of timeline history
271 * files. XXX is it useful to allow gaps in the sequence?
272 */
273 newestTLI = startTLI;
274
275 for (probeTLI = startTLI + 1;; probeTLI++)
276 {
277 if (existsTimeLineHistory(probeTLI))
278 {
279 newestTLI = probeTLI; /* probeTLI exists */
280 }
281 else
282 {
283 /* doesn't exist, assume we're done */
284 break;
285 }
286 }
287
288 return newestTLI;
289}
290
291/*
292 * Create a new timeline history file.
293 *
294 * newTLI: ID of the new timeline
295 * parentTLI: ID of its immediate parent
296 * switchpoint: WAL location where the system switched to the new timeline
297 * reason: human-readable explanation of why the timeline was switched
298 *
299 * Currently this is only used at the end recovery, and so there are no locking
300 * considerations. But we should be just as tense as XLogFileInit to avoid
301 * emplacing a bogus file.
302 */
303void
305 XLogRecPtr switchpoint, char *reason)
306{
307 char path[MAXPGPATH];
308 char tmppath[MAXPGPATH];
309 char histfname[MAXFNAMELEN];
310 char buffer[BLCKSZ];
311 int srcfd;
312 int fd;
313 int nbytes;
314
315 Assert(newTLI > parentTLI); /* else bad selection of newTLI */
316
317 /*
318 * Write into a temp file name.
319 */
320 snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
321
322 unlink(tmppath);
323
324 /* do not use get_sync_bit() here --- want to fsync only at end of fill */
325 fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
326 if (fd < 0)
329 errmsg("could not create file \"%s\": %m", tmppath)));
330
331 /*
332 * If a history file exists for the parent, copy it verbatim
333 */
335 {
336 TLHistoryFileName(histfname, parentTLI);
337 RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
338 }
339 else
340 TLHistoryFilePath(path, parentTLI);
341
342 srcfd = OpenTransientFile(path, O_RDONLY);
343 if (srcfd < 0)
344 {
345 if (errno != ENOENT)
348 errmsg("could not open file \"%s\": %m", path)));
349 /* Not there, so assume parent has no parents */
350 }
351 else
352 {
353 for (;;)
354 {
355 errno = 0;
356 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
357 nbytes = (int) read(srcfd, buffer, sizeof(buffer));
359 if (nbytes < 0 || errno != 0)
362 errmsg("could not read file \"%s\": %m", path)));
363 if (nbytes == 0)
364 break;
365 errno = 0;
366 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
367 if ((int) write(fd, buffer, nbytes) != nbytes)
368 {
369 int save_errno = errno;
370
371 /*
372 * If we fail to make the file, delete it to release disk
373 * space
374 */
375 unlink(tmppath);
376
377 /*
378 * if write didn't set errno, assume problem is no disk space
379 */
380 errno = save_errno ? save_errno : ENOSPC;
381
384 errmsg("could not write to file \"%s\": %m", tmppath)));
385 }
387 }
388
389 if (CloseTransientFile(srcfd) != 0)
392 errmsg("could not close file \"%s\": %m", path)));
393 }
394
395 /*
396 * Append one line with the details of this timeline split.
397 *
398 * If we did have a parent file, insert an extra newline just in case the
399 * parent file failed to end with one.
400 */
401 snprintf(buffer, sizeof(buffer),
402 "%s%u\t%X/%X\t%s\n",
403 (srcfd < 0) ? "" : "\n",
404 parentTLI,
405 LSN_FORMAT_ARGS(switchpoint),
406 reason);
407
408 nbytes = strlen(buffer);
409 errno = 0;
410 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
411 if ((int) write(fd, buffer, nbytes) != nbytes)
412 {
413 int save_errno = errno;
414
415 /*
416 * If we fail to make the file, delete it to release disk space
417 */
418 unlink(tmppath);
419 /* if write didn't set errno, assume problem is no disk space */
420 errno = save_errno ? save_errno : ENOSPC;
421
424 errmsg("could not write to file \"%s\": %m", tmppath)));
425 }
427
428 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC);
429 if (pg_fsync(fd) != 0)
432 errmsg("could not fsync file \"%s\": %m", tmppath)));
434
435 if (CloseTransientFile(fd) != 0)
438 errmsg("could not close file \"%s\": %m", tmppath)));
439
440 /*
441 * Now move the completed history file into place with its final name.
442 */
443 TLHistoryFilePath(path, newTLI);
444 Assert(access(path, F_OK) != 0 && errno == ENOENT);
445 durable_rename(tmppath, path, ERROR);
446
447 /* The history file can be archived immediately. */
449 {
450 TLHistoryFileName(histfname, newTLI);
451 XLogArchiveNotify(histfname);
452 }
453}
454
455/*
456 * Writes a history file for given timeline and contents.
457 *
458 * Currently this is only used in the walreceiver process, and so there are
459 * no locking considerations. But we should be just as tense as XLogFileInit
460 * to avoid emplacing a bogus file.
461 */
462void
464{
465 char path[MAXPGPATH];
466 char tmppath[MAXPGPATH];
467 int fd;
468
469 /*
470 * Write into a temp file name.
471 */
472 snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
473
474 unlink(tmppath);
475
476 /* do not use get_sync_bit() here --- want to fsync only at end of fill */
477 fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
478 if (fd < 0)
481 errmsg("could not create file \"%s\": %m", tmppath)));
482
483 errno = 0;
484 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE);
485 if ((int) write(fd, content, size) != size)
486 {
487 int save_errno = errno;
488
489 /*
490 * If we fail to make the file, delete it to release disk space
491 */
492 unlink(tmppath);
493 /* if write didn't set errno, assume problem is no disk space */
494 errno = save_errno ? save_errno : ENOSPC;
495
498 errmsg("could not write to file \"%s\": %m", tmppath)));
499 }
501
502 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC);
503 if (pg_fsync(fd) != 0)
506 errmsg("could not fsync file \"%s\": %m", tmppath)));
508
509 if (CloseTransientFile(fd) != 0)
512 errmsg("could not close file \"%s\": %m", tmppath)));
513
514 /*
515 * Now move the completed history file into place with its final name,
516 * replacing any existing file with the same name.
517 */
518 TLHistoryFilePath(path, tli);
519 durable_rename(tmppath, path, ERROR);
520}
521
522/*
523 * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
524 */
525bool
527{
528 ListCell *cell;
529
530 foreach(cell, expectedTLEs)
531 {
532 if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
533 return true;
534 }
535
536 return false;
537}
538
539/*
540 * Returns the ID of the timeline in use at a particular point in time, in
541 * the given timeline history.
542 */
545{
546 ListCell *cell;
547
548 foreach(cell, history)
549 {
551
552 if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
553 (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
554 {
555 /* found it */
556 return tle->tli;
557 }
558 }
559
560 /* shouldn't happen. */
561 elog(ERROR, "timeline history was not contiguous");
562 return 0; /* keep compiler quiet */
563}
564
565/*
566 * Returns the point in history where we branched off the given timeline,
567 * and the timeline we branched to (*nextTLI). Returns InvalidXLogRecPtr if
568 * the timeline is current, ie. we have not branched off from it, and throws
569 * an error if the timeline is not part of this server's history.
570 */
572tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
573{
574 ListCell *cell;
575
576 if (nextTLI)
577 *nextTLI = 0;
578 foreach(cell, history)
579 {
581
582 if (tle->tli == tli)
583 return tle->end;
584 if (nextTLI)
585 *nextTLI = tle->tli;
586 }
587
589 (errmsg("requested timeline %u is not in this server's history",
590 tli)));
591 return InvalidXLogRecPtr; /* keep compiler quiet */
592}
List * readTimeLineHistory(TimeLineID targetTLI)
Definition: timeline.c:76
TimeLineID findNewestTimeLine(TimeLineID startTLI)
Definition: timeline.c:264
void writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
Definition: timeline.c:463
TimeLineID tliOfPointInHistory(XLogRecPtr ptr, List *history)
Definition: timeline.c:544
XLogRecPtr tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
Definition: timeline.c:572
bool existsTimeLineHistory(TimeLineID probeTLI)
Definition: timeline.c:222
void restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
Definition: timeline.c:50
void writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, XLogRecPtr switchpoint, char *reason)
Definition: timeline.c:304
bool tliInHistory(TimeLineID tli, List *expectedTLEs)
Definition: timeline.c:526
#define Assert(condition)
Definition: c.h:815
uint64_t uint64
Definition: c.h:489
uint32_t uint32
Definition: c.h:488
int errcode_for_file_access(void)
Definition: elog.c:876
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define FATAL
Definition: elog.h:41
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:781
int CloseTransientFile(int fd)
Definition: fd.c:2831
int FreeFile(FILE *file)
Definition: fd.c:2803
int data_sync_elevel(int elevel)
Definition: fd.c:3959
int pg_fsync(int fd)
Definition: fd.c:385
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2605
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2655
#define write(a, b, c)
Definition: win32.h:14
#define read(a, b, c)
Definition: win32.h:13
List * lcons(void *datum, List *list)
Definition: list.c:495
void * palloc(Size size)
Definition: mcxt.c:1317
#define MAXPGPATH
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
#define snprintf
Definition: port.h:238
static int fd(const char *x, int i)
Definition: preproc-init.c:105
short access
Definition: preproc-type.c:36
static pg_noinline void Size size
Definition: slab.c:607
Definition: pg_list.h:54
XLogRecPtr begin
Definition: timeline.h:28
TimeLineID tli
Definition: timeline.h:27
XLogRecPtr end
Definition: timeline.h:29
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: wait_event.h:85
static void pgstat_report_wait_end(void)
Definition: wait_event.h:101
#define XLogArchivingActive()
Definition: xlog.h:99
#define MAXFNAMELEN
#define XLOGDIR
static void TLHistoryFilePath(char *path, TimeLineID tli)
static void TLHistoryFileName(char *fname, TimeLineID tli)
bool RestoreArchivedFile(char *path, const char *xlogfname, const char *recovername, off_t expectedSize, bool cleanupEnabled)
Definition: xlogarchive.c:54
void XLogArchiveNotify(const char *xlog)
Definition: xlogarchive.c:444
void KeepFileRestoredFromArchive(const char *path, const char *xlogfname)
Definition: xlogarchive.c:358
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
uint32 TimeLineID
Definition: xlogdefs.h:59
bool ArchiveRecoveryRequested
Definition: xlogrecovery.c:137
static List * expectedTLEs
Definition: xlogrecovery.c:123