Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b882246

Browse files
committed
Fix two timeline handling bugs in pg_receivexlog.
When a timeline history file is fetched from server, it is initially created with a temporary file name, and renamed to place. However, the temporary file name was constructed using an uninitialized buffer. Usually that meant that the file was created in current directory instead of the target, which usually goes unnoticed, but if the target is on a different filesystem than the current dir, the rename() would fail. Fix that. The second issue is that pg_receivexlog would not take .partial files into account when determining when scanning the target directory for existing WAL files. If the timeline has switched in the server several times in the last WAL segment, and pg_receivexlog is restarted, it would choose a too old starting point. That's not a problem as long as the old WAL segment exists in the server and can be streamed over, but will cause a failure if it's not. Backpatch to 9.3, where this timeline handling code was written. Analysed by Andrew Gierth, bug #8453, based on a bug report on IRC.
1 parent 496439d commit b882246

File tree

2 files changed

+52
-29
lines changed

2 files changed

+52
-29
lines changed

src/bin/pg_basebackup/pg_receivexlog.c

+49-25
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ FindStreamingStart(uint32 *tli)
121121
struct dirent *dirent;
122122
XLogSegNo high_segno = 0;
123123
uint32 high_tli = 0;
124+
bool high_ispartial = false;
124125

125126
dir = opendir(basedir);
126127
if (dir == NULL)
@@ -132,20 +133,32 @@ FindStreamingStart(uint32 *tli)
132133

133134
while ((dirent = readdir(dir)) != NULL)
134135
{
135-
char fullpath[MAXPGPATH];
136-
struct stat statbuf;
137136
uint32 tli;
138137
unsigned int log,
139138
seg;
140139
XLogSegNo segno;
140+
bool ispartial;
141141

142142
/*
143143
* Check if the filename looks like an xlog file, or a .partial file.
144144
* Xlog files are always 24 characters, and .partial files are 32
145145
* characters.
146146
*/
147-
if (strlen(dirent->d_name) != 24 ||
148-
strspn(dirent->d_name, "0123456789ABCDEF") != 24)
147+
if (strlen(dirent->d_name) == 24)
148+
{
149+
if (strspn(dirent->d_name, "0123456789ABCDEF") != 24)
150+
continue;
151+
ispartial = false;
152+
}
153+
else if (strlen(dirent->d_name) == 32)
154+
{
155+
if (strspn(dirent->d_name, "0123456789ABCDEF") != 24)
156+
continue;
157+
if (strcmp(&dirent->d_name[24], ".partial") != 0)
158+
continue;
159+
ispartial = true;
160+
}
161+
else
149162
continue;
150163

151164
/*
@@ -160,31 +173,40 @@ FindStreamingStart(uint32 *tli)
160173
}
161174
segno = ((uint64) log) << 32 | seg;
162175

163-
/* Check if this is a completed segment or not */
164-
snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
165-
if (stat(fullpath, &statbuf) != 0)
176+
/*
177+
* Check that the segment has the right size, if it's supposed to be
178+
* completed.
179+
*/
180+
if (!ispartial)
166181
{
167-
fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"),
168-
progname, fullpath, strerror(errno));
169-
disconnect_and_exit(1);
170-
}
182+
struct stat statbuf;
183+
char fullpath[MAXPGPATH];
171184

172-
if (statbuf.st_size == XLOG_SEG_SIZE)
173-
{
174-
/* Completed segment */
175-
if (segno > high_segno || (segno == high_segno && tli > high_tli))
185+
snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
186+
if (stat(fullpath, &statbuf) != 0)
187+
{
188+
fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"),
189+
progname, fullpath, strerror(errno));
190+
disconnect_and_exit(1);
191+
}
192+
193+
if (statbuf.st_size != XLOG_SEG_SIZE)
176194
{
177-
high_segno = segno;
178-
high_tli = tli;
195+
fprintf(stderr,
196+
_("%s: segment file \"%s\" has incorrect size %d, skipping\n"),
197+
progname, dirent->d_name, (int) statbuf.st_size);
179198
continue;
180199
}
181200
}
182-
else
201+
202+
/* Looks like a valid segment. Remember that we saw it. */
203+
if ((segno > high_segno) ||
204+
(segno == high_segno && tli > high_tli) ||
205+
(segno == high_segno && tli == high_tli && high_ispartial && !ispartial))
183206
{
184-
fprintf(stderr,
185-
_("%s: segment file \"%s\" has incorrect size %d, skipping\n"),
186-
progname, dirent->d_name, (int) statbuf.st_size);
187-
continue;
207+
high_segno = segno;
208+
high_tli = tli;
209+
high_ispartial = ispartial;
188210
}
189211
}
190212

@@ -195,10 +217,12 @@ FindStreamingStart(uint32 *tli)
195217
XLogRecPtr high_ptr;
196218

197219
/*
198-
* Move the starting pointer to the start of the next segment, since
199-
* the highest one we've seen was completed.
220+
* Move the starting pointer to the start of the next segment, if
221+
* the highest one we saw was completed. Otherwise start streaming
222+
* from the beginning of the .partial segment.
200223
*/
201-
high_segno++;
224+
if (!high_ispartial)
225+
high_segno++;
202226

203227
XLogSegNoOffsetToRecPtr(high_segno, 0, high_ptr);
204228

src/bin/pg_basebackup/receivelog.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,7 @@ close_walfile(char *basedir, char *partial_suffix)
166166
walfile = -1;
167167

168168
/*
169-
* Rename the .partial file only if we've completed writing the whole
170-
* segment or segment_complete is true.
169+
* If we finished writing a .partial file, rename it into place.
171170
*/
172171
if (currpos == XLOG_SEG_SIZE && partial_suffix)
173172
{
@@ -306,6 +305,8 @@ writeTimeLineHistoryFile(char *basedir, TimeLineID tli, char *filename, char *co
306305
return false;
307306
}
308307

308+
snprintf(path, sizeof(path), "%s/%s", basedir, histfname);
309+
309310
/*
310311
* Write into a temp file name.
311312
*/
@@ -356,8 +357,6 @@ writeTimeLineHistoryFile(char *basedir, TimeLineID tli, char *filename, char *co
356357
/*
357358
* Now move the completed history file into place with its final name.
358359
*/
359-
360-
snprintf(path, sizeof(path), "%s/%s", basedir, histfname);
361360
if (rename(tmppath, path) < 0)
362361
{
363362
fprintf(stderr, _("%s: could not rename file \"%s\" to \"%s\": %s\n"),

0 commit comments

Comments
 (0)