LCOV - code coverage report
Current view: top level - src/backend/access/transam - timeline.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 142 180 78.9 %
Date: 2019-11-13 22:07:24 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          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-2019, 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        1198 : restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
      50             : {
      51             :     char        path[MAXPGPATH];
      52             :     char        histfname[MAXFNAMELEN];
      53             :     TimeLineID  tli;
      54             : 
      55        1204 :     for (tli = begin; tli < end; tli++)
      56             :     {
      57           6 :         if (tli == 1)
      58           4 :             continue;
      59             : 
      60           2 :         TLHistoryFileName(histfname, tli);
      61           2 :         if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
      62           0 :             KeepFileRestoredFromArchive(path, histfname);
      63             :     }
      64        1198 : }
      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        1880 : 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        1880 :     TimeLineID  lasttli = 0;
      84             :     XLogRecPtr  prevend;
      85        1880 :     bool        fromArchive = false;
      86             : 
      87             :     /* Timeline 1 does not have a history file, so no need to check */
      88        1880 :     if (targetTLI == 1)
      89             :     {
      90        1782 :         entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
      91        1782 :         entry->tli = targetTLI;
      92        1782 :         entry->begin = entry->end = InvalidXLogRecPtr;
      93        1782 :         return list_make1(entry);
      94             :     }
      95             : 
      96          98 :     if (ArchiveRecoveryRequested)
      97             :     {
      98          42 :         TLHistoryFileName(histfname, targetTLI);
      99          42 :         fromArchive =
     100             :             RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
     101             :     }
     102             :     else
     103          56 :         TLHistoryFilePath(path, targetTLI);
     104             : 
     105          98 :     fd = AllocateFile(path, "r");
     106          98 :     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          98 :     result = NIL;
     120             : 
     121             :     /*
     122             :      * Parse the file...
     123             :      */
     124          98 :     prevend = InvalidXLogRecPtr;
     125         434 :     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         308 :         for (ptr = fline; *ptr; ptr++)
     135             :         {
     136         238 :             if (!isspace((unsigned char) *ptr))
     137         168 :                 break;
     138             :         }
     139         238 :         if (*ptr == '\0' || *ptr == '#')
     140          70 :             continue;
     141             : 
     142         168 :         nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);
     143             : 
     144         168 :         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         168 :         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         168 :         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         168 :         lasttli = tli;
     162             : 
     163         168 :         entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
     164         168 :         entry->tli = tli;
     165         168 :         entry->begin = prevend;
     166         168 :         entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
     167         168 :         prevend = entry->end;
     168             : 
     169             :         /* Build list with newest item first */
     170         168 :         result = lcons(entry, result);
     171             : 
     172             :         /* we ignore the remainder of each line */
     173             :     }
     174             : 
     175          98 :     FreeFile(fd);
     176             : 
     177          98 :     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          98 :     entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
     187          98 :     entry->tli = targetTLI;
     188          98 :     entry->begin = prevend;
     189          98 :     entry->end = InvalidXLogRecPtr;
     190             : 
     191          98 :     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          98 :     if (fromArchive)
     198           2 :         KeepFileRestoredFromArchive(path, histfname);
     199             : 
     200          98 :     return result;
     201             : }
     202             : 
     203             : /*
     204             :  * Probe whether a timeline history file exists for the given timeline ID
     205             :  */
     206             : bool
     207         310 : 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         310 :     if (probeTLI == 1)
     215           0 :         return false;
     216             : 
     217         310 :     if (ArchiveRecoveryRequested)
     218             :     {
     219         270 :         TLHistoryFileName(histfname, probeTLI);
     220         270 :         RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
     221             :     }
     222             :     else
     223          40 :         TLHistoryFilePath(path, probeTLI);
     224             : 
     225         310 :     fd = AllocateFile(path, "r");
     226         310 :     if (fd != NULL)
     227             :     {
     228          40 :         FreeFile(fd);
     229          40 :         return true;
     230             :     }
     231             :     else
     232             :     {
     233         270 :         if (errno != ENOENT)
     234           0 :             ereport(FATAL,
     235             :                     (errcode_for_file_access(),
     236             :                      errmsg("could not open file \"%s\": %m", path)));
     237         270 :         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         260 : 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         260 :     newestTLI = startTLI;
     259             : 
     260         270 :     for (probeTLI = startTLI + 1;; probeTLI++)
     261             :     {
     262         280 :         if (existsTimeLineHistory(probeTLI))
     263             :         {
     264          10 :             newestTLI = probeTLI;   /* probeTLI exists */
     265             :         }
     266             :         else
     267             :         {
     268             :             /* doesn't exist, assume we're done */
     269         260 :             break;
     270             :         }
     271             :     }
     272             : 
     273         260 :     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          46 : 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             :     Assert(newTLI > parentTLI); /* else bad selection of newTLI */
     301             : 
     302             :     /*
     303             :      * Write into a temp file name.
     304             :      */
     305          46 :     snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
     306             : 
     307          46 :     unlink(tmppath);
     308             : 
     309             :     /* do not use get_sync_bit() here --- want to fsync only at end of fill */
     310          46 :     fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
     311          46 :     if (fd < 0)
     312           0 :         ereport(ERROR,
     313             :                 (errcode_for_file_access(),
     314             :                  errmsg("could not create file \"%s\": %m", tmppath)));
     315             : 
     316             :     /*
     317             :      * If a history file exists for the parent, copy it verbatim
     318             :      */
     319          46 :     if (ArchiveRecoveryRequested)
     320             :     {
     321          46 :         TLHistoryFileName(histfname, parentTLI);
     322          46 :         RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
     323             :     }
     324             :     else
     325           0 :         TLHistoryFilePath(path, parentTLI);
     326             : 
     327          46 :     srcfd = OpenTransientFile(path, O_RDONLY);
     328          46 :     if (srcfd < 0)
     329             :     {
     330          36 :         if (errno != ENOENT)
     331           0 :             ereport(ERROR,
     332             :                     (errcode_for_file_access(),
     333             :                      errmsg("could not open file \"%s\": %m", path)));
     334             :         /* Not there, so assume parent has no parents */
     335             :     }
     336             :     else
     337             :     {
     338             :         for (;;)
     339             :         {
     340          10 :             errno = 0;
     341          20 :             pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
     342          20 :             nbytes = (int) read(srcfd, buffer, sizeof(buffer));
     343          20 :             pgstat_report_wait_end();
     344          20 :             if (nbytes < 0 || errno != 0)
     345           0 :                 ereport(ERROR,
     346             :                         (errcode_for_file_access(),
     347             :                          errmsg("could not read file \"%s\": %m", path)));
     348          20 :             if (nbytes == 0)
     349          10 :                 break;
     350          10 :             errno = 0;
     351          10 :             pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
     352          10 :             if ((int) write(fd, buffer, nbytes) != nbytes)
     353             :             {
     354           0 :                 int         save_errno = errno;
     355             : 
     356             :                 /*
     357             :                  * If we fail to make the file, delete it to release disk
     358             :                  * space
     359             :                  */
     360           0 :                 unlink(tmppath);
     361             : 
     362             :                 /*
     363             :                  * if write didn't set errno, assume problem is no disk space
     364             :                  */
     365           0 :                 errno = save_errno ? save_errno : ENOSPC;
     366             : 
     367           0 :                 ereport(ERROR,
     368             :                         (errcode_for_file_access(),
     369             :                          errmsg("could not write to file \"%s\": %m", tmppath)));
     370             :             }
     371          10 :             pgstat_report_wait_end();
     372             :         }
     373             : 
     374          10 :         if (CloseTransientFile(srcfd) != 0)
     375           0 :             ereport(ERROR,
     376             :                     (errcode_for_file_access(),
     377             :                      errmsg("could not close file \"%s\": %m", path)));
     378             :     }
     379             : 
     380             :     /*
     381             :      * Append one line with the details of this timeline split.
     382             :      *
     383             :      * If we did have a parent file, insert an extra newline just in case the
     384             :      * parent file failed to end with one.
     385             :      */
     386          92 :     snprintf(buffer, sizeof(buffer),
     387             :              "%s%u\t%X/%X\t%s\n",
     388             :              (srcfd < 0) ? "" : "\n",
     389             :              parentTLI,
     390          46 :              (uint32) (switchpoint >> 32), (uint32) (switchpoint),
     391             :              reason);
     392             : 
     393          46 :     nbytes = strlen(buffer);
     394          46 :     errno = 0;
     395          46 :     if ((int) write(fd, buffer, nbytes) != nbytes)
     396             :     {
     397           0 :         int         save_errno = errno;
     398             : 
     399             :         /*
     400             :          * If we fail to make the file, delete it to release disk space
     401             :          */
     402           0 :         unlink(tmppath);
     403             :         /* if write didn't set errno, assume problem is no disk space */
     404           0 :         errno = save_errno ? save_errno : ENOSPC;
     405             : 
     406           0 :         ereport(ERROR,
     407             :                 (errcode_for_file_access(),
     408             :                  errmsg("could not write to file \"%s\": %m", tmppath)));
     409             :     }
     410             : 
     411          46 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC);
     412          46 :     if (pg_fsync(fd) != 0)
     413           0 :         ereport(data_sync_elevel(ERROR),
     414             :                 (errcode_for_file_access(),
     415             :                  errmsg("could not fsync file \"%s\": %m", tmppath)));
     416          46 :     pgstat_report_wait_end();
     417             : 
     418          46 :     if (CloseTransientFile(fd) != 0)
     419           0 :         ereport(ERROR,
     420             :                 (errcode_for_file_access(),
     421             :                  errmsg("could not close file \"%s\": %m", tmppath)));
     422             : 
     423             :     /*
     424             :      * Now move the completed history file into place with its final name.
     425             :      */
     426          46 :     TLHistoryFilePath(path, newTLI);
     427             : 
     428             :     /*
     429             :      * Perform the rename using link if available, paranoidly trying to avoid
     430             :      * overwriting an existing file (there shouldn't be one).
     431             :      */
     432          46 :     durable_link_or_rename(tmppath, path, ERROR);
     433             : 
     434             :     /* The history file can be archived immediately. */
     435          46 :     if (XLogArchivingActive())
     436             :     {
     437           6 :         TLHistoryFileName(histfname, newTLI);
     438           6 :         XLogArchiveNotify(histfname);
     439             :     }
     440          46 : }
     441             : 
     442             : /*
     443             :  * Writes a history file for given timeline and contents.
     444             :  *
     445             :  * Currently this is only used in the walreceiver process, and so there are
     446             :  * no locking considerations.  But we should be just as tense as XLogFileInit
     447             :  * to avoid emplacing a bogus file.
     448             :  */
     449             : void
     450          10 : writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
     451             : {
     452             :     char        path[MAXPGPATH];
     453             :     char        tmppath[MAXPGPATH];
     454             :     int         fd;
     455             : 
     456             :     /*
     457             :      * Write into a temp file name.
     458             :      */
     459          10 :     snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
     460             : 
     461          10 :     unlink(tmppath);
     462             : 
     463             :     /* do not use get_sync_bit() here --- want to fsync only at end of fill */
     464          10 :     fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
     465          10 :     if (fd < 0)
     466           0 :         ereport(ERROR,
     467             :                 (errcode_for_file_access(),
     468             :                  errmsg("could not create file \"%s\": %m", tmppath)));
     469             : 
     470          10 :     errno = 0;
     471          10 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE);
     472          10 :     if ((int) write(fd, content, size) != size)
     473             :     {
     474           0 :         int         save_errno = errno;
     475             : 
     476             :         /*
     477             :          * If we fail to make the file, delete it to release disk space
     478             :          */
     479           0 :         unlink(tmppath);
     480             :         /* if write didn't set errno, assume problem is no disk space */
     481           0 :         errno = save_errno ? save_errno : ENOSPC;
     482             : 
     483           0 :         ereport(ERROR,
     484             :                 (errcode_for_file_access(),
     485             :                  errmsg("could not write to file \"%s\": %m", tmppath)));
     486             :     }
     487          10 :     pgstat_report_wait_end();
     488             : 
     489          10 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC);
     490          10 :     if (pg_fsync(fd) != 0)
     491           0 :         ereport(data_sync_elevel(ERROR),
     492             :                 (errcode_for_file_access(),
     493             :                  errmsg("could not fsync file \"%s\": %m", tmppath)));
     494          10 :     pgstat_report_wait_end();
     495             : 
     496          10 :     if (CloseTransientFile(fd) != 0)
     497           0 :         ereport(ERROR,
     498             :                 (errcode_for_file_access(),
     499             :                  errmsg("could not close file \"%s\": %m", tmppath)));
     500             : 
     501             :     /*
     502             :      * Now move the completed history file into place with its final name.
     503             :      */
     504          10 :     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          10 :     durable_link_or_rename(tmppath, path, ERROR);
     511          10 : }
     512             : 
     513             : /*
     514             :  * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
     515             :  */
     516             : bool
     517      240862 : tliInHistory(TimeLineID tli, List *expectedTLEs)
     518             : {
     519             :     ListCell   *cell;
     520             : 
     521      263532 :     foreach(cell, expectedTLEs)
     522             :     {
     523      263532 :         if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
     524      240862 :             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        2018 : tliOfPointInHistory(XLogRecPtr ptr, List *history)
     536             : {
     537             :     ListCell   *cell;
     538             : 
     539        2030 :     foreach(cell, history)
     540             :     {
     541        2030 :         TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
     542             : 
     543        4048 :         if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
     544        2030 :             (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
     545             :         {
     546             :             /* found it */
     547        2018 :             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         682 : tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
     564             : {
     565             :     ListCell   *cell;
     566             : 
     567         682 :     if (nextTLI)
     568         682 :         *nextTLI = 0;
     569         696 :     foreach(cell, history)
     570             :     {
     571         696 :         TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
     572             : 
     573         696 :         if (tle->tli == tli)
     574         682 :             return tle->end;
     575          14 :         if (nextTLI)
     576          14 :             *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             : }

Generated by: LCOV version 1.13