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

Commit 9083353

Browse files
committed
pg_basebackup: Clean created directories on failure
Like initdb, clean up created data and xlog directories, unless the new -n/--noclean option is specified. Tablespace directories are not cleaned up, but a message is written about that. Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
1 parent 63c1a87 commit 9083353

File tree

3 files changed

+119
-7
lines changed

3 files changed

+119
-7
lines changed

doc/src/sgml/ref/pg_basebackup.sgml

+18
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,24 @@ PostgreSQL documentation
398398
</listitem>
399399
</varlistentry>
400400

401+
<varlistentry>
402+
<term><option>-n</option></term>
403+
<term><option>--noclean</option></term>
404+
<listitem>
405+
<para>
406+
By default, when <command>pg_basebackup</command> aborts with an
407+
error, it removes any directories it might have created before
408+
discovering that it cannot finish the job (for example, data directory
409+
and transaction log directory). This option inhibits tidying-up and is
410+
thus useful for debugging.
411+
</para>
412+
413+
<para>
414+
Note that tablespace directories are not cleaned up either way.
415+
</para>
416+
</listitem>
417+
</varlistentry>
418+
401419
<varlistentry>
402420
<term><option>-P</option></term>
403421
<term><option>--progress</option></term>

src/bin/pg_basebackup/pg_basebackup.c

+92-6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ static TablespaceList tablespace_dirs = {NULL, NULL};
5858
static char *xlog_dir = "";
5959
static char format = 'p'; /* p(lain)/t(ar) */
6060
static char *label = "pg_basebackup base backup";
61+
static bool noclean = false;
6162
static bool showprogress = false;
6263
static int verbose = 0;
6364
static int compresslevel = 0;
@@ -69,6 +70,13 @@ static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
6970
static pg_time_t last_progress_report = 0;
7071
static int32 maxrate = 0; /* no limit by default */
7172

73+
static bool success = false;
74+
static bool made_new_pgdata = false;
75+
static bool found_existing_pgdata = false;
76+
static bool made_new_xlogdir = false;
77+
static bool found_existing_xlogdir = false;
78+
static bool made_tablespace_dirs = false;
79+
static bool found_tablespace_dirs = false;
7280

7381
/* Progress counters */
7482
static uint64 totalsize;
@@ -82,6 +90,7 @@ static int bgpipe[2] = {-1, -1};
8290

8391
/* Handle to child process */
8492
static pid_t bgchild = -1;
93+
static bool in_log_streamer = false;
8594

8695
/* End position for xlog streaming, empty string if unknown yet */
8796
static XLogRecPtr xlogendptr;
@@ -98,7 +107,7 @@ static PQExpBuffer recoveryconfcontents = NULL;
98107
/* Function headers */
99108
static void usage(void);
100109
static void disconnect_and_exit(int code);
101-
static void verify_dir_is_empty_or_create(char *dirname);
110+
static void verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found);
102111
static void progress_report(int tablespacenum, const char *filename, bool force);
103112

104113
static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
@@ -114,6 +123,69 @@ static const char *get_tablespace_mapping(const char *dir);
114123
static void tablespace_list_append(const char *arg);
115124

116125

126+
static void
127+
cleanup_directories_atexit(void)
128+
{
129+
if (success || in_log_streamer)
130+
return;
131+
132+
if (!noclean)
133+
{
134+
if (made_new_pgdata)
135+
{
136+
fprintf(stderr, _("%s: removing data directory \"%s\"\n"),
137+
progname, basedir);
138+
if (!rmtree(basedir, true))
139+
fprintf(stderr, _("%s: failed to remove data directory\n"),
140+
progname);
141+
}
142+
else if (found_existing_pgdata)
143+
{
144+
fprintf(stderr,
145+
_("%s: removing contents of data directory \"%s\"\n"),
146+
progname, basedir);
147+
if (!rmtree(basedir, false))
148+
fprintf(stderr, _("%s: failed to remove contents of data directory\n"),
149+
progname);
150+
}
151+
152+
if (made_new_xlogdir)
153+
{
154+
fprintf(stderr, _("%s: removing transaction log directory \"%s\"\n"),
155+
progname, xlog_dir);
156+
if (!rmtree(xlog_dir, true))
157+
fprintf(stderr, _("%s: failed to remove transaction log directory\n"),
158+
progname);
159+
}
160+
else if (found_existing_xlogdir)
161+
{
162+
fprintf(stderr,
163+
_("%s: removing contents of transaction log directory \"%s\"\n"),
164+
progname, xlog_dir);
165+
if (!rmtree(xlog_dir, false))
166+
fprintf(stderr, _("%s: failed to remove contents of transaction log directory\n"),
167+
progname);
168+
}
169+
}
170+
else
171+
{
172+
if (made_new_pgdata || found_existing_pgdata)
173+
fprintf(stderr,
174+
_("%s: data directory \"%s\" not removed at user's request\n"),
175+
progname, basedir);
176+
177+
if (made_new_xlogdir || found_existing_xlogdir)
178+
fprintf(stderr,
179+
_("%s: transaction log directory \"%s\" not removed at user's request\n"),
180+
progname, xlog_dir);
181+
}
182+
183+
if (made_tablespace_dirs || found_tablespace_dirs)
184+
fprintf(stderr,
185+
_("%s: changes to tablespace directories will not be undone"),
186+
progname);
187+
}
188+
117189
static void
118190
disconnect_and_exit(int code)
119191
{
@@ -253,6 +325,7 @@ usage(void)
253325
printf(_(" -c, --checkpoint=fast|spread\n"
254326
" set fast or spread checkpointing\n"));
255327
printf(_(" -l, --label=LABEL set backup label\n"));
328+
printf(_(" -n, --noclean do not clean up after errors\n"));
256329
printf(_(" -P, --progress show progress information\n"));
257330
printf(_(" -v, --verbose output verbose messages\n"));
258331
printf(_(" -V, --version output version information, then exit\n"));
@@ -375,6 +448,8 @@ LogStreamerMain(logstreamer_param *param)
375448
{
376449
StreamCtl stream;
377450

451+
in_log_streamer = true;
452+
378453
MemSet(&stream, 0, sizeof(stream));
379454
stream.startpos = param->startptr;
380455
stream.timeline = param->timeline;
@@ -501,7 +576,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
501576
* be give and the process ended.
502577
*/
503578
static void
504-
verify_dir_is_empty_or_create(char *dirname)
579+
verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
505580
{
506581
switch (pg_check_dir(dirname))
507582
{
@@ -517,12 +592,16 @@ verify_dir_is_empty_or_create(char *dirname)
517592
progname, dirname, strerror(errno));
518593
disconnect_and_exit(1);
519594
}
595+
if (created)
596+
*created = true;
520597
return;
521598
case 1:
522599

523600
/*
524601
* Exists, empty
525602
*/
603+
if (found)
604+
*found = true;
526605
return;
527606
case 2:
528607
case 3:
@@ -1683,7 +1762,7 @@ BaseBackup(void)
16831762
{
16841763
char *path = (char *) get_tablespace_mapping(PQgetvalue(res, i, 1));
16851764

1686-
verify_dir_is_empty_or_create(path);
1765+
verify_dir_is_empty_or_create(path, &made_tablespace_dirs, &found_tablespace_dirs);
16871766
}
16881767
}
16891768

@@ -1892,6 +1971,7 @@ main(int argc, char **argv)
18921971
{"gzip", no_argument, NULL, 'z'},
18931972
{"compress", required_argument, NULL, 'Z'},
18941973
{"label", required_argument, NULL, 'l'},
1974+
{"noclean", no_argument, NULL, 'n'},
18951975
{"dbname", required_argument, NULL, 'd'},
18961976
{"host", required_argument, NULL, 'h'},
18971977
{"port", required_argument, NULL, 'p'},
@@ -1926,7 +2006,9 @@ main(int argc, char **argv)
19262006
}
19272007
}
19282008

1929-
while ((c = getopt_long(argc, argv, "D:F:r:RT:xX:l:zZ:d:c:h:p:U:s:S:wWvP",
2009+
atexit(cleanup_directories_atexit);
2010+
2011+
while ((c = getopt_long(argc, argv, "D:F:r:RT:xX:l:nzZ:d:c:h:p:U:s:S:wWvP",
19302012
long_options, &option_index)) != -1)
19312013
{
19322014
switch (c)
@@ -2001,6 +2083,9 @@ main(int argc, char **argv)
20012083
case 'l':
20022084
label = pg_strdup(optarg);
20032085
break;
2086+
case 'n':
2087+
noclean = true;
2088+
break;
20042089
case 'z':
20052090
#ifdef HAVE_LIBZ
20062091
compresslevel = Z_DEFAULT_COMPRESSION;
@@ -2170,14 +2255,14 @@ main(int argc, char **argv)
21702255
* unless we are writing to stdout.
21712256
*/
21722257
if (format == 'p' || strcmp(basedir, "-") != 0)
2173-
verify_dir_is_empty_or_create(basedir);
2258+
verify_dir_is_empty_or_create(basedir, &made_new_pgdata, &found_existing_pgdata);
21742259

21752260
/* Create transaction log symlink, if required */
21762261
if (strcmp(xlog_dir, "") != 0)
21772262
{
21782263
char *linkloc;
21792264

2180-
verify_dir_is_empty_or_create(xlog_dir);
2265+
verify_dir_is_empty_or_create(xlog_dir, &made_new_xlogdir, &found_existing_xlogdir);
21812266

21822267
/* form name of the place where the symlink must go */
21832268
linkloc = psprintf("%s/pg_xlog", basedir);
@@ -2198,5 +2283,6 @@ main(int argc, char **argv)
21982283

21992284
BaseBackup();
22002285

2286+
success = true;
22012287
return 0;
22022288
}

src/bin/pg_basebackup/t/010_pg_basebackup.pl

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use Config;
55
use PostgresNode;
66
use TestLib;
7-
use Test::More tests => 51;
7+
use Test::More tests => 54;
88

99
program_help_ok('pg_basebackup');
1010
program_version_ok('pg_basebackup');
@@ -40,6 +40,14 @@
4040
[ 'pg_basebackup', '-D', "$tempdir/backup" ],
4141
'pg_basebackup fails because of WAL configuration');
4242

43+
ok(! -d "$tempdir/backup", 'backup directory was cleaned up');
44+
45+
$node->command_fails(
46+
[ 'pg_basebackup', '-D', "$tempdir/backup", '-n' ],
47+
'failing run with noclean option');
48+
49+
ok(-d "$tempdir/backup", 'backup directory was created and left behind');
50+
4351
open CONF, ">>$pgdata/postgresql.conf";
4452
print CONF "max_replication_slots = 10\n";
4553
print CONF "max_wal_senders = 10\n";

0 commit comments

Comments
 (0)