Skip to content

Commit d5497b9

Browse files
committed
Split off functions related to timeline history files and XLOG archiving.
This is just refactoring, to make the functions accessible outside xlog.c. A followup patch will make use of that, to allow fetching timeline history files over streaming replication.
1 parent 0899556 commit d5497b9

File tree

6 files changed

+1058
-929
lines changed

6 files changed

+1058
-929
lines changed

src/backend/access/transam/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ top_builddir = ../../../..
1313
include $(top_builddir)/src/Makefile.global
1414

1515
OBJS = clog.o transam.o varsup.o xact.o rmgr.o slru.o subtrans.o multixact.o \
16-
twophase.o twophase_rmgr.o xlog.o xlogfuncs.o xlogutils.o
16+
timeline.o twophase.o twophase_rmgr.o xlog.o xlogarchive.o xlogfuncs.o \
17+
xlogutils.o
1718

1819
include $(top_srcdir)/src/backend/common.mk
1920

src/backend/access/transam/timeline.c

Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
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 "<WAL segment>.history". For example, if the
10+
* database starts up and switches to timeline 5, while processing WAL
11+
* segment 000000030000002A00000006 (the old timeline was 3), the timeline
12+
* history file would be called "000000050000002A00000006.history".
13+
*
14+
* Each line in the file represents a timeline switch:
15+
*
16+
* <parentTLI> <xlogfname> <reason>
17+
*
18+
* parentTLI ID of the parent timeline
19+
* xlogfname filename of the WAL segment where the switch happened
20+
* reason human-readable explanation of why the timeline was changed
21+
*
22+
* The fields are separated by tabs. Lines beginning with # are comments, and
23+
* are ignored. Empty lines are also ignored.
24+
*
25+
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
26+
* Portions Copyright (c) 1994, Regents of the University of California
27+
*
28+
* src/backend/access/transam/timeline.c
29+
*
30+
*-------------------------------------------------------------------------
31+
*/
32+
33+
#include "postgres.h"
34+
35+
#include <stdio.h>
36+
#include <unistd.h>
37+
38+
#include "access/timeline.h"
39+
#include "access/xlog_internal.h"
40+
#include "access/xlogdefs.h"
41+
#include "storage/fd.h"
42+
43+
/*
44+
* Try to read a timeline's history file.
45+
*
46+
* If successful, return the list of component TLIs (the given TLI followed by
47+
* its ancestor TLIs). If we can't find the history file, assume that the
48+
* timeline has no parents, and return a list of just the specified timeline
49+
* ID.
50+
*/
51+
List *
52+
readTimeLineHistory(TimeLineID targetTLI)
53+
{
54+
List *result;
55+
char path[MAXPGPATH];
56+
char histfname[MAXFNAMELEN];
57+
char fline[MAXPGPATH];
58+
FILE *fd;
59+
60+
/* Timeline 1 does not have a history file, so no need to check */
61+
if (targetTLI == 1)
62+
return list_make1_int((int) targetTLI);
63+
64+
if (InArchiveRecovery)
65+
{
66+
TLHistoryFileName(histfname, targetTLI);
67+
RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0);
68+
}
69+
else
70+
TLHistoryFilePath(path, targetTLI);
71+
72+
fd = AllocateFile(path, "r");
73+
if (fd == NULL)
74+
{
75+
if (errno != ENOENT)
76+
ereport(FATAL,
77+
(errcode_for_file_access(),
78+
errmsg("could not open file \"%s\": %m", path)));
79+
/* Not there, so assume no parents */
80+
return list_make1_int((int) targetTLI);
81+
}
82+
83+
result = NIL;
84+
85+
/*
86+
* Parse the file...
87+
*/
88+
while (fgets(fline, sizeof(fline), fd) != NULL)
89+
{
90+
/* skip leading whitespace and check for # comment */
91+
char *ptr;
92+
char *endptr;
93+
TimeLineID tli;
94+
95+
for (ptr = fline; *ptr; ptr++)
96+
{
97+
if (!isspace((unsigned char) *ptr))
98+
break;
99+
}
100+
if (*ptr == '\0' || *ptr == '#')
101+
continue;
102+
103+
/* expect a numeric timeline ID as first field of line */
104+
tli = (TimeLineID) strtoul(ptr, &endptr, 0);
105+
if (endptr == ptr)
106+
ereport(FATAL,
107+
(errmsg("syntax error in history file: %s", fline),
108+
errhint("Expected a numeric timeline ID.")));
109+
110+
if (result &&
111+
tli <= (TimeLineID) linitial_int(result))
112+
ereport(FATAL,
113+
(errmsg("invalid data in history file: %s", fline),
114+
errhint("Timeline IDs must be in increasing sequence.")));
115+
116+
/* Build list with newest item first */
117+
result = lcons_int((int) tli, result);
118+
119+
/* we ignore the remainder of each line */
120+
}
121+
122+
FreeFile(fd);
123+
124+
if (result &&
125+
targetTLI <= (TimeLineID) linitial_int(result))
126+
ereport(FATAL,
127+
(errmsg("invalid data in history file \"%s\"", path),
128+
errhint("Timeline IDs must be less than child timeline's ID.")));
129+
130+
result = lcons_int((int) targetTLI, result);
131+
132+
ereport(DEBUG3,
133+
(errmsg_internal("history of timeline %u is %s",
134+
targetTLI, nodeToString(result))));
135+
136+
return result;
137+
}
138+
139+
/*
140+
* Probe whether a timeline history file exists for the given timeline ID
141+
*/
142+
bool
143+
existsTimeLineHistory(TimeLineID probeTLI)
144+
{
145+
char path[MAXPGPATH];
146+
char histfname[MAXFNAMELEN];
147+
FILE *fd;
148+
149+
/* Timeline 1 does not have a history file, so no need to check */
150+
if (probeTLI == 1)
151+
return false;
152+
153+
if (InArchiveRecovery)
154+
{
155+
TLHistoryFileName(histfname, probeTLI);
156+
RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0);
157+
}
158+
else
159+
TLHistoryFilePath(path, probeTLI);
160+
161+
fd = AllocateFile(path, "r");
162+
if (fd != NULL)
163+
{
164+
FreeFile(fd);
165+
return true;
166+
}
167+
else
168+
{
169+
if (errno != ENOENT)
170+
ereport(FATAL,
171+
(errcode_for_file_access(),
172+
errmsg("could not open file \"%s\": %m", path)));
173+
return false;
174+
}
175+
}
176+
177+
/*
178+
* Find the newest existing timeline, assuming that startTLI exists.
179+
*
180+
* Note: while this is somewhat heuristic, it does positively guarantee
181+
* that (result + 1) is not a known timeline, and therefore it should
182+
* be safe to assign that ID to a new timeline.
183+
*/
184+
TimeLineID
185+
findNewestTimeLine(TimeLineID startTLI)
186+
{
187+
TimeLineID newestTLI;
188+
TimeLineID probeTLI;
189+
190+
/*
191+
* The algorithm is just to probe for the existence of timeline history
192+
* files. XXX is it useful to allow gaps in the sequence?
193+
*/
194+
newestTLI = startTLI;
195+
196+
for (probeTLI = startTLI + 1;; probeTLI++)
197+
{
198+
if (existsTimeLineHistory(probeTLI))
199+
{
200+
newestTLI = probeTLI; /* probeTLI exists */
201+
}
202+
else
203+
{
204+
/* doesn't exist, assume we're done */
205+
break;
206+
}
207+
}
208+
209+
return newestTLI;
210+
}
211+
212+
/*
213+
* Create a new timeline history file.
214+
*
215+
* newTLI: ID of the new timeline
216+
* parentTLI: ID of its immediate parent
217+
* endTLI et al: ID of the last used WAL file, for annotation purposes
218+
* reason: human-readable explanation of why the timeline was switched
219+
*
220+
* Currently this is only used at the end recovery, and so there are no locking
221+
* considerations. But we should be just as tense as XLogFileInit to avoid
222+
* emplacing a bogus file.
223+
*/
224+
void
225+
writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
226+
TimeLineID endTLI, XLogSegNo endLogSegNo, char *reason)
227+
{
228+
char path[MAXPGPATH];
229+
char tmppath[MAXPGPATH];
230+
char histfname[MAXFNAMELEN];
231+
char xlogfname[MAXFNAMELEN];
232+
char buffer[BLCKSZ];
233+
int srcfd;
234+
int fd;
235+
int nbytes;
236+
237+
Assert(newTLI > parentTLI); /* else bad selection of newTLI */
238+
239+
/*
240+
* Write into a temp file name.
241+
*/
242+
snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
243+
244+
unlink(tmppath);
245+
246+
/* do not use get_sync_bit() here --- want to fsync only at end of fill */
247+
fd = BasicOpenFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
248+
S_IRUSR | S_IWUSR);
249+
if (fd < 0)
250+
ereport(ERROR,
251+
(errcode_for_file_access(),
252+
errmsg("could not create file \"%s\": %m", tmppath)));
253+
254+
/*
255+
* If a history file exists for the parent, copy it verbatim
256+
*/
257+
if (InArchiveRecovery)
258+
{
259+
TLHistoryFileName(histfname, parentTLI);
260+
RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0);
261+
}
262+
else
263+
TLHistoryFilePath(path, parentTLI);
264+
265+
srcfd = BasicOpenFile(path, O_RDONLY, 0);
266+
if (srcfd < 0)
267+
{
268+
if (errno != ENOENT)
269+
ereport(ERROR,
270+
(errcode_for_file_access(),
271+
errmsg("could not open file \"%s\": %m", path)));
272+
/* Not there, so assume parent has no parents */
273+
}
274+
else
275+
{
276+
for (;;)
277+
{
278+
errno = 0;
279+
nbytes = (int) read(srcfd, buffer, sizeof(buffer));
280+
if (nbytes < 0 || errno != 0)
281+
ereport(ERROR,
282+
(errcode_for_file_access(),
283+
errmsg("could not read file \"%s\": %m", path)));
284+
if (nbytes == 0)
285+
break;
286+
errno = 0;
287+
if ((int) write(fd, buffer, nbytes) != nbytes)
288+
{
289+
int save_errno = errno;
290+
291+
/*
292+
* If we fail to make the file, delete it to release disk
293+
* space
294+
*/
295+
unlink(tmppath);
296+
297+
/*
298+
* if write didn't set errno, assume problem is no disk space
299+
*/
300+
errno = save_errno ? save_errno : ENOSPC;
301+
302+
ereport(ERROR,
303+
(errcode_for_file_access(),
304+
errmsg("could not write to file \"%s\": %m", tmppath)));
305+
}
306+
}
307+
close(srcfd);
308+
}
309+
310+
/*
311+
* Append one line with the details of this timeline split.
312+
*
313+
* If we did have a parent file, insert an extra newline just in case the
314+
* parent file failed to end with one.
315+
*/
316+
XLogFileName(xlogfname, endTLI, endLogSegNo);
317+
318+
snprintf(buffer, sizeof(buffer),
319+
"%s%u\t%s\t%s\n",
320+
(srcfd < 0) ? "" : "\n",
321+
parentTLI,
322+
xlogfname,
323+
reason);
324+
325+
nbytes = strlen(buffer);
326+
errno = 0;
327+
if ((int) write(fd, buffer, nbytes) != nbytes)
328+
{
329+
int save_errno = errno;
330+
331+
/*
332+
* If we fail to make the file, delete it to release disk space
333+
*/
334+
unlink(tmppath);
335+
/* if write didn't set errno, assume problem is no disk space */
336+
errno = save_errno ? save_errno : ENOSPC;
337+
338+
ereport(ERROR,
339+
(errcode_for_file_access(),
340+
errmsg("could not write to file \"%s\": %m", tmppath)));
341+
}
342+
343+
if (pg_fsync(fd) != 0)
344+
ereport(ERROR,
345+
(errcode_for_file_access(),
346+
errmsg("could not fsync file \"%s\": %m", tmppath)));
347+
348+
if (close(fd))
349+
ereport(ERROR,
350+
(errcode_for_file_access(),
351+
errmsg("could not close file \"%s\": %m", tmppath)));
352+
353+
354+
/*
355+
* Now move the completed history file into place with its final name.
356+
*/
357+
TLHistoryFilePath(path, newTLI);
358+
359+
/*
360+
* Prefer link() to rename() here just to be really sure that we don't
361+
* overwrite an existing logfile. However, there shouldn't be one, so
362+
* rename() is an acceptable substitute except for the truly paranoid.
363+
*/
364+
#if HAVE_WORKING_LINK
365+
if (link(tmppath, path) < 0)
366+
ereport(ERROR,
367+
(errcode_for_file_access(),
368+
errmsg("could not link file \"%s\" to \"%s\": %m",
369+
tmppath, path)));
370+
unlink(tmppath);
371+
#else
372+
if (rename(tmppath, path) < 0)
373+
ereport(ERROR,
374+
(errcode_for_file_access(),
375+
errmsg("could not rename file \"%s\" to \"%s\": %m",
376+
tmppath, path)));
377+
#endif
378+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy