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-2024, 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"
39 #include "access/xlog_internal.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  */
49 void
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  */
75 List *
77 {
78  List *result;
79  char path[MAXPGPATH];
80  char histfname[MAXFNAMELEN];
81  FILE *fd;
82  TimeLineHistoryEntry *entry;
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)
109  ereport(FATAL,
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);
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))
141  ereport(ERROR,
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 */
162  ereport(FATAL,
163  (errmsg("syntax error in history file: %s", fline),
164  errhint("Expected a numeric timeline ID.")));
165  }
166  if (nfields != 3)
167  ereport(FATAL,
168  (errmsg("syntax error in history file: %s", fline),
169  errhint("Expected a write-ahead log switchpoint location.")));
170 
171  if (result && tli <= lasttli)
172  ereport(FATAL,
173  (errmsg("invalid data in history file: %s", fline),
174  errhint("Timeline IDs must be in increasing sequence.")));
175 
176  lasttli = tli;
177 
178  entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
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)
193  ereport(FATAL,
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  */
201  entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
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  */
221 bool
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)
249  ereport(FATAL,
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  */
303 void
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)
327  ereport(ERROR,
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)
346  ereport(ERROR,
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)
360  ereport(ERROR,
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 
382  ereport(ERROR,
384  errmsg("could not write to file \"%s\": %m", tmppath)));
385  }
387  }
388 
389  if (CloseTransientFile(srcfd) != 0)
390  ereport(ERROR,
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 
422  ereport(ERROR,
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)
436  ereport(ERROR,
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. */
448  if (XLogArchivingActive())
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  */
462 void
463 writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
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)
479  ereport(ERROR,
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 
496  ereport(ERROR,
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)
510  ereport(ERROR,
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  */
525 bool
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  */
572 tliSwitchPoint(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 
588  ereport(ERROR,
589  (errmsg("requested timeline %u is not in this server's history",
590  tli)));
591  return InvalidXLogRecPtr; /* keep compiler quiet */
592 }
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
List * readTimeLineHistory(TimeLineID targetTLI)
Definition: timeline.c:76
bool tliInHistory(TimeLineID tli, List *expectedTLEs)
Definition: timeline.c:526
unsigned int uint32
Definition: c.h:506
#define Assert(condition)
Definition: c.h:858
int errcode_for_file_access(void)
Definition: elog.c:882
int errhint(const char *fmt,...)
Definition: elog.c:1319
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define FATAL
Definition: elog.h:41
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2583
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:782
int CloseTransientFile(int fd)
Definition: fd.c:2809
int FreeFile(FILE *file)
Definition: fd.c:2781
int data_sync_elevel(int elevel)
Definition: fd.c:3936
int pg_fsync(int fd)
Definition: fd.c:386
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2633
#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:1316
#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:88
static void pgstat_report_wait_end(void)
Definition: wait_event.h:104
#define XLogArchivingActive()
Definition: xlog.h:97
#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