mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-22 22:56:29 +00:00
"standby clone": perform clone operations
This commit is contained in:
105
dbutils.c
105
dbutils.c
@@ -1142,6 +1142,7 @@ get_node_record_by_name(PGconn *conn, const char *node_name, t_node_info *node_i
|
|||||||
int result;
|
int result;
|
||||||
|
|
||||||
initPQExpBuffer(&query);
|
initPQExpBuffer(&query);
|
||||||
|
|
||||||
appendPQExpBuffer(&query,
|
appendPQExpBuffer(&query,
|
||||||
"SELECT node_id, type, upstream_node_id, node_name, conninfo, slot_name, priority, active"
|
"SELECT node_id, type, upstream_node_id, node_name, conninfo, slot_name, priority, active"
|
||||||
" FROM repmgr.nodes "
|
" FROM repmgr.nodes "
|
||||||
@@ -1152,6 +1153,8 @@ get_node_record_by_name(PGconn *conn, const char *node_name, t_node_info *node_i
|
|||||||
|
|
||||||
result = _get_node_record(conn, query.data, node_info);
|
result = _get_node_record(conn, query.data, node_info);
|
||||||
|
|
||||||
|
termPQExpBuffer(&query);
|
||||||
|
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
log_verbose(LOG_DEBUG, "get_node_record(): no record found for node %s",
|
log_verbose(LOG_DEBUG, "get_node_record(): no record found for node %s",
|
||||||
@@ -1670,3 +1673,105 @@ get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record)
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ================ */
|
||||||
|
/* backup functions */
|
||||||
|
/* ================ */
|
||||||
|
|
||||||
|
// XXX is first_wal_segment actually used anywhere?
|
||||||
|
bool
|
||||||
|
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint, int server_version_num)
|
||||||
|
{
|
||||||
|
PQExpBufferData query;
|
||||||
|
PGresult *res;
|
||||||
|
|
||||||
|
initPQExpBuffer(&query);
|
||||||
|
|
||||||
|
if (server_version_num >= 100000)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&query,
|
||||||
|
"SELECT pg_catalog.pg_walfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
|
||||||
|
time(NULL),
|
||||||
|
fast_checkpoint ? "TRUE" : "FALSE");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&query,
|
||||||
|
"SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
|
||||||
|
time(NULL),
|
||||||
|
fast_checkpoint ? "TRUE" : "FALSE");
|
||||||
|
}
|
||||||
|
|
||||||
|
log_verbose(LOG_DEBUG, "start_backup():\n %s", query.data);
|
||||||
|
|
||||||
|
res = PQexec(conn, query.data);
|
||||||
|
|
||||||
|
termPQExpBuffer(&query);
|
||||||
|
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
{
|
||||||
|
log_error(_("unable to start backup:\n %s"), PQerrorMessage(conn));
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_wal_segment != NULL)
|
||||||
|
{
|
||||||
|
char *first_wal_seg_pq = PQgetvalue(res, 0, 0);
|
||||||
|
size_t buf_sz = strlen(first_wal_seg_pq);
|
||||||
|
|
||||||
|
first_wal_segment = pg_malloc0(buf_sz + 1);
|
||||||
|
snprintf(first_wal_segment, buf_sz + 1, "%s", first_wal_seg_pq);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
stop_backup(PGconn *conn, char *last_wal_segment, int server_version_num)
|
||||||
|
{
|
||||||
|
PQExpBufferData query;
|
||||||
|
PGresult *res;
|
||||||
|
|
||||||
|
initPQExpBuffer(&query);
|
||||||
|
|
||||||
|
if (server_version_num >= 100000)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&query,
|
||||||
|
"SELECT pg_catalog.pg_walfile_name(pg_catalog.pg_stop_backup())");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&query,
|
||||||
|
"SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_stop_backup())");
|
||||||
|
}
|
||||||
|
|
||||||
|
res = PQexec(conn, query.data);
|
||||||
|
termPQExpBuffer(&query);
|
||||||
|
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
{
|
||||||
|
log_error(_("unable to stop backup:\n %s"), PQerrorMessage(conn));
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_wal_segment != NULL)
|
||||||
|
{
|
||||||
|
char *last_wal_seg_pq = PQgetvalue(res, 0, 0);
|
||||||
|
size_t buf_sz = strlen(last_wal_seg_pq);
|
||||||
|
|
||||||
|
last_wal_segment = pg_malloc0(buf_sz + 1);
|
||||||
|
snprintf(last_wal_segment, buf_sz + 1, "%s", last_wal_seg_pq);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -184,5 +184,9 @@ bool create_replication_slot(PGconn *conn, char *slot_name, int server_version_
|
|||||||
|
|
||||||
int get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
|
int get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
|
||||||
|
|
||||||
|
/* backup functions */
|
||||||
|
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint, int server_version_num);
|
||||||
|
bool stop_backup(PGconn *conn, char *last_wal_segment, int server_version_num);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#define ERR_SWITCHOVER_FAIL 18
|
#define ERR_SWITCHOVER_FAIL 18
|
||||||
#define ERR_BARMAN 19
|
#define ERR_BARMAN 19
|
||||||
#define ERR_REGISTRATION_SYNC 20
|
#define ERR_REGISTRATION_SYNC 20
|
||||||
|
#define ERR_OUT_OF_MEMORY 21
|
||||||
|
|
||||||
#endif /* _ERRCODE_H_ */
|
#endif /* _ERRCODE_H_ */
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -109,6 +109,11 @@ extern bool local_command(const char *command, PQExpBufferData *outputbuf);
|
|||||||
extern bool check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error);
|
extern bool check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error);
|
||||||
extern standy_clone_mode get_standby_clone_mode(void);
|
extern standy_clone_mode get_standby_clone_mode(void);
|
||||||
|
|
||||||
|
extern int copy_remote_files(char *host, char *remote_user, char *remote_path,
|
||||||
|
char *local_path, bool is_directory, int server_version_num);
|
||||||
|
|
||||||
extern void print_error_list(ItemList *error_list, int log_level);
|
extern void print_error_list(ItemList *error_list, int log_level);
|
||||||
|
|
||||||
|
extern char * make_pg_path(char *file);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
125
repmgr-client.c
125
repmgr-client.c
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
#include "repmgr.h"
|
#include "repmgr.h"
|
||||||
#include "repmgr-client.h"
|
#include "repmgr-client.h"
|
||||||
#include "repmgr-client-global.h"
|
#include "repmgr-client-global.h"
|
||||||
@@ -24,6 +25,8 @@
|
|||||||
#include "repmgr-action-standby.h"
|
#include "repmgr-action-standby.h"
|
||||||
#include "repmgr-action-cluster.h"
|
#include "repmgr-action-cluster.h"
|
||||||
|
|
||||||
|
#include <storage/fd.h> /* for PG_TEMP_FILE_PREFIX */
|
||||||
|
|
||||||
|
|
||||||
/* globally available variables *
|
/* globally available variables *
|
||||||
* ============================ */
|
* ============================ */
|
||||||
@@ -40,6 +43,8 @@ char pg_bindir[MAXLEN] = "";
|
|||||||
char repmgr_slot_name[MAXLEN] = "";
|
char repmgr_slot_name[MAXLEN] = "";
|
||||||
char *repmgr_slot_name_ptr = NULL;
|
char *repmgr_slot_name_ptr = NULL;
|
||||||
|
|
||||||
|
char path_buf[MAXLEN] = "";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if --node-id/--node-name provided, place that node's record here
|
* if --node-id/--node-name provided, place that node's record here
|
||||||
* for later use
|
* for later use
|
||||||
@@ -1580,3 +1585,123 @@ get_standby_clone_mode(void)
|
|||||||
|
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
make_pg_path(char *file)
|
||||||
|
{
|
||||||
|
maxlen_snprintf(path_buf, "%s%s", pg_bindir, file);
|
||||||
|
|
||||||
|
return path_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
copy_remote_files(char *host, char *remote_user, char *remote_path,
|
||||||
|
char *local_path, bool is_directory, int server_version_num)
|
||||||
|
{
|
||||||
|
PQExpBufferData rsync_flags;
|
||||||
|
char script[MAXLEN];
|
||||||
|
char host_string[MAXLEN];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
initPQExpBuffer(&rsync_flags);
|
||||||
|
|
||||||
|
if (*config_file_options.rsync_options == '\0')
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
"--archive --checksum --compress --progress --rsh=ssh");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
config_file_options.rsync_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtime_options.force)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --delete --checksum");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remote_user[0])
|
||||||
|
{
|
||||||
|
maxlen_snprintf(host_string, "%s", host);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxlen_snprintf(host_string, "%s@%s", remote_user, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When copying the main PGDATA directory, certain files and contents
|
||||||
|
* of certain directories need to be excluded.
|
||||||
|
*
|
||||||
|
* See function 'sendDir()' in 'src/backend/replication/basebackup.c' -
|
||||||
|
* we're basically simulating what pg_basebackup does, but with rsync rather
|
||||||
|
* than the BASEBACKUP replication protocol command.
|
||||||
|
*
|
||||||
|
* *However* currently we'll always copy the contents of the 'pg_replslot'
|
||||||
|
* directory and delete later if appropriate.
|
||||||
|
*/
|
||||||
|
if (is_directory)
|
||||||
|
{
|
||||||
|
/* Files which we don't want */
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --exclude=postmaster.pid --exclude=postmaster.opts --exclude=global/pg_control");
|
||||||
|
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --exclude=recovery.conf --exclude=recovery.done");
|
||||||
|
|
||||||
|
if (server_version_num >= 90400)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Ideally we'd use PG_AUTOCONF_FILENAME from utils/guc.h, but
|
||||||
|
* that has too many dependencies for a mere client program.
|
||||||
|
*/
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --exclude=postgresql.auto.conf.tmp");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Temporary files which we don't want, if they exist */
|
||||||
|
appendPQExpBuffer(&rsync_flags, " --exclude=%s*",
|
||||||
|
PG_TEMP_FILE_PREFIX);
|
||||||
|
|
||||||
|
/* Directories which we don't want */
|
||||||
|
|
||||||
|
if (server_version_num >= 100000)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --exclude=pg_wal/*");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --exclude=pg_xlog/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPQExpBuffer(&rsync_flags, "%s",
|
||||||
|
" --exclude=pg_log/* --exclude=pg_stat_tmp/*");
|
||||||
|
|
||||||
|
maxlen_snprintf(script, "rsync %s %s:%s/* %s",
|
||||||
|
rsync_flags.data, host_string, remote_path, local_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxlen_snprintf(script, "rsync %s %s:%s %s",
|
||||||
|
rsync_flags.data, host_string, remote_path, local_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info(_("rsync command line: '%s'"), script);
|
||||||
|
|
||||||
|
r = system(script);
|
||||||
|
|
||||||
|
log_debug("copy_remote_files(): r = %i; WIFEXITED: %i; WEXITSTATUS: %i", r, WIFEXITED(r), WEXITSTATUS(r));
|
||||||
|
|
||||||
|
/* exit code 24 indicates vanished files, which isn't a problem for us */
|
||||||
|
if (WIFEXITED(r) && WEXITSTATUS(r) && WEXITSTATUS(r) != 24)
|
||||||
|
log_verbose(LOG_WARNING, "copy_remote_files(): rsync returned unexpected exit status %i", WEXITSTATUS(r));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user