Skip to content

Commit 586e336

Browse files
author
Aleksandr Parfenov
committed
Add support of recovery target LSN
1 parent 4c3a86f commit 586e336

File tree

12 files changed

+265
-16
lines changed

12 files changed

+265
-16
lines changed

src/help.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ help_pg_probackup(void)
116116

117117
printf(_("\n %s restore -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
118118
printf(_(" [-D pgdata-dir] [-i backup-id] [--progress]\n"));
119-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
119+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
120120
printf(_(" [--timeline=timeline] [-T OLDDIR=NEWDIR]\n"));
121121
printf(_(" [--immediate] [--recovery-target-name=target-name]\n"));
122122
printf(_(" [--recovery-target-action=pause|promote|shutdown]\n"));
@@ -125,7 +125,7 @@ help_pg_probackup(void)
125125

126126
printf(_("\n %s validate -B backup-dir [--instance=instance_name]\n"), PROGRAM_NAME);
127127
printf(_(" [-i backup-id] [--progress]\n"));
128-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
128+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
129129
printf(_(" [--recovery-target-name=target-name]\n"));
130130
printf(_(" [--timeline=timeline]\n"));
131131

@@ -265,7 +265,7 @@ help_restore(void)
265265
{
266266
printf(_("%s restore -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
267267
printf(_(" [-D pgdata-dir] [-i backup-id] [--progress]\n"));
268-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
268+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
269269
printf(_(" [--timeline=timeline] [-T OLDDIR=NEWDIR]\n"));
270270
printf(_(" [--immediate] [--recovery-target-name=target-name]\n"));
271271
printf(_(" [--recovery-target-action=pause|promote|shutdown]\n"));
@@ -280,6 +280,7 @@ help_restore(void)
280280
printf(_(" --progress show progress\n"));
281281
printf(_(" --time=time time stamp up to which recovery will proceed\n"));
282282
printf(_(" --xid=xid transaction ID up to which recovery will proceed\n"));
283+
printf(_(" --lsn=lsn LSN of the write-ahead log location up to which recovery will proceed\n"));
283284
printf(_(" --inclusive=boolean whether we stop just after the recovery target\n"));
284285
printf(_(" --timeline=timeline recovering into a particular timeline\n"));
285286
printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"));
@@ -323,7 +324,7 @@ help_validate(void)
323324
{
324325
printf(_("%s validate -B backup-dir [--instance=instance_name]\n"), PROGRAM_NAME);
325326
printf(_(" [-i backup-id] [--progress]\n"));
326-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
327+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
327328
printf(_(" [--timeline=timeline]\n\n"));
328329

329330
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
@@ -333,6 +334,7 @@ help_validate(void)
333334
printf(_(" --progress show progress\n"));
334335
printf(_(" --time=time time stamp up to which recovery will proceed\n"));
335336
printf(_(" --xid=xid transaction ID up to which recovery will proceed\n"));
337+
printf(_(" --lsn=lsn LSN of the write-ahead log location up to which recovery will proceed\n"));
336338
printf(_(" --inclusive=boolean whether we stop just after the recovery target\n"));
337339
printf(_(" --timeline=timeline recovering into a particular timeline\n"));
338340
printf(_(" --recovery-target-name=target-name\n"));

src/parsexlog.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ validate_wal(pgBackup *backup,
299299
const char *archivedir,
300300
time_t target_time,
301301
TransactionId target_xid,
302+
XLogRecPtr target_lsn,
302303
TimeLineID tli)
303304
{
304305
XLogRecPtr startpoint = backup->start_lsn;
@@ -350,7 +351,7 @@ validate_wal(pgBackup *backup,
350351
* If recovery target is provided check that we can restore backup to a
351352
* recovery target time or xid.
352353
*/
353-
if (!TransactionIdIsValid(target_xid) && target_time == 0)
354+
if (!TransactionIdIsValid(target_xid) && target_time == 0 && !XRecOffIsValid(target_lsn))
354355
{
355356
/* Recovery target is not given so exit */
356357
elog(INFO, "Backup %s WAL segments are valid", backup_id);
@@ -384,7 +385,8 @@ validate_wal(pgBackup *backup,
384385
last_xid = backup->recovery_xid;
385386

386387
if ((TransactionIdIsValid(target_xid) && target_xid == last_xid)
387-
|| (target_time != 0 && backup->recovery_time >= target_time))
388+
|| (target_time != 0 && backup->recovery_time >= target_time)
389+
|| (XRecOffIsValid(target_lsn) && backup->stop_lsn >= target_lsn))
388390
all_wal = true;
389391

390392
startpoint = backup->stop_lsn;
@@ -470,6 +472,9 @@ validate_wal(pgBackup *backup,
470472
else if (target_time != 0)
471473
elog(ERROR, "not enough WAL records to time %s",
472474
target_timestamp);
475+
else if (XRecOffIsValid(target_lsn))
476+
elog(ERROR, "not enough WAL records to lsn %X/%X",
477+
(uint32) (target_lsn >> 32), (uint32) (target_lsn));
473478
}
474479

475480
/* clean */

src/pg_probackup.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ uint32 replica_timeout = 300; /* default is 300 seconds */
6161
/* restore options */
6262
static char *target_time;
6363
static char *target_xid;
64+
static char *target_lsn;
6465
static char *target_inclusive;
6566
static TimeLineID target_tli;
6667
static bool target_immediate;
@@ -150,6 +151,7 @@ static pgut_option options[] =
150151
{ 's', 26, "recovery-target-action", &target_action, SOURCE_CMDLINE },
151152
{ 'b', 'R', "restore-as-replica", &restore_as_replica, SOURCE_CMDLINE },
152153
{ 'b', 27, "no-validate", &restore_no_validate, SOURCE_CMDLINE },
154+
{ 's', 28, "lsn", &target_lsn, SOURCE_CMDLINE },
153155
/* delete options */
154156
{ 'b', 130, "wal", &delete_wal, SOURCE_CMDLINE },
155157
{ 'b', 131, "expired", &delete_expired, SOURCE_CMDLINE },
@@ -436,7 +438,7 @@ main(int argc, char *argv[])
436438
{
437439
/* parse all recovery target options into recovery_target_options structure */
438440
recovery_target_options = parseRecoveryTargetOptions(target_time, target_xid,
439-
target_inclusive, target_tli, target_immediate,
441+
target_inclusive, target_tli, target_lsn, target_immediate,
440442
target_name, target_action, restore_no_validate);
441443
}
442444

src/pg_probackup.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ typedef struct pgRecoveryTarget
258258
TransactionId recovery_target_xid;
259259
/* add one more field in order to avoid deparsing recovery_target_xid back */
260260
const char *target_xid_string;
261+
bool lsn_specified;
262+
XLogRecPtr recovery_target_lsn;
263+
/* add one more field in order to avoid deparsing recovery_target_lsn back */
264+
const char *target_lsn_string;
261265
TimeLineID recovery_target_tli;
262266
bool recovery_target_inclusive;
263267
bool inclusive_specified;
@@ -397,8 +401,9 @@ extern bool satisfy_recovery_target(const pgBackup *backup,
397401
extern parray * readTimeLineHistory_probackup(TimeLineID targetTLI);
398402
extern pgRecoveryTarget *parseRecoveryTargetOptions(
399403
const char *target_time, const char *target_xid,
400-
const char *target_inclusive, TimeLineID target_tli, bool target_immediate,
401-
const char *target_name, const char *target_action, bool restore_no_validate);
404+
const char *target_inclusive, TimeLineID target_tli, const char* target_lsn,
405+
bool target_immediate, const char *target_name,
406+
const char *target_action, bool restore_no_validate);
402407

403408
extern void opt_tablespace_map(pgut_option *opt, const char *arg);
404409

@@ -511,6 +516,7 @@ extern void validate_wal(pgBackup *backup,
511516
const char *archivedir,
512517
time_t target_time,
513518
TransactionId target_xid,
519+
XLogRecPtr target_lsn,
514520
TimeLineID tli);
515521
extern bool read_recovery_info(const char *archivedir, TimeLineID tli,
516522
XLogRecPtr start_lsn, XLogRecPtr stop_lsn,
@@ -533,6 +539,7 @@ extern long unsigned int base36dec(const char *text);
533539
extern uint64 get_system_identifier(char *pgdata);
534540
extern uint64 get_remote_system_identifier(PGconn *conn);
535541
extern pg_time_t timestamptz_to_time_t(TimestampTz t);
542+
extern int parse_server_version(char *server_version_str);
536543
extern void pgBackup_init(pgBackup *backup);
537544

538545
/* in status.c */

src/restore.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
276276
* because it's needed to form the name of xlog file.
277277
*/
278278
validate_wal(dest_backup, arclog_path, rt->recovery_target_time,
279-
rt->recovery_target_xid, base_full_backup->tli);
279+
rt->recovery_target_xid, rt->recovery_target_lsn,
280+
base_full_backup->tli);
280281

281282
/* Set every incremental backup between corrupted backup and nearest FULL backup as orphans */
282283
if (corrupted_backup)
@@ -335,6 +336,11 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
335336
for (i = base_full_backup_index; i >= dest_backup_index; i--)
336337
{
337338
pgBackup *backup = (pgBackup *) parray_get(backups, i);
339+
340+
if (rt->lsn_specified && parse_server_version(backup->server_version) < 100000)
341+
elog(ERROR, "Backup %s was created for version %s which doesn't support recovery_target_lsn",
342+
base36enc(dest_backup->start_time), dest_backup->server_version);
343+
338344
restore_backup(backup);
339345
}
340346

@@ -837,6 +843,9 @@ create_recovery_conf(time_t backup_id,
837843
if (rt->xid_specified)
838844
fprintf(fp, "recovery_target_xid = '%s'\n", rt->target_xid_string);
839845

846+
if (rt->recovery_target_lsn)
847+
fprintf(fp, "recovery_target_lsn = '%s'\n", rt->target_lsn_string);
848+
840849
if (rt->recovery_target_immediate)
841850
fprintf(fp, "recovery_target = 'immediate'\n");
842851

@@ -981,6 +990,9 @@ satisfy_recovery_target(const pgBackup *backup, const pgRecoveryTarget *rt)
981990
if (rt->time_specified)
982991
return backup->recovery_time <= rt->recovery_target_time;
983992

993+
if (rt->lsn_specified)
994+
return backup->stop_lsn <= rt->recovery_target_lsn;
995+
984996
return true;
985997
}
986998

@@ -1009,6 +1021,7 @@ parseRecoveryTargetOptions(const char *target_time,
10091021
const char *target_xid,
10101022
const char *target_inclusive,
10111023
TimeLineID target_tli,
1024+
const char *target_lsn,
10121025
bool target_immediate,
10131026
const char *target_name,
10141027
const char *target_action,
@@ -1017,6 +1030,7 @@ parseRecoveryTargetOptions(const char *target_time,
10171030
time_t dummy_time;
10181031
TransactionId dummy_xid;
10191032
bool dummy_bool;
1033+
XLogRecPtr dummy_lsn;
10201034
/*
10211035
* count the number of the mutually exclusive options which may specify
10221036
* recovery target. If final value > 1, throw an error.
@@ -1028,10 +1042,13 @@ parseRecoveryTargetOptions(const char *target_time,
10281042
rt->time_specified = false;
10291043
rt->xid_specified = false;
10301044
rt->inclusive_specified = false;
1045+
rt->lsn_specified = false;
10311046
rt->recovery_target_time = 0;
10321047
rt->recovery_target_xid = 0;
1048+
rt->recovery_target_lsn = InvalidXLogRecPtr;
10331049
rt->target_time_string = NULL;
10341050
rt->target_xid_string = NULL;
1051+
rt->target_lsn_string = NULL;
10351052
rt->recovery_target_inclusive = false;
10361053
rt->recovery_target_tli = 0;
10371054
rt->recovery_target_immediate = false;
@@ -1068,6 +1085,17 @@ parseRecoveryTargetOptions(const char *target_time,
10681085
elog(ERROR, "Invalid value of --xid option %s", target_xid);
10691086
}
10701087

1088+
if (target_lsn)
1089+
{
1090+
recovery_target_specified++;
1091+
rt->lsn_specified = true;
1092+
rt->target_lsn_string = target_lsn;
1093+
if (parse_lsn(target_lsn, &dummy_lsn))
1094+
rt->recovery_target_lsn = dummy_lsn;
1095+
else
1096+
elog(ERROR, "Invalid value of --lsn option %s", target_lsn);
1097+
}
1098+
10711099
if (target_inclusive)
10721100
{
10731101
rt->inclusive_specified = true;
@@ -1112,10 +1140,10 @@ parseRecoveryTargetOptions(const char *target_time,
11121140

11131141
/* More than one mutually exclusive option was defined. */
11141142
if (recovery_target_specified > 1)
1115-
elog(ERROR, "At most one of --immediate, --target-name, --time, or --xid can be used");
1143+
elog(ERROR, "At most one of --immediate, --target-name, --time, --xid, or --lsn can be used");
11161144

11171145
/* If none of the options is defined, '--inclusive' option is meaningless */
1118-
if (!(rt->xid_specified || rt->time_specified) && rt->recovery_target_inclusive)
1146+
if (!(rt->xid_specified || rt->time_specified || rt->lsn_specified) && rt->recovery_target_inclusive)
11191147
elog(ERROR, "--inclusive option applies when either --time or --xid is specified");
11201148

11211149
return rt;

src/util.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,35 @@ timestamptz_to_time_t(TimestampTz t)
235235
return result;
236236
}
237237

238+
/* Parse string representation of the server version */
239+
int
240+
parse_server_version(char *server_version_str)
241+
{
242+
int nfields;
243+
int result = 0;
244+
int major_version = 0;
245+
int minor_version = 0;
246+
247+
nfields = sscanf(server_version_str, "%d.%d", &major_version, &minor_version);
248+
if (nfields == 2)
249+
{
250+
/* Server version lower than 10 */
251+
if (major_version > 10)
252+
elog(ERROR, "Server version format doesn't match major version %d", major_version);
253+
result = major_version * 10000 + minor_version * 100;
254+
}
255+
else if (nfields == 1)
256+
{
257+
if (major_version < 10)
258+
elog(ERROR, "Server version format doesn't match major version %d", major_version);
259+
result = major_version * 10000;
260+
}
261+
else
262+
elog(ERROR, "Unknown server version format");
263+
264+
return result;
265+
}
266+
238267
const char *
239268
status2str(BackupStatus status)
240269
{

src/utils/pgut.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define MAX_TZDISP_HOUR 15 /* maximum allowed hour part */
3232
#define SECS_PER_MINUTE 60
3333
#define MINS_PER_HOUR 60
34+
#define MAXPG_LSNCOMPONENT 8
3435

3536
const char *PROGRAM_NAME = NULL;
3637

@@ -983,6 +984,32 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg)
983984
return true;
984985
}
985986

987+
bool
988+
parse_lsn(const char *value, XLogRecPtr *result)
989+
{
990+
uint32 xlogid;
991+
uint32 xrecoff;
992+
int len1;
993+
int len2;
994+
995+
len1 = strspn(value, "0123456789abcdefABCDEF");
996+
if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || value[len1] != '/')
997+
elog(ERROR, "invalid LSN \"%s\"", value);
998+
len2 = strspn(value + len1 + 1, "0123456789abcdefABCDEF");
999+
if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || value[len1 + 1 + len2] != '\0')
1000+
elog(ERROR, "invalid LSN \"%s\"", value);
1001+
1002+
if (sscanf(value, "%X/%X", &xlogid, &xrecoff) == 2)
1003+
*result = (XLogRecPtr) ((uint64) xlogid << 32) | xrecoff;
1004+
else
1005+
{
1006+
elog(ERROR, "invalid LSN \"%s\"", value);
1007+
return false;
1008+
}
1009+
1010+
return true;
1011+
}
1012+
9861013
static char *
9871014
longopts_to_optstring(const struct option opts[], const size_t len)
9881015
{

src/utils/pgut.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <assert.h>
1818
#include <sys/time.h>
1919

20+
#include "access/xlogdefs.h"
2021
#include "logger.h"
2122

2223
#if !defined(C_H) && !defined(__cplusplus)
@@ -207,6 +208,7 @@ extern bool parse_uint64(const char *value, uint64 *result, int flags);
207208
extern bool parse_time(const char *value, time_t *result, bool utc_default);
208209
extern bool parse_int(const char *value, int *result, int flags,
209210
const char **hintmsg);
211+
extern bool parse_lsn(const char *value, XLogRecPtr *result);
210212

211213
extern void convert_from_base_unit(int64 base_value, int base_unit,
212214
int64 *value, const char **unit);

src/validate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ do_validate_instance(void)
328328
base36enc(current_backup->start_time));
329329
/* Validate corresponding WAL files */
330330
validate_wal(current_backup, arclog_path, 0,
331-
0, base_full_backup->tli);
331+
0, 0, base_full_backup->tli);
332332
}
333333
/* Mark every incremental backup between corrupted backup and nearest FULL backup as orphans */
334334
if (current_backup->status == BACKUP_STATUS_CORRUPT)

tests/cfs_restore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
pg_probackup restore -B backupdir --instance instance_name
66
[-D datadir]
7-
[ -i backup_id | [{--time=time | --xid=xid } [--inclusive=boolean]]][--timeline=timeline] [-T OLDDIR=NEWDIR]
7+
[ -i backup_id | [{--time=time | --xid=xid | --lsn=lsn } [--inclusive=boolean]]][--timeline=timeline] [-T OLDDIR=NEWDIR]
88
[-j num_threads] [--progress] [-q] [-v]
99
1010
"""

0 commit comments

Comments
 (0)