Skip to content

Commit 9f038ee

Browse files
committed
Add support backup from replica.
1 parent 43b7455 commit 9f038ee

File tree

6 files changed

+129
-38
lines changed

6 files changed

+129
-38
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ server because of a reason or another. Its differential backup
1313
facility reduces the amount of data necessary to be taken between
1414
two consecutive backups.
1515

16+
Main features:
17+
* incremental backup from WAL and PTRACK
18+
* backup from replica
19+
* multithreaded backup and restore
20+
* autonomous backup without archive command (will need slot replication)
21+
1622
Download
1723
--------
1824

backup.c

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const char *progname = "pg_arman";
4141
parray *backup_files_list;
4242
static volatile uint32 total_copy_files_increment;
4343
static uint32 total_files_num;
44+
static PGconn *start_stop_connect = NULL;
4445

4546
typedef struct
4647
{
@@ -61,7 +62,7 @@ static void confirm_block_size(const char *name, int blcksz);
6162
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup);
6263
static void pg_stop_backup(pgBackup *backup);
6364
static bool pg_is_standby(void);
64-
static void get_lsn(PGresult *res, XLogRecPtr *lsn);
65+
static void get_lsn(PGconn *conn, PGresult *res, XLogRecPtr *lsn, bool stop_backup);
6566
static void get_xid(PGresult *res, uint32 *xid);
6667
static void pg_ptrack_clear(void);
6768
static char *pg_ptrack_get_and_clear(Oid tablespace_oid,
@@ -74,7 +75,7 @@ static void create_file_list(parray *files,
7475
const char *subdir,
7576
const char *prefix,
7677
bool is_append);
77-
static void wait_for_archive(pgBackup *backup, const char *sql);
78+
static void wait_for_archive(PGconn *conn, pgBackup *backup, const char *sql, bool stop_backup);
7879
static void make_pagemap_from_ptrack(parray *files);
7980
static void StreamLog(void *arg);
8081

@@ -112,7 +113,7 @@ do_backup_database(parray *backup_list, pgBackupOption bkupopt)
112113
pgBackup *prev_backup = NULL;
113114

114115
/* Block backup operations on a standby */
115-
if (pg_is_standby())
116+
if (pg_is_standby() && !from_replica)
116117
elog(ERROR, "Backup cannot run on a standby.");
117118

118119
elog(LOG, "database backup start");
@@ -164,18 +165,21 @@ do_backup_database(parray *backup_list, pgBackupOption bkupopt)
164165
strncat(label, " with pg_arman", lengthof(label));
165166
pg_start_backup(label, smooth_checkpoint, &current);
166167

167-
/* If backup_label does not exist in $PGDATA, stop taking backup */
168-
snprintf(path, lengthof(path), "%s/backup_label", pgdata);
169-
make_native_path(path);
170-
if (!fileExists(path))
171-
has_backup_label = false;
172-
173-
/* Leave if no backup file */
174-
if (!has_backup_label)
168+
if(!from_replica)
175169
{
176-
elog(LOG, "backup_label does not exist, stopping backup");
177-
pg_stop_backup(NULL);
178-
elog(ERROR, "backup_label does not exist in PGDATA.");
170+
/* If backup_label does not exist in $PGDATA, stop taking backup */
171+
snprintf(path, lengthof(path), "%s/backup_label", pgdata);
172+
make_native_path(path);
173+
if (!fileExists(path))
174+
has_backup_label = false;
175+
176+
/* Leave if no backup file */
177+
if (!has_backup_label)
178+
{
179+
elog(LOG, "backup_label does not exist, stopping backup");
180+
pg_stop_backup(NULL);
181+
elog(ERROR, "backup_label does not exist in PGDATA.");
182+
}
179183
}
180184

181185
/*
@@ -246,7 +250,7 @@ do_backup_database(parray *backup_list, pgBackupOption bkupopt)
246250
if (current.backup_mode == BACKUP_MODE_DIFF_PAGE)
247251
{
248252
/* Enforce archiving of last segment and wait for it to be here */
249-
wait_for_archive(&current, "SELECT * FROM pg_switch_xlog()");
253+
wait_for_archive(connection, &current, "SELECT * FROM pg_switch_xlog()", false);
250254

251255
/* Now build the page map */
252256
parray_qsort(backup_files_list, pgFileComparePathDesc);
@@ -563,6 +567,13 @@ check_server_version(void)
563567
(server_version / 100) % 100,
564568
server_version % 100, "9.5");
565569

570+
if (from_replica && server_version < 90600)
571+
elog(ERROR,
572+
"server version is %d.%d.%d, must be %s or higher for backup from replica.",
573+
server_version / 10000,
574+
(server_version / 100) % 100,
575+
server_version % 100, "9.6");
576+
566577
/* confirm block_size (BLCKSZ) and wal_block_size (XLOG_BLCKSZ) */
567578
confirm_block_size("block_size", BLCKSZ);
568579
confirm_block_size("wal_block_size", XLOG_BLCKSZ);
@@ -601,16 +612,27 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup)
601612

602613
params[0] = label;
603614

604-
reconnect();
615+
if (start_stop_connect == NULL)
616+
start_stop_connect = pgut_connect(ERROR);
605617

606618
/* 2nd argument is 'fast'*/
607619
params[1] = smooth ? "false" : "true";
608-
res = execute("SELECT pg_start_backup($1, $2)", 2, params);
620+
if (from_replica)
621+
res = pgut_execute(start_stop_connect,
622+
"SELECT pg_start_backup($1, $2, false)",
623+
2,
624+
params,
625+
ERROR);
626+
else
627+
res = pgut_execute(start_stop_connect,
628+
"SELECT pg_start_backup($1, $2)",
629+
2,
630+
params,
631+
ERROR);
609632

610633
if (backup != NULL)
611-
get_lsn(res, &backup->start_lsn);
634+
get_lsn(start_stop_connect, res, &backup->start_lsn, false);
612635
PQclear(res);
613-
disconnect();
614636
}
615637

616638
static void
@@ -670,7 +692,7 @@ pg_ptrack_get_and_clear(Oid tablespace_oid, Oid db_oid, Oid rel_oid, size_t *res
670692
}
671693

672694
static void
673-
wait_for_archive(pgBackup *backup, const char *sql)
695+
wait_for_archive(PGconn *conn, pgBackup *backup, const char *sql, bool stop_backup)
674696
{
675697
PGresult *res;
676698
char ready_path[MAXPGPATH];
@@ -680,17 +702,18 @@ wait_for_archive(pgBackup *backup, const char *sql)
680702
TimeLineID tli;
681703
XLogSegNo targetSegNo;
682704

683-
reconnect();
705+
if (conn == NULL)
706+
conn = pgut_connect(ERROR);
684707

685708
/* Remove annoying NOTICE messages generated by backend */
686-
res = execute("SET client_min_messages = warning;", 0, NULL);
709+
res = pgut_execute(conn, "SET client_min_messages = warning;", 0, NULL, ERROR);
687710
PQclear(res);
688711

689712
/* And execute the query wanted */
690-
res = execute(sql, 0, NULL);
713+
res = pgut_execute(conn, sql, 0, NULL, ERROR);
691714

692715
/* Get LSN from execution result */
693-
get_lsn(res, &lsn);
716+
get_lsn(conn, res, &lsn, stop_backup);
694717

695718
/*
696719
* Enforce TLI obtention if backup is not present as this code
@@ -720,13 +743,15 @@ wait_for_archive(pgBackup *backup, const char *sql)
720743

721744
PQclear(res);
722745

723-
res = execute(TXID_CURRENT_SQL, 0, NULL);
746+
if (from_replica)
747+
res = pgut_execute(conn, TXID_CURRENT_IF_SQL, 0, NULL, ERROR);
748+
else
749+
res = pgut_execute(conn, TXID_CURRENT_SQL, 0, NULL, ERROR);
724750
if (backup != NULL)
725751
{
726752
get_xid(res, &backup->recovery_xid);
727753
backup->recovery_time = time(NULL);
728754
}
729-
disconnect();
730755

731756
/* wait until switched WAL is archived */
732757
try_count = 0;
@@ -756,17 +781,22 @@ pg_stop_backup(pgBackup *backup)
756781
PGresult *res;
757782
TimeLineID tli;
758783

759-
reconnect();
784+
//reconnect();
785+
if (start_stop_connect == NULL)
786+
start_stop_connect = pgut_connect(ERROR);
760787

761788
/* Remove annoying NOTICE messages generated by backend */
762-
res = execute("SET client_min_messages = warning;", 0, NULL);
789+
res = pgut_execute(start_stop_connect, "SET client_min_messages = warning;", 0, NULL, ERROR);
763790
PQclear(res);
764791

765792
/* And execute the query wanted */
766-
res = execute("SELECT * FROM pg_stop_backup()", 0, NULL);
793+
if (from_replica)
794+
res = pgut_execute(start_stop_connect,"SELECT * FROM pg_stop_backup(false)", 0, NULL, ERROR);
795+
else
796+
res = pgut_execute(start_stop_connect,"SELECT * FROM pg_stop_backup()", 0, NULL, ERROR);
767797

768798
/* Get LSN from execution result */
769-
get_lsn(res, &stop_backup_lsn);
799+
get_lsn(start_stop_connect, res, &stop_backup_lsn, true);
770800
PQclear(res);
771801

772802
/*
@@ -786,18 +816,33 @@ pg_stop_backup(pgBackup *backup)
786816
(uint32) backup->stop_lsn);
787817
}
788818

789-
res = execute(TXID_CURRENT_SQL, 0, NULL);
819+
if (from_replica)
820+
res = pgut_execute(start_stop_connect, TXID_CURRENT_IF_SQL, 0, NULL, ERROR);
821+
else
822+
res = pgut_execute(start_stop_connect, TXID_CURRENT_SQL, 0, NULL, ERROR);
790823
if (backup != NULL)
791824
{
792825
get_xid(res, &backup->recovery_xid);
793826
backup->recovery_time = time(NULL);
794827
}
795828
PQclear(res);
796-
disconnect();
829+
//disconnect();
830+
pgut_disconnect(start_stop_connect);
797831
}
798832
else
799-
wait_for_archive(backup,
800-
"SELECT * FROM pg_stop_backup()");
833+
{
834+
if (from_replica)
835+
wait_for_archive(start_stop_connect,
836+
backup,
837+
"SELECT * FROM pg_stop_backup(false)",
838+
true);
839+
else
840+
wait_for_archive(start_stop_connect,
841+
backup,
842+
"SELECT * FROM pg_stop_backup()",
843+
true);
844+
pgut_disconnect(start_stop_connect);
845+
}
801846
}
802847

803848

@@ -818,15 +863,15 @@ pg_is_standby(void)
818863
* Get LSN from result of pg_start_backup() or pg_stop_backup().
819864
*/
820865
static void
821-
get_lsn(PGresult *res, XLogRecPtr *lsn)
866+
get_lsn(PGconn *conn, PGresult *res, XLogRecPtr *lsn, bool stop_backup)
822867
{
823868
uint32 xlogid;
824869
uint32 xrecoff;
825870

826-
if (res == NULL || PQntuples(res) != 1 || PQnfields(res) != 1)
871+
if (res == NULL || PQntuples(res) != 1 || (PQnfields(res) != 1 && PQnfields(res) != 3))
827872
elog(ERROR,
828873
"result of backup command is invalid: %s",
829-
PQerrorMessage(connection));
874+
PQerrorMessage(conn));
830875

831876
/*
832877
* Extract timeline and LSN from results of pg_stop_backup()
@@ -836,6 +881,36 @@ get_lsn(PGresult *res, XLogRecPtr *lsn)
836881
XLogDataFromLSN(PQgetvalue(res, 0, 0), &xlogid, &xrecoff);
837882
/* Calculate LSN */
838883
*lsn = (XLogRecPtr) ((uint64) xlogid << 32) | xrecoff;
884+
885+
if (stop_backup && from_replica && PQnfields(res) == 3)
886+
{
887+
char path[MAXPGPATH];
888+
char path_backup_label[MAXPGPATH];
889+
char path_tablespace_map[MAXPGPATH];
890+
FILE *fp;
891+
892+
pgBackupGetPath(&current, path, lengthof(path), DATABASE_DIR);
893+
snprintf(path_backup_label, lengthof(path_backup_label), "%s/backup_label", path);
894+
snprintf(path_tablespace_map, lengthof(path_tablespace_map), "%s/tablespace_map", path);
895+
896+
fp = fopen(path_backup_label, "w");
897+
if (fp == NULL)
898+
elog(ERROR, "can't open backup label file \"%s\": %s",
899+
path_backup_label, strerror(errno));
900+
901+
fwrite(PQgetvalue(res, 0, 1), 1, strlen(PQgetvalue(res, 0, 1)), fp);
902+
fclose(fp);
903+
if (strlen(PQgetvalue(res, 0, 2)) == 0)
904+
return;
905+
906+
fp = fopen(path_tablespace_map, "w");
907+
if (fp == NULL)
908+
elog(ERROR, "can't open tablespace map file \"%s\": %s",
909+
path_tablespace_map, strerror(errno));
910+
911+
fwrite(PQgetvalue(res, 0, 2), 1, strlen(PQgetvalue(res, 0, 2)), fp);
912+
fclose(fp);
913+
}
839914
}
840915

841916
/*

doc/pg_arman.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,18 @@ absolute paths; relative paths are not allowed.
241241
Only files exceeded one of those settings are deleted.
242242

243243
**-j**=NUMBER / **--threads**=NUMBER:
244-
Number of threads for backup.
244+
Number of threads for backup.
245245

246246
**--stream**:
247247
Enable stream replication for save WAL during backup process.
248248

249249
**--disable-ptrack-clear**:
250250
Disable clear ptrack files for postgres without ptrack patch.
251251

252+
**--from-replica**:
253+
Use non exclusive start backup for replica. Only for 9.6 and higher.
254+
255+
252256
### RESTORE OPTIONS
253257

254258
The parameters whose name start are started with --recovery refer to

expected/option.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Backup options:
2828
--keep-data-days=DAY keep enough data backup to recover to DAY days age
2929
--disable-ptrack-clear disable clear ptrack for postgres without ptrack
3030
--backup-pg-log start backup pg_log directory
31+
--from-replica use non exclusive start backup for replica
3132

3233
Restore options:
3334
--recovery-target-time time stamp up to which recovery will proceed

pg_arman.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static int keep_data_generations = KEEP_INFINITE;
3636
static int keep_data_days = KEEP_INFINITE;
3737
int num_threads = 1;
3838
bool stream_wal = false;
39+
bool from_replica = false;
3940
bool disable_ptrack_clear = false;
4041
static bool backup_logs = false;
4142
static bool backup_validate = false;
@@ -69,6 +70,7 @@ static pgut_option options[] =
6970
{ 'b', 10, "backup-pg-log", &backup_logs },
7071
{ 'f', 'b', "backup-mode", opt_backup_mode, SOURCE_ENV },
7172
{ 'b', 'C', "smooth-checkpoint", &smooth_checkpoint, SOURCE_ENV },
73+
{ 'b', 12, "from-replica", &from_replica },
7274
/* options with only long name (keep-xxx) */
7375
{ 'i', 1, "keep-data-generations", &keep_data_generations, SOURCE_ENV },
7476
{ 'i', 2, "keep-data-days", &keep_data_days, SOURCE_ENV },
@@ -250,6 +252,7 @@ pgut_help(bool details)
250252
printf(_(" --keep-data-days=DAY keep enough data backup to recover to DAY days age\n"));
251253
printf(_(" --disable-ptrack-clear disable clear ptrack for postgres without ptrack\n"));
252254
printf(_(" --backup-pg-log start backup pg_log directory\n"));
255+
printf(_(" --from-replica use non exclusive start backup for replica\n"));
253256
printf(_("\nRestore options:\n"));
254257
printf(_(" --recovery-target-time time stamp up to which recovery will proceed\n"));
255258
printf(_(" --recovery-target-xid transaction ID up to which recovery will proceed\n"));

pg_arman.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
/* Query to fetch current transaction ID */
2929
#define TXID_CURRENT_SQL "SELECT txid_current();"
30+
#define TXID_CURRENT_IF_SQL "SELECT txid_snapshot_xmax(txid_current_snapshot());"
3031

3132
/* Directory/File names */
3233
#define DATABASE_DIR "database"
@@ -211,6 +212,7 @@ extern parray *backup_files_list;
211212

212213
extern int num_threads;
213214
extern bool stream_wal;
215+
extern bool from_replica;
214216
extern bool disable_ptrack_clear;
215217
extern bool progress;
216218

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