Skip to content

Commit 308d00b

Browse files
author
Michael Paquier
committed
Obtain timeline ID with control file and not XLOG system function
The system function used up to now was pg_xlogfile_name_offset, which cannot be used on a node in recovery, and it was the only way present to fetch the timeline ID of a backup, either incremental or full. So instead scan the control file of server and fetch the timeline from that. This also removes the restriction on which a backup could not be taken on a standby node. The next step being to have the possibility to take backups from streams.
1 parent a31fdbd commit 308d00b

File tree

4 files changed

+161
-17
lines changed

4 files changed

+161
-17
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ SRCS = \
55
data.c \
66
delete.c \
77
dir.c \
8+
fetch.c \
89
init.c \
910
parray.c \
1011
pg_rman.c \

backup.c

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

20+
#include "catalog/pg_control.h"
2021
#include "libpq/pqsignal.h"
2122
#include "pgut/pgut-port.h"
2223

@@ -48,7 +49,7 @@ static void confirm_block_size(const char *name, int blcksz);
4849
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup);
4950
static void pg_stop_backup(pgBackup *backup);
5051
static void pg_switch_xlog(pgBackup *backup);
51-
static void get_lsn(PGresult *res, TimeLineID *timeline, XLogRecPtr *lsn);
52+
static void get_lsn(PGresult *res, XLogRecPtr *lsn);
5253
static void get_xid(PGresult *res, uint32 *xid);
5354
static bool execute_restartpoint(pgBackupOption bkupopt);
5455

@@ -60,6 +61,7 @@ static bool dirExists(const char *path);
6061
static void add_files(parray *files, const char *root, bool add_root, bool is_pgdata);
6162
static int strCompare(const void *str1, const void *str2);
6263
static void create_file_list(parray *files, const char *root, const char *prefix, bool is_append);
64+
static TimeLineID get_current_timeline(void);
6365

6466
/*
6567
* Take a backup of database.
@@ -108,6 +110,13 @@ do_backup_database(parray *backup_list, pgBackupOption bkupopt)
108110
current.total_data_bytes = 0;
109111
current.read_data_bytes = 0;
110112

113+
/*
114+
* Obtain current timeline by scanning control file, theh LSN
115+
* obtained at output of pg_start_backup or pg_stop_backup does
116+
* not contain this information.
117+
*/
118+
current.tli = get_current_timeline();
119+
111120
/* notify start of backup to PostgreSQL server */
112121
time2iso(label, lengthof(label), current.start_time);
113122
strncat(label, " with pg_rman", lengthof(label));
@@ -492,8 +501,8 @@ do_backup_arclog(parray *backup_list)
492501
pg_switch_xlog(&current);
493502

494503
/*
495-
* To take incremental backup, the file list of the last completed database
496-
* backup is needed.
504+
* To take incremental backup, the file list of the last completed
505+
* database backup is needed.
497506
*/
498507
prev_backup = catalog_get_last_arclog_backup(backup_list);
499508
if (verbose && prev_backup == NULL)
@@ -985,10 +994,10 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup)
985994

986995
/* 2nd argument is 'fast'*/
987996
params[1] = smooth ? "false" : "true";
988-
res = execute("SELECT * from pg_xlogfile_name_offset(pg_start_backup($1, $2))", 2, params);
997+
res = execute("SELECT pg_start_backup($1, $2)", 2, params);
989998

990999
if (backup != NULL)
991-
get_lsn(res, &backup->tli, &backup->start_lsn);
1000+
get_lsn(res, &backup->start_lsn);
9921001
PQclear(res);
9931002
disconnect();
9941003
}
@@ -998,22 +1007,42 @@ wait_for_archive(pgBackup *backup, const char *sql)
9981007
{
9991008
PGresult *res;
10001009
char ready_path[MAXPGPATH];
1010+
char file_name[MAXFNAMELEN];
10011011
int try_count;
1012+
XLogRecPtr lsn;
1013+
TimeLineID tli;
10021014

10031015
reconnect();
10041016
res = execute(sql, 0, NULL);
1017+
1018+
/* Get LSN from execution result */
1019+
get_lsn(res, &lsn);
1020+
1021+
/*
1022+
* Enforce TLI obtention if backup is not present as this code
1023+
* path can be taken as a callback at exit.
1024+
*/
1025+
if (backup != NULL)
1026+
tli = backup->tli;
1027+
else
1028+
tli = get_current_timeline();
1029+
1030+
/* Fill in fields if backup exists */
10051031
if (backup != NULL)
10061032
{
1007-
get_lsn(res, &backup->tli, &backup->stop_lsn);
1033+
backup->stop_lsn = lsn;
10081034
elog(LOG, _("%s(): tli=%X lsn=%X/%08X"),
10091035
__FUNCTION__, backup->tli,
10101036
(uint32) (backup->stop_lsn >> 32),
10111037
(uint32) backup->stop_lsn);
10121038
}
10131039

1014-
/* get filename from the result of pg_xlogfile_name_offset() */
1040+
/* As well as WAL file name */
1041+
XLogFileName(file_name, tli, lsn);
1042+
10151043
snprintf(ready_path, lengthof(ready_path),
1016-
"%s/pg_xlog/archive_status/%s.ready", pgdata, PQgetvalue(res, 0, 0));
1044+
"%s/pg_xlog/archive_status/%s.ready", pgdata,
1045+
file_name);
10171046
elog(LOG, "%s() wait for %s", __FUNCTION__, ready_path);
10181047

10191048
PQclear(res);
@@ -1049,32 +1078,41 @@ static void
10491078
pg_stop_backup(pgBackup *backup)
10501079
{
10511080
wait_for_archive(backup,
1052-
"SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup())");
1081+
"SELECT * FROM pg_stop_backup()");
10531082
}
10541083

10551084
/*
1056-
* Force switch to a new transaction log file and update backup->tli.
1085+
* Force switch to a new transaction log file
10571086
*/
10581087
static void
10591088
pg_switch_xlog(pgBackup *backup)
10601089
{
10611090
wait_for_archive(backup,
1062-
"SELECT * FROM pg_xlogfile_name_offset(pg_switch_xlog())");
1091+
"SELECT * FROM pg_switch_xlog())");
10631092
}
10641093

10651094
/*
1066-
* Get TimeLineID and LSN from result of pg_xlogfile_name_offset().
1095+
* Get LSN from result of pg_start_backup() or pg_stop_backup().
10671096
*/
10681097
static void
1069-
get_lsn(PGresult *res, TimeLineID *timeline, XLogRecPtr *lsn)
1098+
get_lsn(PGresult *res, XLogRecPtr *lsn)
10701099
{
1071-
if (res == NULL || PQntuples(res) != 1 || PQnfields(res) != 2)
1100+
uint32 xlogid;
1101+
uint32 xrecoff;
1102+
1103+
if (res == NULL || PQntuples(res) != 1 || PQnfields(res) != 1)
10721104
elog(ERROR_PG_COMMAND,
1073-
_("result of pg_xlogfile_name_offset() is invalid: %s"),
1105+
_("result of backup command is invalid: %s"),
10741106
PQerrorMessage(connection));
10751107

1076-
/* Extract timeline and LSN from result of pg_stop_backup() */
1077-
XLogFromFileName(PQgetvalue(res, 0, 0), timeline, lsn);
1108+
/*
1109+
* Extract timeline and LSN from results of pg_stop_backup()
1110+
* and friends.
1111+
*/
1112+
XLogDataFromLSN(PQgetvalue(res, 0, 0), &xlogid, &xrecoff);
1113+
1114+
/* Calculate LSN */
1115+
*lsn = (XLogRecPtr) ((uint64) xlogid << 32) | xrecoff;
10781116
}
10791117

10801118
/*
@@ -1598,3 +1636,27 @@ create_file_list(parray *files, const char *root, const char *prefix, bool is_ap
15981636
fclose(fp);
15991637
}
16001638
}
1639+
1640+
/*
1641+
* Scan control file of given cluster at obtain the current timeline
1642+
* since last checkpoint that occurred on it.
1643+
*/
1644+
static TimeLineID
1645+
get_current_timeline(void)
1646+
{
1647+
char *buffer;
1648+
size_t size;
1649+
ControlFileData control_file;
1650+
1651+
/* First fetch file... */
1652+
buffer = slurpFile(pgdata, "global/pg_control", &size);
1653+
1654+
/* .. Then interpret it */
1655+
if (size != PG_CONTROL_SIZE)
1656+
elog(ERROR_CORRUPTED, "unexpected control file size %d, expected %d\n",
1657+
(int) size, PG_CONTROL_SIZE);
1658+
memcpy(&control_file, buffer, sizeof(ControlFileData));
1659+
1660+
/* Finally return the timeline wanted */
1661+
return control_file.checkPointCopy.ThisTimeLineID;
1662+
}

fetch.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* fetch.c
4+
* Functions for fetching files from PostgreSQL data directory
5+
*
6+
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
7+
*
8+
*-------------------------------------------------------------------------
9+
*/
10+
11+
#include "postgres_fe.h"
12+
13+
#include "catalog/catalog.h"
14+
15+
#include <sys/types.h>
16+
#include <sys/stat.h>
17+
#include <dirent.h>
18+
#include <fcntl.h>
19+
#include <unistd.h>
20+
#include <string.h>
21+
22+
#include "pg_rman.h"
23+
24+
/*
25+
* Read a file into memory. The file to be read is <datadir>/<path>.
26+
* The file contents are returned in a malloc'd buffer, and *filesize
27+
* is set to the length of the file.
28+
*
29+
* The returned buffer is always zero-terminated; the size of the returned
30+
* buffer is actually *filesize + 1. That's handy when reading a text file.
31+
* This function can be used to read binary files as well, you can just
32+
* ignore the zero-terminator in that case.
33+
*
34+
*/
35+
char *
36+
slurpFile(const char *datadir, const char *path, size_t *filesize)
37+
{
38+
int fd;
39+
char *buffer;
40+
struct stat statbuf;
41+
char fullpath[MAXPGPATH];
42+
int len;
43+
snprintf(fullpath, sizeof(fullpath), "%s/%s", datadir, path);
44+
45+
if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) == -1)
46+
elog(ERROR_CORRUPTED, _("could not open file \"%s\" for reading: %s\n"),
47+
fullpath, strerror(errno));
48+
49+
if (fstat(fd, &statbuf) < 0)
50+
elog(ERROR_CORRUPTED, _("could not open file \"%s\" for reading: %s\n"),
51+
fullpath, strerror(errno));
52+
53+
len = statbuf.st_size;
54+
55+
buffer = pg_malloc(len + 1);
56+
57+
if (read(fd, buffer, len) != len)
58+
elog(ERROR_CORRUPTED, _("could not read file \"%s\": %s\n"),
59+
fullpath, strerror(errno));
60+
61+
close(fd);
62+
63+
/* Zero-terminate the buffer. */
64+
buffer[len] = '\0';
65+
66+
if (filesize)
67+
*filesize = len;
68+
return buffer;
69+
}

pg_rman.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ typedef enum CompressionMode
214214
#define JoinPathEnd(str, prefix) \
215215
((strlen(str) <= strlen(prefix)) ? "" : str + strlen(prefix) + 1)
216216

217+
/*
218+
* Return timeline, xlog ID and record offset from an LSN of the type
219+
* 0/B000188, usual result from pg_stop_backup() and friends.
220+
*/
221+
#define XLogDataFromLSN(data, xlogid, xrecoff) \
222+
sscanf(data, "%X/%X", xlogid, xrecoff)
223+
217224
/* path configuration */
218225
extern char *backup_path;
219226
extern char *pgdata;
@@ -253,6 +260,11 @@ extern int do_show(pgBackupRange *range, bool show_all);
253260
extern int do_delete(pgBackupRange *range, bool force);
254261
extern void pgBackupDelete(int keep_generations, int keep_days);
255262

263+
/* in fetch.c */
264+
extern char *slurpFile(const char *datadir,
265+
const char *path,
266+
size_t *filesize);
267+
256268
/* in validate.c */
257269
extern int do_validate(pgBackupRange *range);
258270
extern void pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database);

0 commit comments

Comments
 (0)