Skip to content

Commit bc34223

Browse files
committed
pg_basebackup pg_receivexlog: Issue fsync more carefully
Several places weren't careful about fsyncing in the way. See 1d4a0ab and 606e0f9 for details about required fsyncs. This adds a couple of functions in src/common/ that have an equivalent in the backend: durable_rename(), fsync_parent_path() From: Michael Paquier <michael.paquier@gmail.com>
1 parent bf5bb2e commit bc34223

File tree

4 files changed

+162
-29
lines changed

4 files changed

+162
-29
lines changed

src/bin/pg_basebackup/pg_basebackup.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <zlib.h>
2828
#endif
2929

30+
#include "common/file_utils.h"
3031
#include "common/string.h"
3132
#include "fe_utils/string_utils.h"
3233
#include "getopt_long.h"
@@ -1196,6 +1197,10 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
11961197

11971198
if (copybuf != NULL)
11981199
PQfreemem(copybuf);
1200+
1201+
/* sync the resulting tar file, errors are not considered fatal */
1202+
if (strcmp(basedir, "-") != 0)
1203+
(void) fsync_fname(filename, false, progname);
11991204
}
12001205

12011206

@@ -1472,6 +1477,11 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
14721477

14731478
if (basetablespace && writerecoveryconf)
14741479
WriteRecoveryConf();
1480+
1481+
/*
1482+
* No data is synced here, everything is done for all tablespaces at the
1483+
* end.
1484+
*/
14751485
}
14761486

14771487
/*
@@ -1950,6 +1960,23 @@ BaseBackup(void)
19501960
PQclear(res);
19511961
PQfinish(conn);
19521962

1963+
/*
1964+
* Make data persistent on disk once backup is completed. For tar
1965+
* format once syncing the parent directory is fine, each tar file
1966+
* created per tablespace has been already synced. In plain format,
1967+
* all the data of the base directory is synced, taking into account
1968+
* all the tablespaces. Errors are not considered fatal.
1969+
*/
1970+
if (format == 't')
1971+
{
1972+
if (strcmp(basedir, "-") != 0)
1973+
(void) fsync_fname(basedir, true, progname);
1974+
}
1975+
else
1976+
{
1977+
(void) fsync_pgdata(basedir, progname);
1978+
}
1979+
19531980
if (verbose)
19541981
fprintf(stderr, "%s: base backup completed\n", progname);
19551982
}

src/bin/pg_basebackup/receivelog.c

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "libpq-fe.h"
2828
#include "access/xlog_internal.h"
29+
#include "common/file_utils.h"
2930

3031

3132
/* fd and filename for currently open WAL file */
@@ -71,17 +72,13 @@ mark_file_as_archived(const char *basedir, const char *fname)
7172
return false;
7273
}
7374

74-
if (fsync(fd) != 0)
75-
{
76-
fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
77-
progname, tmppath, strerror(errno));
78-
79-
close(fd);
75+
close(fd);
8076

77+
if (fsync_fname(tmppath, false, progname) != 0)
8178
return false;
82-
}
8379

84-
close(fd);
80+
if (fsync_parent_path(tmppath, progname) != 0)
81+
return false;
8582

8683
return true;
8784
}
@@ -132,6 +129,16 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
132129
{
133130
/* File is open and ready to use */
134131
walfile = f;
132+
133+
/*
134+
* fsync, in case of a previous crash between padding and fsyncing the
135+
* file.
136+
*/
137+
if (fsync_fname(fn, false, progname) != 0)
138+
return false;
139+
if (fsync_parent_path(fn, progname) != 0)
140+
return false;
141+
135142
return true;
136143
}
137144
if (statbuf.st_size != 0)
@@ -160,6 +167,17 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
160167
}
161168
free(zerobuf);
162169

170+
/*
171+
* fsync WAL file and containing directory, to ensure the file is
172+
* persistently created and zeroed. That's particularly important when
173+
* using synchronous mode, where the file is modified and fsynced
174+
* in-place, without a directory fsync.
175+
*/
176+
if (fsync_fname(fn, false, progname) != 0)
177+
return false;
178+
if (fsync_parent_path(fn, progname) != 0)
179+
return false;
180+
163181
if (lseek(f, SEEK_SET, 0) != 0)
164182
{
165183
fprintf(stderr,
@@ -220,10 +238,9 @@ close_walfile(StreamCtl *stream, XLogRecPtr pos)
220238

221239
snprintf(oldfn, sizeof(oldfn), "%s/%s%s", stream->basedir, current_walfile_name, stream->partial_suffix);
222240
snprintf(newfn, sizeof(newfn), "%s/%s", stream->basedir, current_walfile_name);
223-
if (rename(oldfn, newfn) != 0)
241+
if (durable_rename(oldfn, newfn, progname) != 0)
224242
{
225-
fprintf(stderr, _("%s: could not rename file \"%s\": %s\n"),
226-
progname, current_walfile_name, strerror(errno));
243+
/* durable_rename produced a log entry */
227244
return false;
228245
}
229246
}
@@ -341,14 +358,6 @@ writeTimeLineHistoryFile(StreamCtl *stream, char *filename, char *content)
341358
return false;
342359
}
343360

344-
if (fsync(fd) != 0)
345-
{
346-
close(fd);
347-
fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
348-
progname, tmppath, strerror(errno));
349-
return false;
350-
}
351-
352361
if (close(fd) != 0)
353362
{
354363
fprintf(stderr, _("%s: could not close file \"%s\": %s\n"),
@@ -359,10 +368,9 @@ writeTimeLineHistoryFile(StreamCtl *stream, char *filename, char *content)
359368
/*
360369
* Now move the completed history file into place with its final name.
361370
*/
362-
if (rename(tmppath, path) < 0)
371+
if (durable_rename(tmppath, path, progname) < 0)
363372
{
364-
fprintf(stderr, _("%s: could not rename file \"%s\" to \"%s\": %s\n"),
365-
progname, tmppath, path, strerror(errno));
373+
/* durable_rename produced a log entry */
366374
return false;
367375
}
368376

src/common/file_utils.c

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static void pre_sync_fname(const char *fname, bool isdir,
3434
const char *progname);
3535
#endif
3636
static void walkdir(const char *path,
37-
void (*action) (const char *fname, bool isdir, const char *progname),
37+
int (*action) (const char *fname, bool isdir, const char *progname),
3838
bool process_symlinks, const char *progname);
3939

4040
/*
@@ -120,7 +120,7 @@ fsync_pgdata(const char *pg_data, const char *progname)
120120
*/
121121
static void
122122
walkdir(const char *path,
123-
void (*action) (const char *fname, bool isdir, const char *progname),
123+
int (*action) (const char *fname, bool isdir, const char *progname),
124124
bool process_symlinks, const char *progname)
125125
{
126126
DIR *dir;
@@ -228,7 +228,7 @@ pre_sync_fname(const char *fname, bool isdir, const char *progname)
228228
* directories on systems where that isn't allowed/required. Reports
229229
* other errors non-fatally.
230230
*/
231-
void
231+
int
232232
fsync_fname(const char *fname, bool isdir, const char *progname)
233233
{
234234
int fd;
@@ -256,10 +256,10 @@ fsync_fname(const char *fname, bool isdir, const char *progname)
256256
if (fd < 0)
257257
{
258258
if (errno == EACCES || (isdir && errno == EISDIR))
259-
return;
259+
return 0;
260260
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
261261
progname, fname, strerror(errno));
262-
return;
262+
return -1;
263263
}
264264

265265
returncode = fsync(fd);
@@ -269,8 +269,103 @@ fsync_fname(const char *fname, bool isdir, const char *progname)
269269
* those errors. Anything else needs to be reported.
270270
*/
271271
if (returncode != 0 && !(isdir && errno == EBADF))
272+
{
272273
fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
273274
progname, fname, strerror(errno));
275+
return -1;
276+
}
274277

275278
(void) close(fd);
279+
return 0;
280+
}
281+
282+
/*
283+
* fsync_parent_path -- fsync the parent path of a file or directory
284+
*
285+
* This is aimed at making file operations persistent on disk in case of
286+
* an OS crash or power failure.
287+
*/
288+
int
289+
fsync_parent_path(const char *fname, const char *progname)
290+
{
291+
char parentpath[MAXPGPATH];
292+
293+
strlcpy(parentpath, fname, MAXPGPATH);
294+
get_parent_directory(parentpath);
295+
296+
/*
297+
* get_parent_directory() returns an empty string if the input argument is
298+
* just a file name (see comments in path.c), so handle that as being the
299+
* current directory.
300+
*/
301+
if (strlen(parentpath) == 0)
302+
strlcpy(parentpath, ".", MAXPGPATH);
303+
304+
if (fsync_fname(parentpath, true, progname) != 0)
305+
return -1;
306+
307+
return 0;
308+
}
309+
310+
/*
311+
* durable_rename -- rename(2) wrapper, issuing fsyncs required for durability
312+
*
313+
* Wrapper around rename, similar to the backend version.
314+
*/
315+
int
316+
durable_rename(const char *oldfile, const char *newfile, const char *progname)
317+
{
318+
int fd;
319+
320+
/*
321+
* First fsync the old and target path (if it exists), to ensure that they
322+
* are properly persistent on disk. Syncing the target file is not
323+
* strictly necessary, but it makes it easier to reason about crashes;
324+
* because it's then guaranteed that either source or target file exists
325+
* after a crash.
326+
*/
327+
if (fsync_fname(oldfile, false, progname) != 0)
328+
return -1;
329+
330+
fd = open(newfile, PG_BINARY | O_RDWR, 0);
331+
if (fd < 0)
332+
{
333+
if (errno != ENOENT)
334+
{
335+
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
336+
progname, newfile, strerror(errno));
337+
return -1;
338+
}
339+
}
340+
else
341+
{
342+
if (fsync(fd) != 0)
343+
{
344+
fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
345+
progname, newfile, strerror(errno));
346+
close(fd);
347+
return -1;
348+
}
349+
close(fd);
350+
}
351+
352+
/* Time to do the real deal... */
353+
if (rename(oldfile, newfile) != 0)
354+
{
355+
fprintf(stderr, _("%s: could not rename file \"%s\" to \"%s\": %s\n"),
356+
progname, oldfile, newfile, strerror(errno));
357+
return -1;
358+
}
359+
360+
/*
361+
* To guarantee renaming the file is persistent, fsync the file with its
362+
* new name, and its containing directory.
363+
*/
364+
if (fsync_fname(newfile, false, progname) != 0)
365+
return -1;
366+
367+
if (fsync_parent_path(newfile, progname) != 0)
368+
return -1;
369+
370+
return 0;
276371
}

src/include/common/file_utils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
#ifndef FILE_UTILS_H
1616
#define FILE_UTILS_H
1717

18-
extern void fsync_fname(const char *fname, bool isdir,
19-
const char *progname);
18+
extern int fsync_fname(const char *fname, bool isdir,
19+
const char *progname);
2020
extern void fsync_pgdata(const char *pg_data, const char *progname);
21+
extern int durable_rename(const char *oldfile, const char *newfile,
22+
const char *progname);
23+
extern int fsync_parent_path(const char *fname, const char *progname);
2124

2225
#endif /* FILE_UTILS_H */

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