LCOV - code coverage report
Current view: top level - src/backend/access/transam - timeline.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 28 182 15.4 %
Date: 2017-09-29 15:12:54 Functions: 5 9 55.6 %
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-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             : }

Generated by: LCOV version 1.11