1 /*-------------------------------------------------------------------------
4 * Functions for reading and writing timeline history files.
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.
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".
13 * Each line in the file represents a timeline switch:
15 * <parentTLI> <switchpoint> <reason>
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
21 * The fields are separated by tabs. Lines beginning with # are comments, and
22 * are ignored. Empty lines are also ignored.
24 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
25 * Portions Copyright (c) 1994, Regents of the University of California
27 * src/backend/access/transam/timeline.c
29 *-------------------------------------------------------------------------
37 #include "access/timeline.h"
38 #include "access/xlog.h"
39 #include "access/xlog_internal.h"
40 #include "access/xlogarchive.h"
41 #include "access/xlogdefs.h"
43 #include "storage/fd.h"
46 * Copies all timeline history files with id's between 'begin' and 'end'
47 * from archive to pg_wal.
50 restoreTimeLineHistoryFiles(TimeLineID begin
, TimeLineID end
)
53 char histfname
[MAXFNAMELEN
];
56 for (tli
= begin
; tli
< end
; tli
++)
61 TLHistoryFileName(histfname
, tli
);
62 if (RestoreArchivedFile(path
, histfname
, "RECOVERYHISTORY", 0, false))
63 KeepFileRestoredFromArchive(path
, histfname
);
68 * Try to read a timeline's history file.
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
76 readTimeLineHistory(TimeLineID targetTLI
)
80 char histfname
[MAXFNAMELEN
];
82 TimeLineHistoryEntry
*entry
;
83 TimeLineID lasttli
= 0;
85 bool fromArchive
= false;
87 /* Timeline 1 does not have a history file, so no need to check */
90 entry
= (TimeLineHistoryEntry
*) palloc(sizeof(TimeLineHistoryEntry
));
91 entry
->tli
= targetTLI
;
92 entry
->begin
= entry
->end
= InvalidXLogRecPtr
;
93 return list_make1(entry
);
96 if (ArchiveRecoveryRequested
)
98 TLHistoryFileName(histfname
, targetTLI
);
100 RestoreArchivedFile(path
, histfname
, "RECOVERYHISTORY", 0, false);
103 TLHistoryFilePath(path
, targetTLI
);
105 fd
= AllocateFile(path
, "r");
110 (errcode_for_file_access(),
111 errmsg("could not open file \"%s\": %m", path
)));
112 /* Not there, so assume no parents */
113 entry
= (TimeLineHistoryEntry
*) palloc(sizeof(TimeLineHistoryEntry
));
114 entry
->tli
= targetTLI
;
115 entry
->begin
= entry
->end
= InvalidXLogRecPtr
;
116 return list_make1(entry
);
124 prevend
= InvalidXLogRecPtr
;
127 char fline
[MAXPGPATH
];
131 uint32 switchpoint_hi
;
132 uint32 switchpoint_lo
;
135 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ
);
136 res
= fgets(fline
, sizeof(fline
), fd
);
137 pgstat_report_wait_end();
142 (errcode_for_file_access(),
143 errmsg("could not read file \"%s\": %m", path
)));
148 /* skip leading whitespace and check for # comment */
149 for (ptr
= fline
; *ptr
; ptr
++)
151 if (!isspace((unsigned char) *ptr
))
154 if (*ptr
== '\0' || *ptr
== '#')
157 nfields
= sscanf(fline
, "%u\t%X/%X", &tli
, &switchpoint_hi
, &switchpoint_lo
);
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.")));
168 (errmsg("syntax error in history file: %s", fline
),
169 errhint("Expected a write-ahead log switchpoint location.")));
171 if (result
&& tli
<= lasttli
)
173 (errmsg("invalid data in history file: %s", fline
),
174 errhint("Timeline IDs must be in increasing sequence.")));
178 entry
= (TimeLineHistoryEntry
*) palloc(sizeof(TimeLineHistoryEntry
));
180 entry
->begin
= prevend
;
181 entry
->end
= ((uint64
) (switchpoint_hi
)) << 32 | (uint64
) switchpoint_lo
;
182 prevend
= entry
->end
;
184 /* Build list with newest item first */
185 result
= lcons(entry
, result
);
187 /* we ignore the remainder of each line */
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.")));
198 * Create one more entry for the "tip" of the timeline, which has no entry
199 * in the history file.
201 entry
= (TimeLineHistoryEntry
*) palloc(sizeof(TimeLineHistoryEntry
));
202 entry
->tli
= targetTLI
;
203 entry
->begin
= prevend
;
204 entry
->end
= InvalidXLogRecPtr
;
206 result
= lcons(entry
, result
);
209 * If the history file was fetched from archive, save it in pg_wal for
213 KeepFileRestoredFromArchive(path
, histfname
);
219 * Probe whether a timeline history file exists for the given timeline ID
222 existsTimeLineHistory(TimeLineID probeTLI
)
224 char path
[MAXPGPATH
];
225 char histfname
[MAXFNAMELEN
];
228 /* Timeline 1 does not have a history file, so no need to check */
232 if (ArchiveRecoveryRequested
)
234 TLHistoryFileName(histfname
, probeTLI
);
235 RestoreArchivedFile(path
, histfname
, "RECOVERYHISTORY", 0, false);
238 TLHistoryFilePath(path
, probeTLI
);
240 fd
= AllocateFile(path
, "r");
250 (errcode_for_file_access(),
251 errmsg("could not open file \"%s\": %m", path
)));
257 * Find the newest existing timeline, assuming that startTLI exists.
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.
264 findNewestTimeLine(TimeLineID startTLI
)
266 TimeLineID newestTLI
;
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?
273 newestTLI
= startTLI
;
275 for (probeTLI
= startTLI
+ 1;; probeTLI
++)
277 if (existsTimeLineHistory(probeTLI
))
279 newestTLI
= probeTLI
; /* probeTLI exists */
283 /* doesn't exist, assume we're done */
292 * Create a new timeline history file.
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
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.
304 writeTimeLineHistory(TimeLineID newTLI
, TimeLineID parentTLI
,
305 XLogRecPtr switchpoint
, char *reason
)
307 char path
[MAXPGPATH
];
308 char tmppath
[MAXPGPATH
];
309 char histfname
[MAXFNAMELEN
];
315 Assert(newTLI
> parentTLI
); /* else bad selection of newTLI */
318 * Write into a temp file name.
320 snprintf(tmppath
, MAXPGPATH
, XLOGDIR
"/xlogtemp.%d", (int) getpid());
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
);
328 (errcode_for_file_access(),
329 errmsg("could not create file \"%s\": %m", tmppath
)));
332 * If a history file exists for the parent, copy it verbatim
334 if (ArchiveRecoveryRequested
)
336 TLHistoryFileName(histfname
, parentTLI
);
337 RestoreArchivedFile(path
, histfname
, "RECOVERYHISTORY", 0, false);
340 TLHistoryFilePath(path
, parentTLI
);
342 srcfd
= OpenTransientFile(path
, O_RDONLY
);
347 (errcode_for_file_access(),
348 errmsg("could not open file \"%s\": %m", path
)));
349 /* Not there, so assume parent has no parents */
356 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ
);
357 nbytes
= (int) read(srcfd
, buffer
, sizeof(buffer
));
358 pgstat_report_wait_end();
359 if (nbytes
< 0 || errno
!= 0)
361 (errcode_for_file_access(),
362 errmsg("could not read file \"%s\": %m", path
)));
366 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE
);
367 if ((int) write(fd
, buffer
, nbytes
) != nbytes
)
369 int save_errno
= errno
;
372 * If we fail to make the file, delete it to release disk
378 * if write didn't set errno, assume problem is no disk space
380 errno
= save_errno
? save_errno
: ENOSPC
;
383 (errcode_for_file_access(),
384 errmsg("could not write to file \"%s\": %m", tmppath
)));
386 pgstat_report_wait_end();
389 if (CloseTransientFile(srcfd
) != 0)
391 (errcode_for_file_access(),
392 errmsg("could not close file \"%s\": %m", path
)));
396 * Append one line with the details of this timeline split.
398 * If we did have a parent file, insert an extra newline just in case the
399 * parent file failed to end with one.
401 snprintf(buffer
, sizeof(buffer
),
403 (srcfd
< 0) ? "" : "\n",
405 LSN_FORMAT_ARGS(switchpoint
),
408 nbytes
= strlen(buffer
);
410 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE
);
411 if ((int) write(fd
, buffer
, nbytes
) != nbytes
)
413 int save_errno
= errno
;
416 * If we fail to make the file, delete it to release disk space
419 /* if write didn't set errno, assume problem is no disk space */
420 errno
= save_errno
? save_errno
: ENOSPC
;
423 (errcode_for_file_access(),
424 errmsg("could not write to file \"%s\": %m", tmppath
)));
426 pgstat_report_wait_end();
428 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC
);
429 if (pg_fsync(fd
) != 0)
430 ereport(data_sync_elevel(ERROR
),
431 (errcode_for_file_access(),
432 errmsg("could not fsync file \"%s\": %m", tmppath
)));
433 pgstat_report_wait_end();
435 if (CloseTransientFile(fd
) != 0)
437 (errcode_for_file_access(),
438 errmsg("could not close file \"%s\": %m", tmppath
)));
441 * Now move the completed history file into place with its final name.
443 TLHistoryFilePath(path
, newTLI
);
444 Assert(access(path
, F_OK
) != 0 && errno
== ENOENT
);
445 durable_rename(tmppath
, path
, ERROR
);
447 /* The history file can be archived immediately. */
448 if (XLogArchivingActive())
450 TLHistoryFileName(histfname
, newTLI
);
451 XLogArchiveNotify(histfname
);
456 * Writes a history file for given timeline and contents.
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.
463 writeTimeLineHistoryFile(TimeLineID tli
, char *content
, int size
)
465 char path
[MAXPGPATH
];
466 char tmppath
[MAXPGPATH
];
470 * Write into a temp file name.
472 snprintf(tmppath
, MAXPGPATH
, XLOGDIR
"/xlogtemp.%d", (int) getpid());
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
);
480 (errcode_for_file_access(),
481 errmsg("could not create file \"%s\": %m", tmppath
)));
484 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE
);
485 if ((int) write(fd
, content
, size
) != size
)
487 int save_errno
= errno
;
490 * If we fail to make the file, delete it to release disk space
493 /* if write didn't set errno, assume problem is no disk space */
494 errno
= save_errno
? save_errno
: ENOSPC
;
497 (errcode_for_file_access(),
498 errmsg("could not write to file \"%s\": %m", tmppath
)));
500 pgstat_report_wait_end();
502 pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC
);
503 if (pg_fsync(fd
) != 0)
504 ereport(data_sync_elevel(ERROR
),
505 (errcode_for_file_access(),
506 errmsg("could not fsync file \"%s\": %m", tmppath
)));
507 pgstat_report_wait_end();
509 if (CloseTransientFile(fd
) != 0)
511 (errcode_for_file_access(),
512 errmsg("could not close file \"%s\": %m", tmppath
)));
515 * Now move the completed history file into place with its final name,
516 * replacing any existing file with the same name.
518 TLHistoryFilePath(path
, tli
);
519 durable_rename(tmppath
, path
, ERROR
);
523 * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
526 tliInHistory(TimeLineID tli
, List
*expectedTLEs
)
530 foreach(cell
, expectedTLEs
)
532 if (((TimeLineHistoryEntry
*) lfirst(cell
))->tli
== tli
)
540 * Returns the ID of the timeline in use at a particular point in time, in
541 * the given timeline history.
544 tliOfPointInHistory(XLogRecPtr ptr
, List
*history
)
548 foreach(cell
, history
)
550 TimeLineHistoryEntry
*tle
= (TimeLineHistoryEntry
*) lfirst(cell
);
552 if ((XLogRecPtrIsInvalid(tle
->begin
) || tle
->begin
<= ptr
) &&
553 (XLogRecPtrIsInvalid(tle
->end
) || ptr
< tle
->end
))
560 /* shouldn't happen. */
561 elog(ERROR
, "timeline history was not contiguous");
562 return 0; /* keep compiler quiet */
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.
572 tliSwitchPoint(TimeLineID tli
, List
*history
, TimeLineID
*nextTLI
)
578 foreach(cell
, history
)
580 TimeLineHistoryEntry
*tle
= (TimeLineHistoryEntry
*) lfirst(cell
);
589 (errmsg("requested timeline %u is not in this server's history",
591 return InvalidXLogRecPtr
; /* keep compiler quiet */