Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9ace401

Browse files
author
Artur Zakirov
committed
Refactoring do_restore() and do_validate(). Check tablespace mapping before actual restore
1 parent 320d0b6 commit 9ace401

File tree

5 files changed

+187
-141
lines changed

5 files changed

+187
-141
lines changed

doc/pg_probackup.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ Specifies whether to stop just after the specified recovery target (true), or ju
480480

481481
Specifies recovering into a particular timeline.
482482

483-
-T
483+
-T OLDDIR=NEWDIR
484484
--tablespace-mapping=OLDDIR=NEWDIR
485485

486486
Relocate the tablespace in directory `OLDDIR` to `NEWDIR` during restore. Both

restore.c

+120-67
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ typedef struct TablespaceListCell
3030
struct TablespaceListCell *next;
3131
char old_dir[MAXPGPATH];
3232
char new_dir[MAXPGPATH];
33+
bool checked; /* If this mapping was checked during
34+
restore */
3335
} TablespaceListCell;
3436

3537
typedef struct TablespaceList
@@ -54,6 +56,7 @@ typedef struct TablespaceCreatedList
5456
static void restore_database(pgBackup *backup);
5557
static void restore_directories(const char *pg_data_dir,
5658
const char *backup_dir);
59+
static void check_tablespace_mapping(pgBackup *backup);
5760
static void create_recovery_conf(time_t backup_id,
5861
const char *target_time,
5962
const char *target_xid,
@@ -82,20 +85,25 @@ do_restore(time_t backup_id,
8285
{
8386
int i;
8487
int base_index; /* index of base (full) backup */
88+
int last_diff_index = -1; /* index of last differential backup */
8589
int ret;
8690
parray *backups;
8791

8892
parray *timelines;
8993
pgBackup *base_backup = NULL;
9094
pgBackup *dest_backup = NULL;
9195
pgRecoveryTarget *rt = NULL;
92-
bool backup_id_found = false;
96+
bool need_recovery_conf = false;
9397

9498
/* PGDATA and ARCLOG_PATH are always required */
9599
if (pgdata == NULL)
96100
elog(ERROR,
97101
"required parameter not specified: PGDATA (-D, --pgdata)");
98102

103+
/* Check if restore destination empty */
104+
if (!dir_is_empty(pgdata))
105+
elog(ERROR, "restore destination is not empty: \"%s\"", pgdata);
106+
99107
elog(LOG, "========================================");
100108
elog(LOG, "restore start");
101109

@@ -131,110 +139,91 @@ do_restore(time_t backup_id,
131139
elog(LOG, "searching recent full backup");
132140
for (i = 0; i < parray_num(backups); i++)
133141
{
142+
bool satisfied = false;
143+
134144
base_backup = (pgBackup *) parray_get(backups, i);
135145

136146
if (backup_id && base_backup->start_time > backup_id)
137147
continue;
138148

139-
if (backup_id == base_backup->start_time &&
140-
base_backup->status == BACKUP_STATUS_OK)
149+
if (backup_id == base_backup->start_time)
141150
{
142-
backup_id_found = true;
151+
/* Checks for target backup */
152+
if (base_backup->status != BACKUP_STATUS_OK)
153+
elog(ERROR, "given backup %s is in %s status",
154+
base36enc(backup_id), status2str(base_backup->status));
155+
143156
dest_backup = base_backup;
144157
}
145158

146-
if (backup_id == base_backup->start_time &&
147-
base_backup->status != BACKUP_STATUS_OK)
148-
elog(ERROR, "given backup %s is %s", base36enc(backup_id),
149-
status2str(base_backup->status));
150-
151159
if (dest_backup != NULL &&
152160
base_backup->backup_mode == BACKUP_MODE_FULL &&
153161
base_backup->status != BACKUP_STATUS_OK)
154-
elog(ERROR, "base backup %s for given backup %s is %s",
162+
elog(ERROR, "base backup %s for given backup %s is in %s status",
155163
base36enc(base_backup->start_time),
156164
base36enc(dest_backup->start_time),
157165
status2str(base_backup->status));
158166

159-
if (base_backup->backup_mode < BACKUP_MODE_FULL ||
160-
base_backup->status != BACKUP_STATUS_OK)
167+
/* Dont check error backups */
168+
if (base_backup->status != BACKUP_STATUS_OK ||
169+
/* Dont check differential backups if we found latest */
170+
(last_diff_index >= 0 && base_backup->backup_mode != BACKUP_MODE_FULL))
161171
continue;
162172

163173
if (target_tli)
164174
{
165175
if (satisfy_timeline(timelines, base_backup) &&
166176
satisfy_recovery_target(base_backup, rt) &&
167-
(backup_id_found || backup_id == 0))
168-
goto base_backup_found;
177+
(dest_backup || backup_id == 0))
178+
satisfied = true;
169179
}
170180
else
171181
if (satisfy_recovery_target(base_backup, rt) &&
172-
(backup_id_found || backup_id == 0))
173-
goto base_backup_found;
182+
(dest_backup || backup_id == 0))
183+
satisfied = true;
174184

175-
backup_id_found = false;
185+
/* Target backup should satisfy restore options */
186+
if (backup_id == base_backup->start_time && !satisfied)
187+
elog(ERROR, "backup %s does not satisfy restore options",
188+
base36enc(base_backup->start_time));
189+
190+
if (satisfied)
191+
{
192+
if (base_backup->backup_mode != BACKUP_MODE_FULL)
193+
last_diff_index = i;
194+
else
195+
goto base_backup_found;
196+
}
176197
}
177198
/* no full backup found, cannot restore */
178-
elog(ERROR, "no full backup found, cannot restore.");
199+
elog(ERROR, "no full backup found, cannot restore");
179200

180201
base_backup_found:
181202
base_index = i;
203+
if (last_diff_index == -1)
204+
last_diff_index = base_index;
182205

183-
/* Check if restore destination empty */
184-
if (!dir_is_empty(pgdata))
185-
elog(ERROR, "restore destination is not empty");
186-
187-
print_backup_lsn(base_backup);
188-
189-
if (backup_id != 0)
190-
stream_wal = base_backup->stream;
191-
192-
/* restore base backup */
193-
restore_database(base_backup);
194-
195-
/* restore following differential backup */
196-
elog(LOG, "searching differential backup...");
206+
Assert(last_diff_index <= base_index);
207+
/* Tablespace directories checking */
208+
check_tablespace_mapping((pgBackup *) parray_get(backups, last_diff_index));
197209

198-
for (i = base_index - 1; i >= 0; i--)
210+
/* Restore backups from base_index to last_diff_index */
211+
need_recovery_conf = target_time != NULL || target_xid != NULL;
212+
for (i = base_index; i >= last_diff_index; i--)
199213
{
200-
pgBackup *backup = (pgBackup *) parray_get(backups, i);
201-
202-
/* don't use incomplete nor different timeline backup */
203-
if (backup->status != BACKUP_STATUS_OK ||
204-
backup->tli != base_backup->tli)
205-
continue;
206-
207-
if (backup->backup_mode == BACKUP_MODE_FULL)
208-
break;
214+
pgBackup *backup = (pgBackup *) parray_get(backups, i);
209215

210-
if (backup_id && backup->start_time > backup_id)
211-
break;
212-
213-
/* use database backup only */
214-
if (backup->backup_mode != BACKUP_MODE_DIFF_PAGE &&
215-
backup->backup_mode != BACKUP_MODE_DIFF_PTRACK)
216-
continue;
217-
218-
/* is the backup is necessary for restore to target timeline ? */
219-
if (target_tli)
216+
if (backup->status == BACKUP_STATUS_OK)
220217
{
221-
if (!satisfy_timeline(timelines, backup) ||
222-
!satisfy_recovery_target(backup, rt))
223-
continue;
224-
}
225-
else
226-
if (!satisfy_recovery_target(backup, rt))
227-
continue;
228-
229-
if (backup_id != 0)
230-
stream_wal = backup->stream;
218+
need_recovery_conf = need_recovery_conf || !backup->stream;
231219

232-
print_backup_lsn(backup);
233-
restore_database(backup);
220+
print_backup_lsn(backup);
221+
restore_database(backup);
222+
}
234223
}
235224

236225
/* create recovery.conf */
237-
if (!stream_wal || target_time != NULL || target_xid != NULL)
226+
if (need_recovery_conf)
238227
create_recovery_conf(backup_id, target_time, target_xid,
239228
target_inclusive, base_backup->tli);
240229

@@ -470,9 +459,12 @@ restore_directories(const char *pg_data_dir, const char *backup_dir)
470459
linked_path, dir_created, link_name);
471460
}
472461

473-
/* Check if restore destination empty */
462+
/*
463+
* This check was done in check_tablespace_mapping(). But do
464+
* it again.
465+
*/
474466
if (!dir_is_empty(linked_path))
475-
elog(ERROR, "restore destination is not empty \"%s\"",
467+
elog(ERROR, "restore destination is not empty: \"%s\"",
476468
linked_path);
477469

478470
if (link_sep)
@@ -523,6 +515,65 @@ restore_directories(const char *pg_data_dir, const char *backup_dir)
523515
parray_free(dirs);
524516
}
525517

518+
/*
519+
* Check that all tablespace mapping entries have correct linked directory
520+
* paths. Linked directories should be empty or do not exist.
521+
*
522+
* If tablespace-mapping option is supplied all OLDDIR entries should have
523+
* entries in tablespace_map file.
524+
*/
525+
static void
526+
check_tablespace_mapping(pgBackup *backup)
527+
{
528+
char backup_path[MAXPGPATH];
529+
parray *links;
530+
size_t i;
531+
TablespaceListCell *cell;
532+
533+
links = parray_new();
534+
535+
pgBackupGetPath(backup, backup_path, lengthof(backup_path), NULL);
536+
read_tablespace_map(links, backup_path);
537+
538+
elog(LOG, "check tablespace directories...");
539+
540+
/* 1 - all linked directories should be empty */
541+
for (i = 0; i < parray_num(links); i++)
542+
{
543+
pgFile *link = (pgFile *) parray_get(links, i);
544+
const char *linked_path = link->linked;
545+
TablespaceListCell *cell;
546+
547+
for (cell = tablespace_dirs.head; cell; cell = cell->next)
548+
if (strcmp(link->linked, cell->old_dir) == 0)
549+
{
550+
linked_path = cell->new_dir;
551+
cell->checked = true;
552+
break;
553+
}
554+
555+
if (!is_absolute_path(linked_path))
556+
elog(ERROR, "tablespace directory is not an absolute path: %s\n",
557+
linked_path);
558+
559+
if (!dir_is_empty(linked_path))
560+
elog(ERROR, "restore destination is not empty: \"%s\"",
561+
linked_path);
562+
}
563+
564+
/* 2 - OLDDIR should has an entry in links */
565+
for (cell = tablespace_dirs.head; cell; cell = cell->next)
566+
{
567+
if (!cell->checked)
568+
elog(ERROR, "--tablespace-mapping option's old directory "
569+
"has not an entry in tablespace_map file: \"%s\"",
570+
cell->old_dir);
571+
}
572+
573+
parray_walk(links, pgBackupFree);
574+
parray_free(links);
575+
}
576+
526577
/*
527578
* Restore files into $PGDATA.
528579
*/
@@ -998,6 +1049,8 @@ opt_tablespace_map(pgut_option *opt, const char *arg)
9981049
elog(ERROR, "new directory is not an absolute path in tablespace mapping: %s\n",
9991050
cell->new_dir);
10001051

1052+
cell->checked = false;
1053+
10011054
if (tablespace_dirs.tail)
10021055
tablespace_dirs.tail->next = cell;
10031056
else

tests/restore_test.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ def test_restore_with_tablespace_mapping_12(self):
515515

516516
# 1 - Try to restore to existing directory
517517
node.stop()
518-
self.assertEqual(six.b("ERROR: restore destination is not empty\n"),
518+
self.assertIn(six.b("ERROR: restore destination is not empty"),
519519
self.restore_pb(node))
520520

521521
# 2 - Try to restore to existing tablespace directory
@@ -524,7 +524,6 @@ def test_restore_with_tablespace_mapping_12(self):
524524
self.restore_pb(node))
525525

526526
# 3 - Restore using tablespace-mapping
527-
node.cleanup()
528527
tblspc_path_new = path.join(node.base_dir, "tblspc_new")
529528
self.assertIn(six.b("INFO: restore complete."),
530529
self.restore_pb(node,

tests/validate_test.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_validate_wal_1(self):
5656
id_backup = self.show_pb(node)[0].id
5757

5858
# Validate to real time
59-
self.assertIn(six.b("INFO: Backup validation stopped on"),
59+
self.assertIn(six.b("INFO: backup validation completed successfully on"),
6060
self.validate_pb(node, options=["--time='{:%Y-%m-%d %H:%M:%S}'".format(
6161
target_time)]))
6262

@@ -66,20 +66,20 @@ def test_validate_wal_1(self):
6666
target_time - timedelta(days=2))]))
6767

6868
# Validate to unreal time #2
69-
self.assertIn(six.b("ERROR: there are no WAL records to time"),
69+
self.assertIn(six.b("ERROR: not enough WAL records to time"),
7070
self.validate_pb(node, options=["--time='{:%Y-%m-%d %H:%M:%S}'".format(
7171
target_time + timedelta(days=2))]))
7272

7373
# Validate to real xid
74-
self.assertIn(six.b("INFO: Backup validation stopped on"),
74+
self.assertIn(six.b("INFO: backup validation completed successfully on"),
7575
self.validate_pb(node, options=["--xid=%s" % target_xid]))
7676

7777
# Validate to unreal xid
78-
self.assertIn(six.b("ERROR: there are no WAL records to xid"),
78+
self.assertIn(six.b("ERROR: not enough WAL records to xid"),
7979
self.validate_pb(node, options=["--xid=%d" % (int(target_xid) + 1000)]))
8080

8181
# Validate with backup ID
82-
self.assertIn(six.b("INFO: Backup validation stopped on"),
82+
self.assertIn(six.b("INFO: backup validation completed successfully on"),
8383
self.validate_pb(node, id_backup))
8484

8585
# Validate broken WAL
@@ -91,4 +91,4 @@ def test_validate_wal_1(self):
9191
f.write(six.b("blablabla"))
9292

9393
res = self.validate_pb(node, id_backup, options=['--xid=%s' % target_xid])
94-
self.assertIn(six.b("there are no WAL records to xid"), res)
94+
self.assertIn(six.b("not enough WAL records to xid"), res)

0 commit comments

Comments
 (0)