#include "postgres.h"
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include "access/timeline.h"
#include "access/xlog_internal.h"
#include "access/xlogdefs.h"
#include "storage/fd.h"
Go to the source code of this file.
Functions | |
void | restoreTimeLineHistoryFiles (TimeLineID begin, TimeLineID end) |
List * | readTimeLineHistory (TimeLineID targetTLI) |
bool | existsTimeLineHistory (TimeLineID probeTLI) |
TimeLineID | findNewestTimeLine (TimeLineID startTLI) |
void | writeTimeLineHistory (TimeLineID newTLI, TimeLineID parentTLI, XLogRecPtr switchpoint, char *reason) |
void | writeTimeLineHistoryFile (TimeLineID tli, char *content, int size) |
bool | tliInHistory (TimeLineID tli, List *expectedTLEs) |
TimeLineID | tliOfPointInHistory (XLogRecPtr ptr, List *history) |
XLogRecPtr | tliSwitchPoint (TimeLineID tli, List *history, TimeLineID *nextTLI) |
bool existsTimeLineHistory | ( | TimeLineID | probeTLI | ) |
Definition at line 206 of file timeline.c.
References AllocateFile(), ArchiveRecoveryRequested, ereport, errcode_for_file_access(), errmsg(), FATAL, FreeFile(), NULL, RestoreArchivedFile(), TLHistoryFileName, and TLHistoryFilePath.
Referenced by findNewestTimeLine(), readRecoveryCommandFile(), and WalRcvFetchTimeLineHistoryFiles().
{ char path[MAXPGPATH]; char histfname[MAXFNAMELEN]; FILE *fd; /* Timeline 1 does not have a history file, so no need to check */ if (probeTLI == 1) return false; if (ArchiveRecoveryRequested) { TLHistoryFileName(histfname, probeTLI); RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false); } else TLHistoryFilePath(path, probeTLI); fd = AllocateFile(path, "r"); if (fd != NULL) { FreeFile(fd); return true; } else { if (errno != ENOENT) ereport(FATAL, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", path))); return false; } }
TimeLineID findNewestTimeLine | ( | TimeLineID | startTLI | ) |
Definition at line 248 of file timeline.c.
References existsTimeLineHistory().
Referenced by readRecoveryCommandFile(), rescanLatestTimeLine(), and StartupXLOG().
{ TimeLineID newestTLI; TimeLineID probeTLI; /* * The algorithm is just to probe for the existence of timeline history * files. XXX is it useful to allow gaps in the sequence? */ newestTLI = startTLI; for (probeTLI = startTLI + 1;; probeTLI++) { if (existsTimeLineHistory(probeTLI)) { newestTLI = probeTLI; /* probeTLI exists */ } else { /* doesn't exist, assume we're done */ break; } } return newestTLI; }
List* readTimeLineHistory | ( | TimeLineID | targetTLI | ) |
Definition at line 74 of file timeline.c.
References AllocateFile(), ArchiveRecoveryRequested, TimeLineHistoryEntry::begin, TimeLineHistoryEntry::end, ereport, errcode_for_file_access(), errhint(), errmsg(), FATAL, FreeFile(), KeepFileRestoredFromArchive(), lcons(), list_make1, NULL, palloc(), RestoreArchivedFile(), TLHistoryFileName, TLHistoryFilePath, and TimeLineHistoryEntry::tli.
Referenced by rescanLatestTimeLine(), StartReplication(), WaitForWALToBecomeAvailable(), XLogFileReadAnyTLI(), and XLogSend().
{ List *result; char path[MAXPGPATH]; char histfname[MAXFNAMELEN]; char fline[MAXPGPATH]; FILE *fd; TimeLineHistoryEntry *entry; TimeLineID lasttli = 0; XLogRecPtr prevend; bool fromArchive = false; /* Timeline 1 does not have a history file, so no need to check */ if (targetTLI == 1) { entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry)); entry->tli = targetTLI; entry->begin = entry->end = InvalidXLogRecPtr; return list_make1(entry); } if (ArchiveRecoveryRequested) { TLHistoryFileName(histfname, targetTLI); fromArchive = RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false); } else TLHistoryFilePath(path, targetTLI); fd = AllocateFile(path, "r"); if (fd == NULL) { if (errno != ENOENT) ereport(FATAL, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", path))); /* Not there, so assume no parents */ entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry)); entry->tli = targetTLI; entry->begin = entry->end = InvalidXLogRecPtr; return list_make1(entry); } result = NIL; /* * Parse the file... */ prevend = InvalidXLogRecPtr; while (fgets(fline, sizeof(fline), fd) != NULL) { /* skip leading whitespace and check for # comment */ char *ptr; TimeLineID tli; uint32 switchpoint_hi; uint32 switchpoint_lo; int nfields; for (ptr = fline; *ptr; ptr++) { if (!isspace((unsigned char) *ptr)) break; } if (*ptr == '\0' || *ptr == '#') continue; nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo); if (nfields < 1) { /* expect a numeric timeline ID as first field of line */ ereport(FATAL, (errmsg("syntax error in history file: %s", fline), errhint("Expected a numeric timeline ID."))); } if (nfields != 3) ereport(FATAL, (errmsg("syntax error in history file: %s", fline), errhint("Expected an XLOG switchpoint location."))); if (result && tli <= lasttli) ereport(FATAL, (errmsg("invalid data in history file: %s", fline), errhint("Timeline IDs must be in increasing sequence."))); lasttli = tli; entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry)); entry->tli = tli; entry->begin = prevend; entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo; prevend = entry->end; /* Build list with newest item first */ result = lcons(entry, result); /* we ignore the remainder of each line */ } FreeFile(fd); if (result && targetTLI <= lasttli) ereport(FATAL, (errmsg("invalid data in history file \"%s\"", path), errhint("Timeline IDs must be less than child timeline's ID."))); /* * Create one more entry for the "tip" of the timeline, which has no * entry in the history file. */ entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry)); entry->tli = targetTLI; entry->begin = prevend; entry->end = InvalidXLogRecPtr; result = lcons(entry, result); /* * If the history file was fetched from archive, save it in pg_xlog for * future reference. */ if (fromArchive) KeepFileRestoredFromArchive(path, histfname); return result; }
void restoreTimeLineHistoryFiles | ( | TimeLineID | begin, | |
TimeLineID | end | |||
) |
Definition at line 48 of file timeline.c.
References KeepFileRestoredFromArchive(), RestoreArchivedFile(), and TLHistoryFileName.
Referenced by rescanLatestTimeLine(), and StartupXLOG().
{ char path[MAXPGPATH]; char histfname[MAXFNAMELEN]; TimeLineID tli; for (tli = begin; tli < end; tli++) { if (tli == 1) continue; TLHistoryFileName(histfname, tli); if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false)) KeepFileRestoredFromArchive(path, histfname); } }
bool tliInHistory | ( | TimeLineID | tli, | |
List * | expectedTLEs | |||
) |
Definition at line 531 of file timeline.c.
References lfirst.
Referenced by checkTimeLineSwitch(), and ReadRecord().
{ ListCell *cell; foreach(cell, expectedTLEs) { if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli) return true; } return false; }
TimeLineID tliOfPointInHistory | ( | XLogRecPtr | ptr, | |
List * | history | |||
) |
Definition at line 549 of file timeline.c.
References TimeLineHistoryEntry::begin, elog, TimeLineHistoryEntry::end, ERROR, lfirst, TimeLineHistoryEntry::tli, and XLogRecPtrIsInvalid.
Referenced by StartupXLOG(), and WaitForWALToBecomeAvailable().
{ ListCell *cell; foreach(cell, history) { TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell); if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) && (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end)) { /* found it */ return tle->tli; } } /* shouldn't happen. */ elog(ERROR, "timeline history was not contiguous"); return 0; /* keep compiler quiet */ }
XLogRecPtr tliSwitchPoint | ( | TimeLineID | tli, | |
List * | history, | |||
TimeLineID * | nextTLI | |||
) |
Definition at line 576 of file timeline.c.
References TimeLineHistoryEntry::end, ereport, errmsg(), ERROR, lfirst, and TimeLineHistoryEntry::tli.
Referenced by StartReplication(), StartupXLOG(), and XLogSend().
{ ListCell *cell; if (nextTLI) *nextTLI = 0; foreach (cell, history) { TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell); if (tle->tli == tli) return tle->end; if (nextTLI) *nextTLI = tle->tli; } ereport(ERROR, (errmsg("requested timeline %u is not in this server's history", tli))); return InvalidXLogRecPtr; /* keep compiler quiet */ }
void writeTimeLineHistory | ( | TimeLineID | newTLI, | |
TimeLineID | parentTLI, | |||
XLogRecPtr | switchpoint, | |||
char * | reason | |||
) |
Definition at line 288 of file timeline.c.
References ArchiveRecoveryRequested, Assert, CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), ERROR, link(), MAXPGPATH, OpenTransientFile(), pg_fsync(), read, RestoreArchivedFile(), snprintf(), TLHistoryFileName, TLHistoryFilePath, unlink(), write, XLogArchiveNotify(), and XLOGDIR.
Referenced by StartupXLOG().
{ char path[MAXPGPATH]; char tmppath[MAXPGPATH]; char histfname[MAXFNAMELEN]; char buffer[BLCKSZ]; int srcfd; int fd; int nbytes; Assert(newTLI > parentTLI); /* else bad selection of newTLI */ /* * Write into a temp file name. */ snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); unlink(tmppath); /* do not use get_sync_bit() here --- want to fsync only at end of fill */ fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", tmppath))); /* * If a history file exists for the parent, copy it verbatim */ if (ArchiveRecoveryRequested) { TLHistoryFileName(histfname, parentTLI); RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false); } else TLHistoryFilePath(path, parentTLI); srcfd = OpenTransientFile(path, O_RDONLY, 0); if (srcfd < 0) { if (errno != ENOENT) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", path))); /* Not there, so assume parent has no parents */ } else { for (;;) { errno = 0; nbytes = (int) read(srcfd, buffer, sizeof(buffer)); if (nbytes < 0 || errno != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", path))); if (nbytes == 0) break; errno = 0; if ((int) write(fd, buffer, nbytes) != nbytes) { int save_errno = errno; /* * If we fail to make the file, delete it to release disk * space */ unlink(tmppath); /* * if write didn't set errno, assume problem is no disk space */ errno = save_errno ? save_errno : ENOSPC; ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", tmppath))); } } CloseTransientFile(srcfd); } /* * Append one line with the details of this timeline split. * * If we did have a parent file, insert an extra newline just in case the * parent file failed to end with one. */ snprintf(buffer, sizeof(buffer), "%s%u\t%X/%X\t%s\n", (srcfd < 0) ? "" : "\n", parentTLI, (uint32) (switchpoint >> 32), (uint32) (switchpoint), reason); nbytes = strlen(buffer); errno = 0; if ((int) write(fd, buffer, nbytes) != nbytes) { int save_errno = errno; /* * If we fail to make the file, delete it to release disk space */ unlink(tmppath); /* if write didn't set errno, assume problem is no disk space */ errno = save_errno ? save_errno : ENOSPC; ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", tmppath))); } if (pg_fsync(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", tmppath))); if (CloseTransientFile(fd)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); /* * Now move the completed history file into place with its final name. */ TLHistoryFilePath(path, newTLI); /* * Prefer link() to rename() here just to be really sure that we don't * overwrite an existing file. However, there shouldn't be one, so * rename() is an acceptable substitute except for the truly paranoid. */ #if HAVE_WORKING_LINK if (link(tmppath, path) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not link file \"%s\" to \"%s\": %m", tmppath, path))); unlink(tmppath); #else if (rename(tmppath, path) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\": %m", tmppath, path))); #endif /* The history file can be archived immediately. */ TLHistoryFileName(histfname, newTLI); XLogArchiveNotify(histfname); }
void writeTimeLineHistoryFile | ( | TimeLineID | tli, | |
char * | content, | |||
int | size | |||
) |
Definition at line 452 of file timeline.c.
References CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), ERROR, link(), MAXPGPATH, OpenTransientFile(), pg_fsync(), snprintf(), TLHistoryFilePath, unlink(), write, and XLOGDIR.
Referenced by ReceiveXlogStream(), and WalRcvFetchTimeLineHistoryFiles().
{ char path[MAXPGPATH]; char tmppath[MAXPGPATH]; int fd; /* * Write into a temp file name. */ snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); unlink(tmppath); /* do not use get_sync_bit() here --- want to fsync only at end of fill */ fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", tmppath))); errno = 0; if ((int) write(fd, content, size) != size) { int save_errno = errno; /* * If we fail to make the file, delete it to release disk space */ unlink(tmppath); /* if write didn't set errno, assume problem is no disk space */ errno = save_errno ? save_errno : ENOSPC; ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", tmppath))); } if (pg_fsync(fd) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", tmppath))); if (CloseTransientFile(fd)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tmppath))); /* * Now move the completed history file into place with its final name. */ TLHistoryFilePath(path, tli); /* * Prefer link() to rename() here just to be really sure that we don't * overwrite an existing logfile. However, there shouldn't be one, so * rename() is an acceptable substitute except for the truly paranoid. */ #if HAVE_WORKING_LINK if (link(tmppath, path) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not link file \"%s\" to \"%s\": %m", tmppath, path))); unlink(tmppath); #else if (rename(tmppath, path) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\": %m", tmppath, path))); #endif }