Line data Source code
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-2017, 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/xlogdefs.h"
41 : #include "pgstat.h"
42 : #include "storage/fd.h"
43 :
44 : /*
45 : * Copies all timeline history files with id's between 'begin' and 'end'
46 : * from archive to pg_wal.
47 : */
48 : void
49 3 : restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
50 : {
51 : char path[MAXPGPATH];
52 : char histfname[MAXFNAMELEN];
53 : TimeLineID tli;
54 :
55 3 : for (tli = begin; tli < end; tli++)
56 : {
57 0 : if (tli == 1)
58 0 : continue;
59 :
60 0 : TLHistoryFileName(histfname, tli);
61 0 : if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
62 0 : KeepFileRestoredFromArchive(path, histfname);
63 : }
64 3 : }
65 :
66 : /*
67 : * Try to read a timeline's history file.
68 : *
69 : * If successful, return the list of component TLIs (the given TLI followed by
70 : * its ancestor TLIs). If we can't find the history file, assume that the
71 : * timeline has no parents, and return a list of just the specified timeline
72 : * ID.
73 : */
74 : List *
75 9 : readTimeLineHistory(TimeLineID targetTLI)
76 : {
77 : List *result;
78 : char path[MAXPGPATH];
79 : char histfname[MAXFNAMELEN];
80 : char fline[MAXPGPATH];
81 : FILE *fd;
82 : TimeLineHistoryEntry *entry;
83 9 : TimeLineID lasttli = 0;
84 : XLogRecPtr prevend;
85 9 : bool fromArchive = false;
86 :
87 : /* Timeline 1 does not have a history file, so no need to check */
88 9 : if (targetTLI == 1)
89 : {
90 9 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
91 9 : entry->tli = targetTLI;
92 9 : entry->begin = entry->end = InvalidXLogRecPtr;
93 9 : return list_make1(entry);
94 : }
95 :
96 0 : if (ArchiveRecoveryRequested)
97 : {
98 0 : TLHistoryFileName(histfname, targetTLI);
99 0 : fromArchive =
100 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
101 : }
102 : else
103 0 : TLHistoryFilePath(path, targetTLI);
104 :
105 0 : fd = AllocateFile(path, "r");
106 0 : if (fd == NULL)
107 : {
108 0 : if (errno != ENOENT)
109 0 : ereport(FATAL,
110 : (errcode_for_file_access(),
111 : errmsg("could not open file \"%s\": %m", path)));
112 : /* Not there, so assume no parents */
113 0 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
114 0 : entry->tli = targetTLI;
115 0 : entry->begin = entry->end = InvalidXLogRecPtr;
116 0 : return list_make1(entry);
117 : }
118 :
119 0 : result = NIL;
120 :
121 : /*
122 : * Parse the file...
123 : */
124 0 : prevend = InvalidXLogRecPtr;
125 0 : while (fgets(fline, sizeof(fline), fd) != NULL)
126 : {
127 : /* skip leading whitespace and check for # comment */
128 : char *ptr;
129 : TimeLineID tli;
130 : uint32 switchpoint_hi;
131 : uint32 switchpoint_lo;
132 : int nfields;
133 :
134 0 : for (ptr = fline; *ptr; ptr++)
135 : {
136 0 : if (!isspace((unsigned char) *ptr))
137 0 : break;
138 : }
139 0 : if (*ptr == '\0' || *ptr == '#')
140 0 : continue;
141 :
142 0 : nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);
143 :
144 0 : if (nfields < 1)
145 : {
146 : /* expect a numeric timeline ID as first field of line */
147 0 : ereport(FATAL,
148 : (errmsg("syntax error in history file: %s", fline),
149 : errhint("Expected a numeric timeline ID.")));
150 : }
151 0 : if (nfields != 3)
152 0 : ereport(FATAL,
153 : (errmsg("syntax error in history file: %s", fline),
154 : errhint("Expected a write-ahead log switchpoint location.")));
155 :
156 0 : if (result && tli <= lasttli)
157 0 : ereport(FATAL,
158 : (errmsg("invalid data in history file: %s", fline),
159 : errhint("Timeline IDs must be in increasing sequence.")));
160 :
161 0 : lasttli = tli;
162 :
163 0 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
164 0 : entry->tli = tli;
165 0 : entry->begin = prevend;
166 0 : entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
167 0 : prevend = entry->end;
168 :
169 : /* Build list with newest item first */
170 0 : result = lcons(entry, result);
171 :
172 : /* we ignore the remainder of each line */
173 : }
174 :
175 0 : FreeFile(fd);
176 :
177 0 : if (result && targetTLI <= lasttli)
178 0 : ereport(FATAL,
179 : (errmsg("invalid data in history file \"%s\"", path),
180 : errhint("Timeline IDs must be less than child timeline's ID.")));
181 :
182 : /*
183 : * Create one more entry for the "tip" of the timeline, which has no entry
184 : * in the history file.
185 : */
186 0 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
187 0 : entry->tli = targetTLI;
188 0 : entry->begin = prevend;
189 0 : entry->end = InvalidXLogRecPtr;
190 :
191 0 : result = lcons(entry, result);
192 :
193 : /*
194 : * If the history file was fetched from archive, save it in pg_wal for
195 : * future reference.
196 : */
197 0 : if (fromArchive)
198 0 : KeepFileRestoredFromArchive(path, histfname);
199 :
200 0 : return result;
201 : }
202 :
203 : /*
204 : * Probe whether a timeline history file exists for the given timeline ID
205 : */
206 : bool
207 0 : existsTimeLineHistory(TimeLineID probeTLI)
208 : {
209 : char path[MAXPGPATH];
210 : char histfname[MAXFNAMELEN];
211 : FILE *fd;
212 :
213 : /* Timeline 1 does not have a history file, so no need to check */
214 0 : if (probeTLI == 1)
215 0 : return false;
216 :
217 0 : if (ArchiveRecoveryRequested)
218 : {
219 0 : TLHistoryFileName(histfname, probeTLI);
220 0 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
221 : }
222 : else
223 0 : TLHistoryFilePath(path, probeTLI);
224 :
225 0 : fd = AllocateFile(path, "r");
226 0 : if (fd != NULL)
227 : {
228 0 : FreeFile(fd);
229 0 : return true;
230 : }
231 : else
232 : {
233 0 : if (errno != ENOENT)
234 0 : ereport(FATAL,
235 : (errcode_for_file_access(),
236 : errmsg("could not open file \"%s\": %m", path)));
237 0 : return false;
238 : }
239 : }
240 :
241 : /*
242 : * Find the newest existing timeline, assuming that startTLI exists.
243 : *
244 : * Note: while this is somewhat heuristic, it does positively guarantee
245 : * that (result + 1) is not a known timeline, and therefore it should
246 : * be safe to assign that ID to a new timeline.
247 : */
248 : TimeLineID
249 0 : findNewestTimeLine(TimeLineID startTLI)
250 : {
251 : TimeLineID newestTLI;
252 : TimeLineID probeTLI;
253 :
254 : /*
255 : * The algorithm is just to probe for the existence of timeline history
256 : * files. XXX is it useful to allow gaps in the sequence?
257 : */
258 0 : newestTLI = startTLI;
259 :
260 0 : for (probeTLI = startTLI + 1;; probeTLI++)
261 : {
262 0 : if (existsTimeLineHistory(probeTLI))
263 : {
264 0 : newestTLI = probeTLI; /* probeTLI exists */
265 : }
266 : else
267 : {
268 : /* doesn't exist, assume we're done */
269 0 : break;
270 : }
271 0 : }
272 :
273 0 : return newestTLI;
274 : }
275 :
276 : /*
277 : * Create a new timeline history file.
278 : *
279 : * newTLI: ID of the new timeline
280 : * parentTLI: ID of its immediate parent
281 : * switchpoint: WAL location where the system switched to the new timeline
282 : * reason: human-readable explanation of why the timeline was switched
283 : *
284 : * Currently this is only used at the end recovery, and so there are no locking
285 : * considerations. But we should be just as tense as XLogFileInit to avoid
286 : * emplacing a bogus file.
287 : */
288 : void
289 0 : writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
290 : XLogRecPtr switchpoint, char *reason)
291 : {
292 : char path[MAXPGPATH];
293 : char tmppath[MAXPGPATH];
294 : char histfname[MAXFNAMELEN];
295 : char buffer[BLCKSZ];
296 : int srcfd;
297 : int fd;
298 : int nbytes;
299 :
300 0 : Assert(newTLI > parentTLI); /* else bad selection of newTLI */
301 :
302 : /*
303 : * Write into a temp file name.
304 : */
305 0 : snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
306 :
307 0 : unlink(tmppath);
308 :
309 : /* do not use get_sync_bit() here --- want to fsync only at end of fill */
310 0 : fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
311 : S_IRUSR | S_IWUSR);
312 0 : if (fd < 0)
313 0 : ereport(ERROR,
314 : (errcode_for_file_access(),
315 : errmsg("could not create file \"%s\": %m", tmppath)));
316 :
317 : /*
318 : * If a history file exists for the parent, copy it verbatim
319 : */
320 0 : if (ArchiveRecoveryRequested)
321 : {
322 0 : TLHistoryFileName(histfname, parentTLI);
323 0 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
324 : }
325 : else
326 0 : TLHistoryFilePath(path, parentTLI);
327 :
328 0 : srcfd = OpenTransientFile(path, O_RDONLY, 0);
329 0 : if (srcfd < 0)
330 : {
331 0 : if (errno != ENOENT)
332 0 : ereport(ERROR,
333 : (errcode_for_file_access(),
334 : errmsg("could not open file \"%s\": %m", path)));
335 : /* Not there, so assume parent has no parents */
336 : }
337 : else
338 : {
339 : for (;;)
340 : {
341 0 : errno = 0;
342 0 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
343 0 : nbytes = (int) read(srcfd, buffer, sizeof(buffer));
344 0 : pgstat_report_wait_end();
345 0 : if (nbytes < 0 || errno != 0)
346 0 : ereport(ERROR,
347 : (errcode_for_file_access(),
348 : errmsg("could not read file \"%s\": %m", path)));
349 0 : if (nbytes == 0)
350 0 : break;
351 0 : errno = 0;
352 0 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
353 0 : if ((int) write(fd, buffer, nbytes) != nbytes)
354 : {
355 0 : int save_errno = errno;
356 :
357 : /*
358 : * If we fail to make the file, delete it to release disk
359 : * space
360 : */
361 0 : unlink(tmppath);
362 :
363 : /*
364 : * if write didn't set errno, assume problem is no disk space
365 : */
366 0 : errno = save_errno ? save_errno : ENOSPC;
367 :
368 0 : ereport(ERROR,
369 : (errcode_for_file_access(),
370 : errmsg("could not write to file \"%s\": %m", tmppath)));
371 : }
372 0 : pgstat_report_wait_end();
373 0 : }
374 0 : CloseTransientFile(srcfd);
375 : }
376 :
377 : /*
378 : * Append one line with the details of this timeline split.
379 : *
380 : * If we did have a parent file, insert an extra newline just in case the
381 : * parent file failed to end with one.
382 : */
383 0 : snprintf(buffer, sizeof(buffer),
384 : "%s%u\t%X/%X\t%s\n",
385 : (srcfd < 0) ? "" : "\n",
386 : parentTLI,
387 0 : (uint32) (switchpoint >> 32), (uint32) (switchpoint),
388 : reason);
389 :
390 0 : nbytes = strlen(buffer);
391 0 : errno = 0;
392 0 : if ((int) write(fd, buffer, nbytes) != nbytes)
393 : {
394 0 : int save_errno = errno;
395 :
396 : /*
397 : * If we fail to make the file, delete it to release disk space
398 : */
399 0 : unlink(tmppath);
400 : /* if write didn't set errno, assume problem is no disk space */
401 0 : errno = save_errno ? save_errno : ENOSPC;
402 :
403 0 : ereport(ERROR,
404 : (errcode_for_file_access(),
405 : errmsg("could not write to file \"%s\": %m", tmppath)));
406 : }
407 :
408 0 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC);
409 0 : if (pg_fsync(fd) != 0)
410 0 : ereport(ERROR,
411 : (errcode_for_file_access(),
412 : errmsg("could not fsync file \"%s\": %m", tmppath)));
413 0 : pgstat_report_wait_end();
414 :
415 0 : if (CloseTransientFile(fd))
416 0 : ereport(ERROR,
417 : (errcode_for_file_access(),
418 : errmsg("could not close file \"%s\": %m", tmppath)));
419 :
420 :
421 : /*
422 : * Now move the completed history file into place with its final name.
423 : */
424 0 : TLHistoryFilePath(path, newTLI);
425 :
426 : /*
427 : * Perform the rename using link if available, paranoidly trying to avoid
428 : * overwriting an existing file (there shouldn't be one).
429 : */
430 0 : durable_link_or_rename(tmppath, path, ERROR);
431 :
432 : /* The history file can be archived immediately. */
433 0 : if (XLogArchivingActive())
434 : {
435 0 : TLHistoryFileName(histfname, newTLI);
436 0 : XLogArchiveNotify(histfname);
437 : }
438 0 : }
439 :
440 : /*
441 : * Writes a history file for given timeline and contents.
442 : *
443 : * Currently this is only used in the walreceiver process, and so there are
444 : * no locking considerations. But we should be just as tense as XLogFileInit
445 : * to avoid emplacing a bogus file.
446 : */
447 : void
448 0 : writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
449 : {
450 : char path[MAXPGPATH];
451 : char tmppath[MAXPGPATH];
452 : int fd;
453 :
454 : /*
455 : * Write into a temp file name.
456 : */
457 0 : snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
458 :
459 0 : unlink(tmppath);
460 :
461 : /* do not use get_sync_bit() here --- want to fsync only at end of fill */
462 0 : fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
463 : S_IRUSR | S_IWUSR);
464 0 : if (fd < 0)
465 0 : ereport(ERROR,
466 : (errcode_for_file_access(),
467 : errmsg("could not create file \"%s\": %m", tmppath)));
468 :
469 0 : errno = 0;
470 0 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE);
471 0 : if ((int) write(fd, content, size) != size)
472 : {
473 0 : int save_errno = errno;
474 :
475 : /*
476 : * If we fail to make the file, delete it to release disk space
477 : */
478 0 : unlink(tmppath);
479 : /* if write didn't set errno, assume problem is no disk space */
480 0 : errno = save_errno ? save_errno : ENOSPC;
481 :
482 0 : ereport(ERROR,
483 : (errcode_for_file_access(),
484 : errmsg("could not write to file \"%s\": %m", tmppath)));
485 : }
486 0 : pgstat_report_wait_end();
487 :
488 0 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC);
489 0 : if (pg_fsync(fd) != 0)
490 0 : ereport(ERROR,
491 : (errcode_for_file_access(),
492 : errmsg("could not fsync file \"%s\": %m", tmppath)));
493 0 : pgstat_report_wait_end();
494 :
495 0 : if (CloseTransientFile(fd))
496 0 : ereport(ERROR,
497 : (errcode_for_file_access(),
498 : errmsg("could not close file \"%s\": %m", tmppath)));
499 :
500 :
501 : /*
502 : * Now move the completed history file into place with its final name.
503 : */
504 0 : TLHistoryFilePath(path, tli);
505 :
506 : /*
507 : * Perform the rename using link if available, paranoidly trying to avoid
508 : * overwriting an existing file (there shouldn't be one).
509 : */
510 0 : durable_link_or_rename(tmppath, path, ERROR);
511 0 : }
512 :
513 : /*
514 : * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
515 : */
516 : bool
517 6 : tliInHistory(TimeLineID tli, List *expectedTLEs)
518 : {
519 : ListCell *cell;
520 :
521 6 : foreach(cell, expectedTLEs)
522 : {
523 6 : if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
524 6 : return true;
525 : }
526 :
527 0 : return false;
528 : }
529 :
530 : /*
531 : * Returns the ID of the timeline in use at a particular point in time, in
532 : * the given timeline history.
533 : */
534 : TimeLineID
535 9 : tliOfPointInHistory(XLogRecPtr ptr, List *history)
536 : {
537 : ListCell *cell;
538 :
539 9 : foreach(cell, history)
540 : {
541 9 : TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
542 :
543 18 : if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
544 9 : (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
545 : {
546 : /* found it */
547 9 : return tle->tli;
548 : }
549 : }
550 :
551 : /* shouldn't happen. */
552 0 : elog(ERROR, "timeline history was not contiguous");
553 : return 0; /* keep compiler quiet */
554 : }
555 :
556 : /*
557 : * Returns the point in history where we branched off the given timeline,
558 : * and the timeline we branched to (*nextTLI). Returns InvalidXLogRecPtr if
559 : * the timeline is current, ie. we have not branched off from it, and throws
560 : * an error if the timeline is not part of this server's history.
561 : */
562 : XLogRecPtr
563 6 : tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
564 : {
565 : ListCell *cell;
566 :
567 6 : if (nextTLI)
568 6 : *nextTLI = 0;
569 6 : foreach(cell, history)
570 : {
571 6 : TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
572 :
573 6 : if (tle->tli == tli)
574 6 : return tle->end;
575 0 : if (nextTLI)
576 0 : *nextTLI = tle->tli;
577 : }
578 :
579 0 : ereport(ERROR,
580 : (errmsg("requested timeline %u is not in this server's history",
581 : tli)));
582 : return InvalidXLogRecPtr; /* keep compiler quiet */
583 : }
|