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

Lines changed: 18 additions & 0 deletions
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

Lines changed: 92 additions & 6 deletions
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

Lines changed: 9 additions & 1 deletion
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)
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