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 *-------------------------------------------------------------------------
46 * Copies all timeline history files with id's between 'begin' and 'end'
47 * from archive to pg_wal.
56 for (tli = begin; tli < end; tli++)
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
85 bool fromArchive =
false;
87 /* Timeline 1 does not have a history file, so no need to check */
91 entry->
tli = targetTLI;
111 errmsg(
"could not open file \"%s\": %m", path)));
112 /* Not there, so assume no parents */
114 entry->
tli = targetTLI;
136 res = fgets(fline,
sizeof(fline),
fd);
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/%08X", &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.")));
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.
202 entry->
tli = targetTLI;
203 entry->
begin = prevend;
206 result =
lcons(entry, result);
209 * If the history file was fetched from archive, save it in pg_wal for
219 * Probe whether a timeline history file exists for the given timeline ID
228 /* Timeline 1 does not have a history file, so no need to check */
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.
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++)
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.
315 Assert(newTLI > parentTLI);
/* else bad selection of newTLI */
318 * Write into a temp file name.
324 /* do not use get_sync_bit() here --- want to fsync only at end of fill */
329 errmsg(
"could not create file \"%s\": %m", tmppath)));
332 * If a history file exists for the parent, copy it verbatim
348 errmsg(
"could not open file \"%s\": %m", path)));
349 /* Not there, so assume parent has no parents */
357 nbytes = (int)
read(srcfd, buffer,
sizeof(buffer));
359 if (nbytes < 0 || errno != 0)
362 errmsg(
"could not read file \"%s\": %m", path)));
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;
384 errmsg(
"could not write to file \"%s\": %m", tmppath)));
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.
402 "%s%u\t%X/%08X\t%s\n",
403 (srcfd < 0) ?
"" :
"\n",
408 nbytes = strlen(buffer);
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;
424 errmsg(
"could not write to file \"%s\": %m", tmppath)));
432 errmsg(
"could not fsync file \"%s\": %m", tmppath)));
438 errmsg(
"could not close file \"%s\": %m", tmppath)));
441 * Now move the completed history file into place with its final name.
447 /* The history file can be archived immediately. */
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.
470 * Write into a temp file name.
476 /* do not use get_sync_bit() here --- want to fsync only at end of fill */
481 errmsg(
"could not create file \"%s\": %m", tmppath)));
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;
498 errmsg(
"could not write to file \"%s\": %m", tmppath)));
506 errmsg(
"could not fsync file \"%s\": %m", tmppath)));
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.
523 * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
540 * Returns the ID of the timeline in use at a particular point in time, in
541 * the given timeline history.
548 foreach(cell, history)
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.
578 foreach(cell, history)
589 (
errmsg(
"requested timeline %u is not in this server's history",
List * readTimeLineHistory(TimeLineID targetTLI)
TimeLineID findNewestTimeLine(TimeLineID startTLI)
void writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
TimeLineID tliOfPointInHistory(XLogRecPtr ptr, List *history)
XLogRecPtr tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
bool existsTimeLineHistory(TimeLineID probeTLI)
void restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
void writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, XLogRecPtr switchpoint, char *reason)
bool tliInHistory(TimeLineID tli, List *expectedTLEs)
int errcode_for_file_access(void)
int errhint(const char *fmt,...)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
int durable_rename(const char *oldfile, const char *newfile, int elevel)
int CloseTransientFile(int fd)
int data_sync_elevel(int elevel)
FILE * AllocateFile(const char *name, const char *mode)
int OpenTransientFile(const char *fileName, int fileFlags)
Assert(PointerIsAligned(start, uint64))
List * lcons(void *datum, List *list)
static int fd(const char *x, int i)
static void pgstat_report_wait_start(uint32 wait_event_info)
static void pgstat_report_wait_end(void)
#define XLogArchivingActive()
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)
void XLogArchiveNotify(const char *xlog)
void KeepFileRestoredFromArchive(const char *path, const char *xlogfname)
#define LSN_FORMAT_ARGS(lsn)
#define XLogRecPtrIsInvalid(r)
#define InvalidXLogRecPtr
bool ArchiveRecoveryRequested
static List * expectedTLEs