Header And Logo

PostgreSQL
| The world's most advanced open source database.

timeline.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * timeline.c
00004  *      Functions for reading and writing timeline history files.
00005  *
00006  * A timeline history file lists the timeline changes of the timeline, in
00007  * a simple text format. They are archived along with the WAL segments.
00008  *
00009  * The files are named like "<tli>.history". For example, if the database
00010  * starts up and switches to timeline 5, the timeline history file would be
00011  * called "00000005.history".
00012  *
00013  * Each line in the file represents a timeline switch:
00014  *
00015  * <parentTLI> <switchpoint> <reason>
00016  *
00017  *  parentTLI   ID of the parent timeline
00018  *  switchpoint XLogRecPtr of the WAL position where the switch happened
00019  *  reason      human-readable explanation of why the timeline was changed
00020  *
00021  * The fields are separated by tabs. Lines beginning with # are comments, and
00022  * are ignored. Empty lines are also ignored.
00023  *
00024  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00025  * Portions Copyright (c) 1994, Regents of the University of California
00026  *
00027  * src/backend/access/transam/timeline.c
00028  *
00029  *-------------------------------------------------------------------------
00030  */
00031 
00032 #include "postgres.h"
00033 
00034 #include <sys/stat.h>
00035 #include <stdio.h>
00036 #include <unistd.h>
00037 
00038 #include "access/timeline.h"
00039 #include "access/xlog_internal.h"
00040 #include "access/xlogdefs.h"
00041 #include "storage/fd.h"
00042 
00043 /*
00044  * Copies all timeline history files with id's between 'begin' and 'end'
00045  * from archive to pg_xlog.
00046  */
00047 void
00048 restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
00049 {
00050     char        path[MAXPGPATH];
00051     char        histfname[MAXFNAMELEN];
00052     TimeLineID tli;
00053 
00054     for (tli = begin; tli < end; tli++)
00055     {
00056         if (tli == 1)
00057             continue;
00058 
00059         TLHistoryFileName(histfname, tli);
00060         if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
00061             KeepFileRestoredFromArchive(path, histfname);
00062     }
00063 }
00064 
00065 /*
00066  * Try to read a timeline's history file.
00067  *
00068  * If successful, return the list of component TLIs (the given TLI followed by
00069  * its ancestor TLIs).  If we can't find the history file, assume that the
00070  * timeline has no parents, and return a list of just the specified timeline
00071  * ID.
00072  */
00073 List *
00074 readTimeLineHistory(TimeLineID targetTLI)
00075 {
00076     List       *result;
00077     char        path[MAXPGPATH];
00078     char        histfname[MAXFNAMELEN];
00079     char        fline[MAXPGPATH];
00080     FILE       *fd;
00081     TimeLineHistoryEntry *entry;
00082     TimeLineID  lasttli = 0;
00083     XLogRecPtr  prevend;
00084     bool        fromArchive = false;
00085 
00086     /* Timeline 1 does not have a history file, so no need to check */
00087     if (targetTLI == 1)
00088     {
00089         entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
00090         entry->tli = targetTLI;
00091         entry->begin = entry->end = InvalidXLogRecPtr;
00092         return list_make1(entry);
00093     }
00094 
00095     if (ArchiveRecoveryRequested)
00096     {
00097         TLHistoryFileName(histfname, targetTLI);
00098         fromArchive =
00099             RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
00100     }
00101     else
00102         TLHistoryFilePath(path, targetTLI);
00103 
00104     fd = AllocateFile(path, "r");
00105     if (fd == NULL)
00106     {
00107         if (errno != ENOENT)
00108             ereport(FATAL,
00109                     (errcode_for_file_access(),
00110                      errmsg("could not open file \"%s\": %m", path)));
00111         /* Not there, so assume no parents */
00112         entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
00113         entry->tli = targetTLI;
00114         entry->begin = entry->end = InvalidXLogRecPtr;
00115         return list_make1(entry);
00116     }
00117 
00118     result = NIL;
00119 
00120     /*
00121      * Parse the file...
00122      */
00123     prevend = InvalidXLogRecPtr;
00124     while (fgets(fline, sizeof(fline), fd) != NULL)
00125     {
00126         /* skip leading whitespace and check for # comment */
00127         char       *ptr;
00128         TimeLineID  tli;
00129         uint32      switchpoint_hi;
00130         uint32      switchpoint_lo;
00131         int         nfields;
00132 
00133         for (ptr = fline; *ptr; ptr++)
00134         {
00135             if (!isspace((unsigned char) *ptr))
00136                 break;
00137         }
00138         if (*ptr == '\0' || *ptr == '#')
00139             continue;
00140 
00141         nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);
00142 
00143         if (nfields < 1)
00144         {
00145             /* expect a numeric timeline ID as first field of line */
00146             ereport(FATAL,
00147                     (errmsg("syntax error in history file: %s", fline),
00148                      errhint("Expected a numeric timeline ID.")));
00149         }
00150         if (nfields != 3)
00151             ereport(FATAL,
00152                     (errmsg("syntax error in history file: %s", fline),
00153                      errhint("Expected an XLOG switchpoint location.")));
00154 
00155         if (result && tli <= lasttli)
00156             ereport(FATAL,
00157                     (errmsg("invalid data in history file: %s", fline),
00158                    errhint("Timeline IDs must be in increasing sequence.")));
00159 
00160         lasttli = tli;
00161 
00162         entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
00163         entry->tli = tli;
00164         entry->begin = prevend;
00165         entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
00166         prevend = entry->end;
00167 
00168         /* Build list with newest item first */
00169         result = lcons(entry, result);
00170 
00171         /* we ignore the remainder of each line */
00172     }
00173 
00174     FreeFile(fd);
00175 
00176     if (result && targetTLI <= lasttli)
00177         ereport(FATAL,
00178                 (errmsg("invalid data in history file \"%s\"", path),
00179             errhint("Timeline IDs must be less than child timeline's ID.")));
00180 
00181     /*
00182      * Create one more entry for the "tip" of the timeline, which has no
00183      * entry in the history file.
00184      */
00185     entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
00186     entry->tli = targetTLI;
00187     entry->begin = prevend;
00188     entry->end = InvalidXLogRecPtr;
00189 
00190     result = lcons(entry, result);
00191 
00192     /*
00193      * If the history file was fetched from archive, save it in pg_xlog for
00194      * future reference.
00195      */
00196     if (fromArchive)
00197         KeepFileRestoredFromArchive(path, histfname);
00198 
00199     return result;
00200 }
00201 
00202 /*
00203  * Probe whether a timeline history file exists for the given timeline ID
00204  */
00205 bool
00206 existsTimeLineHistory(TimeLineID probeTLI)
00207 {
00208     char        path[MAXPGPATH];
00209     char        histfname[MAXFNAMELEN];
00210     FILE       *fd;
00211 
00212     /* Timeline 1 does not have a history file, so no need to check */
00213     if (probeTLI == 1)
00214         return false;
00215 
00216     if (ArchiveRecoveryRequested)
00217     {
00218         TLHistoryFileName(histfname, probeTLI);
00219         RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
00220     }
00221     else
00222         TLHistoryFilePath(path, probeTLI);
00223 
00224     fd = AllocateFile(path, "r");
00225     if (fd != NULL)
00226     {
00227         FreeFile(fd);
00228         return true;
00229     }
00230     else
00231     {
00232         if (errno != ENOENT)
00233             ereport(FATAL,
00234                     (errcode_for_file_access(),
00235                      errmsg("could not open file \"%s\": %m", path)));
00236         return false;
00237     }
00238 }
00239 
00240 /*
00241  * Find the newest existing timeline, assuming that startTLI exists.
00242  *
00243  * Note: while this is somewhat heuristic, it does positively guarantee
00244  * that (result + 1) is not a known timeline, and therefore it should
00245  * be safe to assign that ID to a new timeline.
00246  */
00247 TimeLineID
00248 findNewestTimeLine(TimeLineID startTLI)
00249 {
00250     TimeLineID  newestTLI;
00251     TimeLineID  probeTLI;
00252 
00253     /*
00254      * The algorithm is just to probe for the existence of timeline history
00255      * files.  XXX is it useful to allow gaps in the sequence?
00256      */
00257     newestTLI = startTLI;
00258 
00259     for (probeTLI = startTLI + 1;; probeTLI++)
00260     {
00261         if (existsTimeLineHistory(probeTLI))
00262         {
00263             newestTLI = probeTLI;       /* probeTLI exists */
00264         }
00265         else
00266         {
00267             /* doesn't exist, assume we're done */
00268             break;
00269         }
00270     }
00271 
00272     return newestTLI;
00273 }
00274 
00275 /*
00276  * Create a new timeline history file.
00277  *
00278  *  newTLI: ID of the new timeline
00279  *  parentTLI: ID of its immediate parent
00280  *  switchpoint: XLOG position where the system switched to the new timeline
00281  *  reason: human-readable explanation of why the timeline was switched
00282  *
00283  * Currently this is only used at the end recovery, and so there are no locking
00284  * considerations.  But we should be just as tense as XLogFileInit to avoid
00285  * emplacing a bogus file.
00286  */
00287 void
00288 writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
00289                      XLogRecPtr switchpoint, char *reason)
00290 {
00291     char        path[MAXPGPATH];
00292     char        tmppath[MAXPGPATH];
00293     char        histfname[MAXFNAMELEN];
00294     char        buffer[BLCKSZ];
00295     int         srcfd;
00296     int         fd;
00297     int         nbytes;
00298 
00299     Assert(newTLI > parentTLI); /* else bad selection of newTLI */
00300 
00301     /*
00302      * Write into a temp file name.
00303      */
00304     snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
00305 
00306     unlink(tmppath);
00307 
00308     /* do not use get_sync_bit() here --- want to fsync only at end of fill */
00309     fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
00310                            S_IRUSR | S_IWUSR);
00311     if (fd < 0)
00312         ereport(ERROR,
00313                 (errcode_for_file_access(),
00314                  errmsg("could not create file \"%s\": %m", tmppath)));
00315 
00316     /*
00317      * If a history file exists for the parent, copy it verbatim
00318      */
00319     if (ArchiveRecoveryRequested)
00320     {
00321         TLHistoryFileName(histfname, parentTLI);
00322         RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
00323     }
00324     else
00325         TLHistoryFilePath(path, parentTLI);
00326 
00327     srcfd = OpenTransientFile(path, O_RDONLY, 0);
00328     if (srcfd < 0)
00329     {
00330         if (errno != ENOENT)
00331             ereport(ERROR,
00332                     (errcode_for_file_access(),
00333                      errmsg("could not open file \"%s\": %m", path)));
00334         /* Not there, so assume parent has no parents */
00335     }
00336     else
00337     {
00338         for (;;)
00339         {
00340             errno = 0;
00341             nbytes = (int) read(srcfd, buffer, sizeof(buffer));
00342             if (nbytes < 0 || errno != 0)
00343                 ereport(ERROR,
00344                         (errcode_for_file_access(),
00345                          errmsg("could not read file \"%s\": %m", path)));
00346             if (nbytes == 0)
00347                 break;
00348             errno = 0;
00349             if ((int) write(fd, buffer, nbytes) != nbytes)
00350             {
00351                 int         save_errno = errno;
00352 
00353                 /*
00354                  * If we fail to make the file, delete it to release disk
00355                  * space
00356                  */
00357                 unlink(tmppath);
00358 
00359                 /*
00360                  * if write didn't set errno, assume problem is no disk space
00361                  */
00362                 errno = save_errno ? save_errno : ENOSPC;
00363 
00364                 ereport(ERROR,
00365                         (errcode_for_file_access(),
00366                      errmsg("could not write to file \"%s\": %m", tmppath)));
00367             }
00368         }
00369         CloseTransientFile(srcfd);
00370     }
00371 
00372     /*
00373      * Append one line with the details of this timeline split.
00374      *
00375      * If we did have a parent file, insert an extra newline just in case the
00376      * parent file failed to end with one.
00377      */
00378     snprintf(buffer, sizeof(buffer),
00379              "%s%u\t%X/%X\t%s\n",
00380              (srcfd < 0) ? "" : "\n",
00381              parentTLI,
00382              (uint32) (switchpoint >> 32), (uint32) (switchpoint),
00383              reason);
00384 
00385     nbytes = strlen(buffer);
00386     errno = 0;
00387     if ((int) write(fd, buffer, nbytes) != nbytes)
00388     {
00389         int         save_errno = errno;
00390 
00391         /*
00392          * If we fail to make the file, delete it to release disk space
00393          */
00394         unlink(tmppath);
00395         /* if write didn't set errno, assume problem is no disk space */
00396         errno = save_errno ? save_errno : ENOSPC;
00397 
00398         ereport(ERROR,
00399                 (errcode_for_file_access(),
00400                  errmsg("could not write to file \"%s\": %m", tmppath)));
00401     }
00402 
00403     if (pg_fsync(fd) != 0)
00404         ereport(ERROR,
00405                 (errcode_for_file_access(),
00406                  errmsg("could not fsync file \"%s\": %m", tmppath)));
00407 
00408     if (CloseTransientFile(fd))
00409         ereport(ERROR,
00410                 (errcode_for_file_access(),
00411                  errmsg("could not close file \"%s\": %m", tmppath)));
00412 
00413 
00414     /*
00415      * Now move the completed history file into place with its final name.
00416      */
00417     TLHistoryFilePath(path, newTLI);
00418 
00419     /*
00420      * Prefer link() to rename() here just to be really sure that we don't
00421      * overwrite an existing file.  However, there shouldn't be one, so
00422      * rename() is an acceptable substitute except for the truly paranoid.
00423      */
00424 #if HAVE_WORKING_LINK
00425     if (link(tmppath, path) < 0)
00426         ereport(ERROR,
00427                 (errcode_for_file_access(),
00428                  errmsg("could not link file \"%s\" to \"%s\": %m",
00429                         tmppath, path)));
00430     unlink(tmppath);
00431 #else
00432     if (rename(tmppath, path) < 0)
00433         ereport(ERROR,
00434                 (errcode_for_file_access(),
00435                  errmsg("could not rename file \"%s\" to \"%s\": %m",
00436                         tmppath, path)));
00437 #endif
00438 
00439     /* The history file can be archived immediately. */
00440     TLHistoryFileName(histfname, newTLI);
00441     XLogArchiveNotify(histfname);
00442 }
00443 
00444 /*
00445  * Writes a history file for given timeline and contents.
00446  *
00447  * Currently this is only used in the walreceiver process, and so there are
00448  * no locking considerations.  But we should be just as tense as XLogFileInit
00449  * to avoid emplacing a bogus file.
00450  */
00451 void
00452 writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
00453 {
00454     char        path[MAXPGPATH];
00455     char        tmppath[MAXPGPATH];
00456     int         fd;
00457 
00458     /*
00459      * Write into a temp file name.
00460      */
00461     snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
00462 
00463     unlink(tmppath);
00464 
00465     /* do not use get_sync_bit() here --- want to fsync only at end of fill */
00466     fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
00467                            S_IRUSR | S_IWUSR);
00468     if (fd < 0)
00469         ereport(ERROR,
00470                 (errcode_for_file_access(),
00471                  errmsg("could not create file \"%s\": %m", tmppath)));
00472 
00473     errno = 0;
00474     if ((int) write(fd, content, size) != size)
00475     {
00476         int         save_errno = errno;
00477 
00478         /*
00479          * If we fail to make the file, delete it to release disk space
00480          */
00481         unlink(tmppath);
00482         /* if write didn't set errno, assume problem is no disk space */
00483         errno = save_errno ? save_errno : ENOSPC;
00484 
00485         ereport(ERROR,
00486                 (errcode_for_file_access(),
00487                  errmsg("could not write to file \"%s\": %m", tmppath)));
00488     }
00489 
00490     if (pg_fsync(fd) != 0)
00491         ereport(ERROR,
00492                 (errcode_for_file_access(),
00493                  errmsg("could not fsync file \"%s\": %m", tmppath)));
00494 
00495     if (CloseTransientFile(fd))
00496         ereport(ERROR,
00497                 (errcode_for_file_access(),
00498                  errmsg("could not close file \"%s\": %m", tmppath)));
00499 
00500 
00501     /*
00502      * Now move the completed history file into place with its final name.
00503      */
00504     TLHistoryFilePath(path, tli);
00505 
00506     /*
00507      * Prefer link() to rename() here just to be really sure that we don't
00508      * overwrite an existing logfile.  However, there shouldn't be one, so
00509      * rename() is an acceptable substitute except for the truly paranoid.
00510      */
00511 #if HAVE_WORKING_LINK
00512     if (link(tmppath, path) < 0)
00513         ereport(ERROR,
00514                 (errcode_for_file_access(),
00515                  errmsg("could not link file \"%s\" to \"%s\": %m",
00516                         tmppath, path)));
00517     unlink(tmppath);
00518 #else
00519     if (rename(tmppath, path) < 0)
00520         ereport(ERROR,
00521                 (errcode_for_file_access(),
00522                  errmsg("could not rename file \"%s\" to \"%s\": %m",
00523                         tmppath, path)));
00524 #endif
00525 }
00526 
00527 /*
00528  * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
00529  */
00530 bool
00531 tliInHistory(TimeLineID tli, List *expectedTLEs)
00532 {
00533     ListCell *cell;
00534 
00535     foreach(cell, expectedTLEs)
00536     {
00537         if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
00538             return true;
00539     }
00540 
00541     return false;
00542 }
00543 
00544 /*
00545  * Returns the ID of the timeline in use at a particular point in time, in
00546  * the given timeline history.
00547  */
00548 TimeLineID
00549 tliOfPointInHistory(XLogRecPtr ptr, List *history)
00550 {
00551     ListCell *cell;
00552 
00553     foreach(cell, history)
00554     {
00555         TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
00556         if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
00557             (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
00558         {
00559             /* found it */
00560             return tle->tli;
00561         }
00562     }
00563 
00564     /* shouldn't happen. */
00565     elog(ERROR, "timeline history was not contiguous");
00566     return 0;   /* keep compiler quiet */
00567 }
00568 
00569 /*
00570  * Returns the point in history where we branched off the given timeline,
00571  * and the timeline we branched to (*nextTLI). Returns InvalidXLogRecPtr if
00572  * the timeline is current, ie. we have not branched off from it, and throws
00573  * an error if the timeline is not part of this server's history.
00574  */
00575 XLogRecPtr
00576 tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
00577 {
00578     ListCell   *cell;
00579 
00580     if (nextTLI)
00581         *nextTLI = 0;
00582     foreach (cell, history)
00583     {
00584         TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
00585 
00586         if (tle->tli == tli)
00587             return tle->end;
00588         if (nextTLI)
00589             *nextTLI = tle->tli;
00590     }
00591 
00592     ereport(ERROR,
00593             (errmsg("requested timeline %u is not in this server's history",
00594                     tli)));
00595     return InvalidXLogRecPtr; /* keep compiler quiet */
00596 }