Compare commits

..

26 Commits

Author SHA1 Message Date
Ian Barwick
318f1dac40 Update HISTORY 2017-05-29 11:43:30 +09:00
Ian Barwick
bda4b0995c Patch Makefile from downstream
Per GitHub #282

Ref:
  https://anonscm.debian.org/cgit/pkg-postgresql/repmgr.git/tree/debian/patches/makefile-no-libs.patch
2017-05-22 22:26:27 +09:00
Ian Barwick
c14449f0a7 Fix build on PostgreSQL older than the current libpq
Make sure that the includedir_internal directory is used before the
includedir_server, otherwise the build may fail for PostgreSQL
version lower than the libpq version.

Backpatched from downstream:
  https://anonscm.debian.org/cgit/pkg-postgresql/repmgr.git/tree/debian/patches/makefile-libpq-internal.patch

Per GitHub #282
2017-05-22 22:26:21 +09:00
Ian Barwick
557e34b70c Add basic regression test from downstream
Per GitHub #282
Ref: https://anonscm.debian.org/cgit/pkg-postgresql/repmgr.git/tree/debian/patches/regress.patch
2017-05-22 22:26:15 +09:00
Ian Barwick
333083869b repmgrd: fix more PostgreSQL 10 WAL function renamings 2017-05-22 22:25:53 +09:00
Ian Barwick
57fae00844 repmgrd: support latest round of PostgreSQL 10 WAL function renamings 2017-05-22 11:02:22 +09:00
Ian Barwick
3de336f1c0 Update HISTORY 2017-05-22 08:44:54 +09:00
Ian Barwick
5493b57443 repmgr: parse --no-slot in pg_basebackup_options
From PostgreSQL 10 we'll need to know whether this is present when
performing sanity checks for available replication slots.

Add a sanity check for conflicting presence of -S/--slot while we're
at it so we can abort early.
2017-05-22 08:40:49 +09:00
Ian Barwick
e53f1bf844 repmgrd: support further renamed WAL function for PostgreSQL 10 2017-05-22 08:38:12 +09:00
Ian Barwick
90638811c8 repmgr: support further renamed WAL function for PostreSQL 10
pg_xlogfile_name() -> pg_walfile_name()
2017-05-22 08:37:44 +09:00
Ian Barwick
892e3b93d1 repmgr: support --wal-method (replacing --xlog-method) for pg_basebackup in PostgreSQL 10 2017-05-22 08:35:30 +09:00
Ian Barwick
6f15a7e52e repmgr: initial support for PostgreSQL 10
Handle directory name change from pg_xlog to pg_wal.

Note that some functions with "xlog" in their name may also change.
2017-05-22 08:26:17 +09:00
Ian Barwick
98998f73bf repmgrd: remove unnecessary inclusions
Per gripe in GitHub #303
2017-05-22 08:13:39 +09:00
Ian Barwick
34ac2d8141 Bump version
3.3.2
2017-05-15 08:46:21 +09:00
Ian Barwick
c820b61f28 Update HISTORY 2017-05-15 08:45:11 +09:00
Ian Barwick
9e620656c5 Minor log/comment fixes 2017-05-15 08:25:52 +09:00
Ian Barwick
2fa277cc53 repmgr: fix --replication-user option when using conninfo string
In "standby clone", if a conninfo string was provided, this was passed
as-is to pg_basebackup - rewrite conninfo string to include the
value passed with --replication-user, if provided.
2017-05-05 09:28:53 +09:00
Ian Barwick
6a4f5944a1 repmgr: add missing '-P' option
Addresses GitHub #293
2017-05-05 09:22:52 +09:00
Ian Barwick
c02a12a113 repmgrd: actually call repmgr_update_last_updated()
Function was created but never actually used, resulting in incorrect
values for "communication_time_lag" in the "repl_status" view.

This appears to have been an oversight in the original commit
( c3b58658ad ).

Addresses GitHub #290
2017-05-05 09:22:46 +09:00
Ian Barwick
01b3933922 repmgr: master register - remove superfluous transaction start
Also make quotation mark usage in logging output consistent.
2017-05-05 09:22:09 +09:00
Ian Barwick
39b3b32814 repmgr: improve detection of pg_rewind on remote server
If `pg_bindir` is not explicitly provided, the remote `ls` command
will be `ls pg_rewind`, which will very likely not find pg_rewind.
In this case execute `which pg_rewind` to confirm it's in the default
path.

Addresses GitHub #267.
2017-05-05 09:22:03 +09:00
Ian Barwick
846e0f73b2 repmgr: avoid spurious cluster name errors during 'standby switchover'
'standby restore-config' doesn't require a configuration file, but
pass it anyway.

Addresses GitHub #269
2017-05-05 09:21:56 +09:00
Ian Barwick
7467525c8d Add log_detail() method 2017-05-05 09:21:51 +09:00
Simon Riggs
b27a94ccbe Typo in failover docs, reported by VaoTsun 2017-05-05 09:21:47 +09:00
Simon Riggs
2e69d155da Typo in README.md reported by dhx 2017-05-05 09:21:43 +09:00
Ian Barwick
870a367d3b repmgr: prevent spurious error message when running 'standby switchover'
When 'repmgr standby follow' is run on a dormant server, with connection
parameters for the upstream node provided (which is done during the
switchover process to reintegrate the stopped former master into the
replication cluster), a spurious error message is generated about
a slot which cannot be deleted as it's active. During the switchover
process the current master's (former standby's) slot on the former master
is deleted at a later point so can be skipped here.

The error message is annoying but harmless and has no effect on the
switchover process.

Addresses GitHub #285.
2017-04-10 23:00:10 +09:00
15 changed files with 400 additions and 104 deletions

13
HISTORY
View File

@@ -1,4 +1,15 @@
3.3.1 2017-03- 3.3.2 2017-06-01
Add support for PostgreSQL 10 (Ian)
repmgr: ensure --replication-user option is honoured when passing database
connection parameters as a conninfo string (Ian)
repmgr: improve detection of pg_rewind on remote server (Ian)
repmgr: add DETAIL log output for additional clarification of error messages (Ian)
repmgr: suppress various spurious error messages in `standby follow` and
`standby switchover` (Ian)
repmgr: add missing `-P` option (Ian)
repmgrd: monitoring statistic reporting fixes (Ian)
3.3.1 2017-03-13
repmgrd: prevent invalid apply lag value being written to the repmgrd: prevent invalid apply lag value being written to the
monitoring table (Ian) monitoring table (Ian)
repmgrd: fix error in XLogRecPtr conversion when calculating repmgrd: fix error in XLogRecPtr conversion when calculating

View File

@@ -8,8 +8,9 @@ repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o dirmod.o compat.o repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o dirmod.o compat.o
DATA = repmgr.sql uninstall_repmgr.sql DATA = repmgr.sql uninstall_repmgr.sql
REGRESS = repmgr_funcs repmgr_test
PG_CPPFLAGS = -I$(libpq_srcdir) PG_CPPFLAGS = -I$(includedir_internal) -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport) PG_LIBS = $(libpq_pgport)
@@ -17,11 +18,11 @@ all: repmgrd repmgr
$(MAKE) -C sql $(MAKE) -C sql
repmgrd: $(repmgrd_OBJS) repmgrd: $(repmgrd_OBJS)
$(CC) -o repmgrd $(CFLAGS) $(repmgrd_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) $(CC) -o repmgrd $(CFLAGS) $(repmgrd_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX)
$(MAKE) -C sql $(MAKE) -C sql
repmgr: $(repmgr_OBJS) repmgr: $(repmgr_OBJS)
$(CC) -o repmgr $(CFLAGS) $(repmgr_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) $(CC) -o repmgr $(CFLAGS) $(repmgr_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX)
# Make all objects depend on all include files. This is a bit of a # Make all objects depend on all include files. This is a bit of a
# shotgun approach, but the codebase is small enough that a complete rebuild # shotgun approach, but the codebase is small enough that a complete rebuild

View File

@@ -504,7 +504,7 @@ place. To ensure this happens when using the default `pg_basebackup` method,
which will ensure all WAL files generated during the cloning process are which will ensure all WAL files generated during the cloning process are
streamed in parallel with the main backup. Note that this requires two streamed in parallel with the main backup. Note that this requires two
replication connections to be available (`repmgr` will verify sufficient replication connections to be available (`repmgr` will verify sufficient
connections are available before attempting to clonse). connections are available before attempting to clone).
To override this behaviour, in `repmgr.conf` set `pg_basebackup`'s To override this behaviour, in `repmgr.conf` set `pg_basebackup`'s
`--xlog-method` parameter to `fetch`: `--xlog-method` parameter to `fetch`:

View File

@@ -1132,15 +1132,25 @@ drop_replication_slot(PGconn *conn, char *slot_name)
bool bool
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint) start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint, int server_version_num)
{ {
char sqlquery[QUERY_STR_LEN]; char sqlquery[QUERY_STR_LEN];
PGresult *res; PGresult *res;
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery,
"SELECT pg_catalog.pg_walfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
time(NULL),
fast_checkpoint ? "TRUE" : "FALSE");
}
else
{
sqlquery_snprintf(sqlquery, sqlquery_snprintf(sqlquery,
"SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))", "SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
time(NULL), time(NULL),
fast_checkpoint ? "TRUE" : "FALSE"); fast_checkpoint ? "TRUE" : "FALSE");
}
log_verbose(LOG_DEBUG, "start_backup():\n%s\n", sqlquery); log_verbose(LOG_DEBUG, "start_backup():\n%s\n", sqlquery);
@@ -1168,12 +1178,19 @@ start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
bool bool
stop_backup(PGconn *conn, char *last_wal_segment) stop_backup(PGconn *conn, char *last_wal_segment, int server_version_num)
{ {
char sqlquery[QUERY_STR_LEN]; char sqlquery[QUERY_STR_LEN];
PGresult *res; PGresult *res;
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_walfile_name(pg_catalog.pg_stop_backup())");
}
else
{
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_stop_backup())"); sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_stop_backup())");
}
res = PQexec(conn, sqlquery); res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)

View File

@@ -122,8 +122,8 @@ char *get_repmgr_schema_quoted(PGconn *conn);
bool create_replication_slot(PGconn *conn, char *slot_name, int server_version_num, PQExpBufferData *error_msg); bool create_replication_slot(PGconn *conn, char *slot_name, int server_version_num, PQExpBufferData *error_msg);
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);
bool drop_replication_slot(PGconn *conn, char *slot_name); bool drop_replication_slot(PGconn *conn, char *slot_name);
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint); 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); bool stop_backup(PGconn *conn, char *last_wal_segment, int server_version_num);
bool set_config(PGconn *conn, const char *config_param, const char *config_value); bool set_config(PGconn *conn, const char *config_param, const char *config_value);
bool set_config_bool(PGconn *conn, const char *config_param, bool state); bool set_config_bool(PGconn *conn, const char *config_param, bool state);
bool witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster_name); bool witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster_name);

View File

@@ -53,7 +53,7 @@ will be carried out:
e.g. if a replication cluster is spread over multiple data centres, a split-brain e.g. if a replication cluster is spread over multiple data centres, a split-brain
situation does not occur if there is a network failure between datacentres. Note situation does not occur if there is a network failure between datacentres. Note
that if nodes are split evenly between data centres, a witness server can be that if nodes are split evenly between data centres, a witness server can be
used to establish the "majority" daat centre. used to establish the "majority" data centre.
* `repmgrd` polls all visible servers and waits for each node to return a valid LSN; * `repmgrd` polls all visible servers and waits for each node to return a valid LSN;
it updates the LSN previously stored for this node if it has increased since it updates the LSN previously stored for this node if it has increased since

18
expected/repmgr_funcs.out Normal file
View File

@@ -0,0 +1,18 @@
/*
* repmgr_function.sql
* Copyright (c) 2ndQuadrant, 2010-2017
*
*/
-- SET SEARCH_PATH TO 'repmgr';
CREATE FUNCTION repmgr_update_standby_location(text) RETURNS boolean
AS '$libdir/repmgr_funcs', 'repmgr_update_standby_location'
LANGUAGE C STRICT;
CREATE FUNCTION repmgr_get_last_standby_location() RETURNS text
AS '$libdir/repmgr_funcs', 'repmgr_get_last_standby_location'
LANGUAGE C STRICT;
CREATE FUNCTION repmgr_update_last_updated() RETURNS TIMESTAMP WITH TIME ZONE
AS '$libdir/repmgr_funcs', 'repmgr_update_last_updated'
LANGUAGE C STRICT;
CREATE FUNCTION repmgr_get_last_updated() RETURNS TIMESTAMP WITH TIME ZONE
AS '$libdir/repmgr_funcs', 'repmgr_get_last_updated'
LANGUAGE C STRICT;

24
expected/repmgr_test.out Normal file
View File

@@ -0,0 +1,24 @@
select * from repmgr_update_standby_location('');
repmgr_update_standby_location
--------------------------------
f
(1 row)
select * from repmgr_get_last_standby_location();
repmgr_get_last_standby_location
----------------------------------
(1 row)
select * from repmgr_update_last_updated();
repmgr_update_last_updated
----------------------------
(1 row)
select * from repmgr_get_last_updated();
repmgr_get_last_updated
-------------------------
(1 row)

16
log.c
View File

@@ -71,7 +71,7 @@ _stderr_log_with_level(const char *level_name, int level, const char *fmt, va_li
/* /*
* Store the requested level so that if there's a subsequent * Store the requested level so that if there's a subsequent
* log_hint(), we can suppress that if appropriate. * log_hint() or log_detail(), we can suppress that if appropriate.
*/ */
last_log_level = level; last_log_level = level;
@@ -113,6 +113,20 @@ log_hint(const char *fmt, ...)
} }
void
log_detail(const char *fmt, ...)
{
va_list ap;
if (terse_logging == false)
{
va_start(ap, fmt);
_stderr_log_with_level("DETAIL", last_log_level, fmt, ap);
va_end(ap);
}
}
void void
log_verbose(int level, const char *fmt, ...) log_verbose(int level, const char *fmt, ...)
{ {

2
log.h
View File

@@ -126,6 +126,8 @@ bool logger_shutdown(void);
void logger_set_verbose(void); void logger_set_verbose(void);
void logger_set_terse(void); void logger_set_terse(void);
void log_detail(const char *fmt, ...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
void log_hint(const char *fmt, ...) void log_hint(const char *fmt, ...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2))); __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
void log_verbose(int level, const char *fmt, ...) void log_verbose(int level, const char *fmt, ...)

217
repmgr.c
View File

@@ -96,7 +96,7 @@
static int test_ssh_connection(char *host, char *remote_user); static int test_ssh_connection(char *host, char *remote_user);
static int copy_remote_files(char *host, char *remote_user, char *remote_path, static int copy_remote_files(char *host, char *remote_user, char *remote_path,
char *local_path, bool is_directory, int server_version_num); char *local_path, bool is_directory, int server_version_num);
static int run_basebackup(const char *data_dir, int server_version); static int run_basebackup(const char *data_dir, int server_version_num);
static void check_parameters_for_action(const int action); static void check_parameters_for_action(const int action);
static bool create_schema(PGconn *conn); static bool create_schema(PGconn *conn);
static bool create_recovery_file(const char *data_dir, t_conninfo_param_list *recovery_conninfo); static bool create_recovery_file(const char *data_dir, t_conninfo_param_list *recovery_conninfo);
@@ -158,7 +158,8 @@ static void param_set(t_conninfo_param_list *param_list, const char *param, cons
static char *param_get(t_conninfo_param_list *param_list, const char *param); static char *param_get(t_conninfo_param_list *param_list, const char *param);
static bool parse_conninfo_string(const char *conninfo_str, t_conninfo_param_list *param_list, char *errmsg, bool ignore_application_name); static bool parse_conninfo_string(const char *conninfo_str, t_conninfo_param_list *param_list, char *errmsg, bool ignore_application_name);
static void conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list); static void conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list);
static void parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options); static char *param_list_to_string(t_conninfo_param_list *param_list);
static bool parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options, int server_version_num, ItemList *error_list);
static void config_file_list_init(t_configfile_list *list, int max_size); static void config_file_list_init(t_configfile_list *list, int max_size);
static void config_file_list_add(t_configfile_list *list, const char *file, const char *filename, bool in_data_dir); static void config_file_list_add(t_configfile_list *list, const char *file, const char *filename, bool in_data_dir);
@@ -220,14 +221,14 @@ main(int argc, char **argv)
{"rsync-only", no_argument, NULL, 'r'}, {"rsync-only", no_argument, NULL, 'r'},
{"fast-checkpoint", no_argument, NULL, 'c'}, {"fast-checkpoint", no_argument, NULL, 'c'},
{"log-level", required_argument, NULL, 'L'}, {"log-level", required_argument, NULL, 'L'},
{"terse", required_argument, NULL, 't'}, {"terse", no_argument, NULL, 't'},
{"mode", required_argument, NULL, 'm'}, {"mode", required_argument, NULL, 'm'},
{"pwprompt", no_argument, NULL, 'P'},
{"remote-config-file", required_argument, NULL, 'C'}, {"remote-config-file", required_argument, NULL, 'C'},
{"help", no_argument, NULL, OPT_HELP}, {"help", no_argument, NULL, OPT_HELP},
{"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG}, {"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG},
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY}, {"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY},
{"pg_rewind", optional_argument, NULL, OPT_PG_REWIND}, {"pg_rewind", optional_argument, NULL, OPT_PG_REWIND},
{"pwprompt", optional_argument, NULL, OPT_PWPROMPT},
{"csv", no_argument, NULL, OPT_CSV}, {"csv", no_argument, NULL, OPT_CSV},
{"node", required_argument, NULL, OPT_NODE}, {"node", required_argument, NULL, OPT_NODE},
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN}, {"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
@@ -338,7 +339,7 @@ main(int argc, char **argv)
strncpy(runtime_options.dbname, runtime_options.username, MAXLEN); strncpy(runtime_options.dbname, runtime_options.username, MAXLEN);
} }
while ((c = getopt_long(argc, argv, "?Vd:h:p:U:S:D:f:R:w:k:FWIvb:rcL:tm:C:l:", long_options, while ((c = getopt_long(argc, argv, "?Vd:h:p:U:S:D:f:R:w:k:FWIvb:rcL:tm:C:l:P", long_options,
&optindex)) != -1) &optindex)) != -1)
{ {
/* /*
@@ -464,6 +465,9 @@ main(int argc, char **argv)
case 'C': case 'C':
strncpy(runtime_options.remote_config_file, optarg, MAXLEN); strncpy(runtime_options.remote_config_file, optarg, MAXLEN);
break; break;
case 'P':
runtime_options.witness_pwprompt = true;
break;
case OPT_CHECK_UPSTREAM_CONFIG: case OPT_CHECK_UPSTREAM_CONFIG:
check_upstream_config = true; check_upstream_config = true;
break; break;
@@ -514,9 +518,6 @@ main(int argc, char **argv)
} }
runtime_options.pg_rewind_supplied = true; runtime_options.pg_rewind_supplied = true;
break; break;
case OPT_PWPROMPT:
runtime_options.witness_pwprompt = true;
break;
case OPT_CSV: case OPT_CSV:
runtime_options.csv_mode = true; runtime_options.csv_mode = true;
break; break;
@@ -877,7 +878,6 @@ main(int argc, char **argv)
if (runtime_options.terse) if (runtime_options.terse)
logger_set_terse(); logger_set_terse();
/* /*
* Node configuration information is not needed for all actions, with * Node configuration information is not needed for all actions, with
* STANDBY CLONE being the main exception. * STANDBY CLONE being the main exception.
@@ -1899,7 +1899,7 @@ do_master_register(void)
if (!schema_exists) if (!schema_exists)
{ {
log_info(_("master register: creating database objects inside the %s schema\n"), log_info(_("master register: creating database objects inside the '%s' schema\n"),
get_repmgr_schema()); get_repmgr_schema());
begin_transaction(conn); begin_transaction(conn);
@@ -1946,8 +1946,6 @@ do_master_register(void)
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
begin_transaction(conn);
/* /*
* Check whether there's an existing record for this node, and * Check whether there's an existing record for this node, and
* update it if --force set * update it if --force set
@@ -2011,7 +2009,7 @@ do_master_register(void)
PQfinish(conn); PQfinish(conn);
log_notice(_("master node correctly registered for cluster %s with id %d (conninfo: %s)\n"), log_notice(_("master node correctly registered for cluster '%s' with id %d (conninfo: %s)\n"),
options.cluster_name, options.node, options.conninfo); options.cluster_name, options.node, options.conninfo);
return; return;
} }
@@ -2152,7 +2150,7 @@ do_standby_register(void)
log_err(_("no record found for upstream node %i\n"), log_err(_("no record found for upstream node %i\n"),
options.upstream_node); options.upstream_node);
/* footgun alert - only do this if you know what you're doing */ /* footgun alert - only do this if you know what you're doing */
log_hint(_("use option -F/--force to create a dummy upstream record")); log_hint(_("use option -F/--force to create a dummy upstream record\n"));
PQfinish(master_conn); PQfinish(master_conn);
if (PQstatus(conn) == CONNECTION_OK) if (PQstatus(conn) == CONNECTION_OK)
PQfinish(conn); PQfinish(conn);
@@ -3645,7 +3643,18 @@ do_standby_clone(void)
initPQExpBuffer(&tablespace_map); initPQExpBuffer(&tablespace_map);
} }
if (start_backup(source_conn, first_wal_segment, runtime_options.fast_checkpoint) == false) /*
* From 9.1 default is to wait for a sync standby to ack, avoid that by
* turning off sync rep for this session
*/
if (set_config_bool(source_conn, "synchronous_commit", false) == false)
{
r = ERR_BAD_CONFIG;
retval = ERR_BAD_CONFIG;
goto stop_backup;
}
if (start_backup(source_conn, first_wal_segment, runtime_options.fast_checkpoint, server_version_num) == false)
{ {
r = ERR_BAD_BASEBACKUP; r = ERR_BAD_BASEBACKUP;
retval = ERR_BAD_BASEBACKUP; retval = ERR_BAD_BASEBACKUP;
@@ -3991,7 +4000,7 @@ stop_backup:
if (mode == rsync && pg_start_backup_executed) if (mode == rsync && pg_start_backup_executed)
{ {
log_notice(_("notifying master about backup completion...\n")); log_notice(_("notifying master about backup completion...\n"));
if (stop_backup(source_conn, last_wal_segment) == false) if (stop_backup(source_conn, last_wal_segment, server_version_num) == false)
{ {
r = ERR_BAD_BASEBACKUP; r = ERR_BAD_BASEBACKUP;
retval = ERR_BAD_BASEBACKUP; retval = ERR_BAD_BASEBACKUP;
@@ -4764,7 +4773,7 @@ do_standby_follow(void)
* (a former standby) exists on the former upstream, drop it. * (a former standby) exists on the former upstream, drop it.
*/ */
if (options.use_replication_slots && original_upstream_node_id != UNKNOWN_NODE_ID) if (options.use_replication_slots && runtime_options.host_param_provided == false && original_upstream_node_id != UNKNOWN_NODE_ID)
{ {
t_node_info upstream_node_record = T_NODE_INFO_INITIALIZER; t_node_info upstream_node_record = T_NODE_INFO_INITIALIZER;
int upstream_query_result; int upstream_query_result;
@@ -5068,7 +5077,11 @@ do_standby_switchover(void)
initPQExpBuffer(&remote_command_str); initPQExpBuffer(&remote_command_str);
if (strcmp(remote_pg_rewind, "pg_rewind") == 0)
appendPQExpBuffer(&remote_command_str, "which ");
else
appendPQExpBuffer(&remote_command_str, "ls "); appendPQExpBuffer(&remote_command_str, "ls ");
appendShellString(&remote_command_str, remote_pg_rewind); appendShellString(&remote_command_str, remote_pg_rewind);
appendPQExpBuffer(&remote_command_str, " >/dev/null 2>&1 && echo 1 || echo 0"); appendPQExpBuffer(&remote_command_str, " >/dev/null 2>&1 && echo 1 || echo 0");
@@ -5085,7 +5098,11 @@ do_standby_switchover(void)
if (*command_output.data == '0') if (*command_output.data == '0')
{ {
log_err(_("unable to find pg_rewind on the remote server\n")); log_err(_("unable to find pg_rewind on the remote server\n"));
log_err(_("expected location is: %s\n"), remote_pg_rewind); if (strcmp(remote_pg_rewind, "pg_rewind") == 0)
log_hint(_("set pg_bindir in repmgr.conf or provide with -b/--pg_bindir\n"));
else
log_detail(_("expected location is: %s\n"), remote_pg_rewind);
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
@@ -5439,10 +5456,20 @@ do_standby_switchover(void)
/* Restore any previously archived config files */ /* Restore any previously archived config files */
initPQExpBuffer(&remote_command_str); initPQExpBuffer(&remote_command_str);
/* --force */
appendPQExpBuffer(&remote_command_str, appendPQExpBuffer(&remote_command_str,
"%s standby restore-config -D ", "%s standby restore-config -D ",
make_pg_path("repmgr")); make_pg_path("repmgr"));
appendShellString(&remote_command_str, remote_data_directory); appendShellString(&remote_command_str, remote_data_directory);
/*
* append pass the configuration file to prevent spurious errors
* about missing cluster_name
*/
appendPQExpBuffer(&remote_command_str,
" -f ");
appendShellString(&remote_command_str, runtime_options.remote_config_file);
appendPQExpBuffer(&remote_command_str, appendPQExpBuffer(&remote_command_str,
" --config-archive-dir="); " --config-archive-dir=");
appendShellString(&remote_command_str, remote_archive_config_dir); appendShellString(&remote_command_str, remote_archive_config_dir);
@@ -5898,11 +5925,13 @@ do_standby_restore_config(void)
if (rmdir(runtime_options.config_archive_dir) != 0 && errno != EEXIST) if (rmdir(runtime_options.config_archive_dir) != 0 && errno != EEXIST)
{ {
log_err(_("Unable to delete %s\n"), runtime_options.config_archive_dir); log_warning(_("unable to delete %s\n"), runtime_options.config_archive_dir);
exit(ERR_BAD_CONFIG); log_detail(_("directory may need to be manually removed\n"));
}
else
{
log_verbose(LOG_NOTICE, "directory %s deleted\n", runtime_options.config_archive_dir);
} }
log_verbose(LOG_NOTICE, "Directory %s deleted\n", runtime_options.config_archive_dir);
return; return;
} }
@@ -6988,7 +7017,7 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
static int static int
run_basebackup(const char *data_dir, int server_version) run_basebackup(const char *data_dir, int server_version_num)
{ {
char script[MAXLEN]; char script[MAXLEN];
int r = 0; int r = 0;
@@ -7000,7 +7029,7 @@ run_basebackup(const char *data_dir, int server_version)
* Parse the pg_basebackup_options provided in repmgr.conf - we'll want * Parse the pg_basebackup_options provided in repmgr.conf - we'll want
* to check later whether certain options were set by the user * to check later whether certain options were set by the user
*/ */
parse_pg_basebackup_options(options.pg_basebackup_options, &backup_options); parse_pg_basebackup_options(options.pg_basebackup_options, &backup_options, server_version_num, NULL);
/* Create pg_basebackup command line options */ /* Create pg_basebackup command line options */
@@ -7016,7 +7045,22 @@ run_basebackup(const char *data_dir, int server_version)
*/ */
if (runtime_options.conninfo_provided == true) if (runtime_options.conninfo_provided == true)
{ {
appendPQExpBuffer(&params, " -d '%s'", runtime_options.dbname); t_conninfo_param_list conninfo;
char *conninfo_str;
initialize_conninfo_params(&conninfo, false);
/* string will already have been parsed */
(void) parse_conninfo_string(runtime_options.dbname, &conninfo, NULL, false);
if (*runtime_options.replication_user)
param_set(&conninfo, "user", runtime_options.replication_user);
conninfo_str = param_list_to_string(&conninfo);
appendPQExpBuffer(&params, " -d '%s'", conninfo_str);
pfree(conninfo_str);
} }
/* /*
@@ -7064,6 +7108,7 @@ run_basebackup(const char *data_dir, int server_version)
* From 9.6, if replication slots are in use, we'll have previously * From 9.6, if replication slots are in use, we'll have previously
* created a slot with reserved LSN, and will stream from that slot to avoid * created a slot with reserved LSN, and will stream from that slot to avoid
* WAL buildup on the master using the -S/--slot, which requires -X/--xlog-method=stream * WAL buildup on the master using the -S/--slot, which requires -X/--xlog-method=stream
* (from 10, -X/--wal-method=stream)
*/ */
if (!strlen(backup_options.xlog_method)) if (!strlen(backup_options.xlog_method))
{ {
@@ -7081,17 +7126,17 @@ run_basebackup(const char *data_dir, int server_version)
* *
* NOTE: * NOTE:
* It's possible to set 'pg_basebackup_options' with an invalid combination * It's possible to set 'pg_basebackup_options' with an invalid combination
* of values for --xlog-method and --slot - we're not checking that, just that * of values for --wal-method (--xlog-method) and --slot - we're not checking that, just that
* we're not overriding any user-supplied values * we're not overriding any user-supplied values
*/ */
if (server_version >= 90600 && options.use_replication_slots) if (server_version_num >= 90600 && options.use_replication_slots)
{ {
bool slot_add = true; bool slot_add = true;
/* /*
* Check whether 'pg_basebackup_options' in repmgr.conf has the --slot option set, * Check whether 'pg_basebackup_options' in repmgr.conf has the --slot option set,
* or if --xlog-method is set to a value other than "stream" (in which case we can't * or if --wal-method (--xlog-method) is set to a value other than "stream"
* use --slot). * (in which case we can't use --slot).
*/ */
if (strlen(backup_options.slot) || (strlen(backup_options.xlog_method) && strcmp(backup_options.xlog_method, "stream") != 0)) { if (strlen(backup_options.slot) || (strlen(backup_options.xlog_method) && strcmp(backup_options.xlog_method, "stream") != 0)) {
slot_add = false; slot_add = false;
@@ -7719,7 +7764,7 @@ create_schema(PGconn *conn)
* to perform additional cleanup * to perform additional cleanup
* *
* char *server_version_string * char *server_version_string
* passed to get_server_version(), which will place the human-readble * passed to get_server_version(), which will place the human-readable
* server version string there (e.g. "9.4.0") * server version string there (e.g. "9.4.0")
*/ */
static int static int
@@ -7807,6 +7852,8 @@ check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error)
bool config_ok = true; bool config_ok = true;
char *wal_error_message = NULL; char *wal_error_message = NULL;
t_basebackup_options backup_options = T_BASEBACKUP_OPTIONS_INITIALIZER; t_basebackup_options backup_options = T_BASEBACKUP_OPTIONS_INITIALIZER;
bool backup_options_ok = true;
ItemList backup_option_errors = { NULL, NULL };
bool xlog_stream = true; bool xlog_stream = true;
enum { enum {
@@ -7832,7 +7879,24 @@ check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error)
* this will influence some checks * this will influence some checks
*/ */
parse_pg_basebackup_options(options.pg_basebackup_options, &backup_options); backup_options_ok = parse_pg_basebackup_options(
options.pg_basebackup_options,
&backup_options, server_version_num,
&backup_option_errors);
if (backup_options_ok == false)
{
if (exit_on_error == true)
{
log_err(_("error(s) encountered parsing 'pg_basebackup_options'\n"));
print_error_list(&backup_option_errors, LOG_ERR);
log_hint(_("'pg_basebackup_options' is: '%s'\n"), options.pg_basebackup_options);
exit(ERR_BAD_CONFIG);
}
config_ok = false;
}
if (strlen(backup_options.xlog_method) && strcmp(backup_options.xlog_method, "stream") != 0) if (strlen(backup_options.xlog_method) && strcmp(backup_options.xlog_method, "stream") != 0)
xlog_stream = false; xlog_stream = false;
@@ -8682,8 +8746,43 @@ conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list)
} }
static void static char *
parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options) param_list_to_string(t_conninfo_param_list *param_list)
{
int c;
PQExpBufferData conninfo_buf;
char *conninfo_str;
int len;
initPQExpBuffer(&conninfo_buf);
for (c = 0; c < param_list->size && param_list->keywords[c] != NULL; c++)
{
if (param_list->values[c] != NULL && param_list->values[c][0] != '\0')
{
if (c > 0)
appendPQExpBufferChar(&conninfo_buf, ' ');
appendPQExpBuffer(&conninfo_buf,
"%s=%s",
param_list->keywords[c],
param_list->values[c]);
}
}
len = strlen(conninfo_buf.data) + 1;
conninfo_str = pg_malloc0(len);
strncpy(conninfo_str, conninfo_buf.data, len);
termPQExpBuffer(&conninfo_buf);
return conninfo_str;
}
static bool
parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options, int server_version_num, ItemList *error_list)
{ {
int options_len = strlen(pg_basebackup_options) + 1; int options_len = strlen(pg_basebackup_options) + 1;
char *options_string = pg_malloc(options_len); char *options_string = pg_malloc(options_len);
@@ -8703,17 +8802,38 @@ parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_opti
int optindex = 0; int optindex = 0;
struct option *long_options;
bool backup_options_ok = true;
/* We're only interested in these options */ /* We're only interested in these options */
static struct option long_options[] = static struct option long_options_9[] =
{ {
{"slot", required_argument, NULL, 'S'}, {"slot", required_argument, NULL, 'S'},
{"xlog-method", required_argument, NULL, 'X'}, {"xlog-method", required_argument, NULL, 'X'},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
/*
* From PostgreSQL 10, --xlog-method is renamed --wal-method
* and there's also --no-slot, which we'll want to consider.
*/
static struct option long_options_10[] =
{
{"slot", required_argument, NULL, 'S'},
{"wal-method", required_argument, NULL, 'X'},
{"no-slot", no_argument, NULL, 1},
{NULL, 0, NULL, 0}
};
/* Don't attempt to tokenise an empty string */ /* Don't attempt to tokenise an empty string */
if (!strlen(pg_basebackup_options)) if (!strlen(pg_basebackup_options))
return; return backup_options_ok;
if (server_version_num >= 100000)
long_options = long_options_10;
else
long_options = long_options_9;
/* Copy the string before operating on it with strtok() */ /* Copy the string before operating on it with strtok() */
strncpy(options_string, pg_basebackup_options, options_len); strncpy(options_string, pg_basebackup_options, options_len);
@@ -8760,6 +8880,9 @@ parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_opti
/* Reset getopt's optind variable */ /* Reset getopt's optind variable */
optind = 0; optind = 0;
/* Prevent getopt from emitting errors */
opterr = 0;
while ((c = getopt_long(argc_item, argv_array, "S:X:", long_options, while ((c = getopt_long(argc_item, argv_array, "S:X:", long_options,
&optindex)) != -1) &optindex)) != -1)
{ {
@@ -8771,10 +8894,32 @@ parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_opti
case 'X': case 'X':
strncpy(backup_options->xlog_method, optarg, MAXLEN); strncpy(backup_options->xlog_method, optarg, MAXLEN);
break; break;
case 1:
backup_options->no_slot = true;
break;
case '?':
if (server_version_num >= 100000 && optopt == 1)
{
if (error_list != NULL)
{
item_list_append(error_list, "invalid use of --no-slot");
}
backup_options_ok = false;
}
break;
} }
} }
return; if (backup_options->no_slot == true && backup_options->slot[0] != '\0')
{
if (error_list != NULL)
{
item_list_append(error_list, "--no-slot cannot be used with -S/--slot");
}
backup_options_ok = false;
}
return backup_options_ok;
} }
static void static void

View File

@@ -55,7 +55,6 @@
#define OPT_COPY_EXTERNAL_CONFIG_FILES 4 #define OPT_COPY_EXTERNAL_CONFIG_FILES 4
#define OPT_CONFIG_ARCHIVE_DIR 5 #define OPT_CONFIG_ARCHIVE_DIR 5
#define OPT_PG_REWIND 6 #define OPT_PG_REWIND 6
#define OPT_PWPROMPT 7
#define OPT_CSV 8 #define OPT_CSV 8
#define OPT_NODE 9 #define OPT_NODE 9
#define OPT_WITHOUT_BARMAN 10 #define OPT_WITHOUT_BARMAN 10
@@ -118,7 +117,7 @@ typedef struct
char recovery_min_apply_delay[MAXLEN]; char recovery_min_apply_delay[MAXLEN];
/* standby register paarameters */ /* standby register parameters */
bool wait_register_sync; bool wait_register_sync;
int wait_register_sync_seconds; int wait_register_sync_seconds;
@@ -193,9 +192,10 @@ typedef struct
{ {
char slot[MAXLEN]; char slot[MAXLEN];
char xlog_method[MAXLEN]; char xlog_method[MAXLEN];
bool no_slot; /* from PostgreSQL 10 */
} t_basebackup_options; } t_basebackup_options;
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "" } #define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", false }
typedef struct typedef struct
{ {

View File

@@ -30,18 +30,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "repmgr.h" #include "repmgr.h"
#include "config.h"
#include "log.h" #include "log.h"
#include "strutil.h"
#include "version.h" #include "version.h"
/* Required PostgreSQL headers */
#include "access/xlogdefs.h"
#include "pqexpbuffer.h"
/* Message strings passed in repmgrSharedState->location */ /* Message strings passed in repmgrSharedState->location */
#define PASSIVE_NODE "PASSIVE_NODE" #define PASSIVE_NODE "PASSIVE_NODE"
@@ -71,6 +63,7 @@ bool failover_done = false;
bool manual_mode_upstream_disconnected = false; bool manual_mode_upstream_disconnected = false;
char *pid_file = NULL; char *pid_file = NULL;
int server_version_num = 0;
static void help(void); static void help(void);
static void usage(void); static void usage(void);
@@ -145,8 +138,6 @@ main(int argc, char **argv)
FILE *fd; FILE *fd;
int server_version_num = 0;
set_progname(argv[0]); set_progname(argv[0]);
/* Disallow running as root to prevent directory ownership problems */ /* Disallow running as root to prevent directory ownership problems */
@@ -718,12 +709,31 @@ witness_monitor(void)
return; return;
} }
strcpy(monitor_witness_timestamp, PQgetvalue(res, 0, 0)); strncpy(monitor_witness_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
PQclear(res); PQclear(res);
/* /*
* Build the SQL to execute on master * Build the SQL to execute on master
*/ */
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor "
" (primary_node, standby_node, "
" last_monitor_time, last_apply_time, "
" last_wal_primary_location, last_wal_standby_location, "
" replication_lag, apply_lag )"
" VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, NULL, "
" pg_catalog.pg_current_wal_lsn(), NULL, "
" 0, 0) ",
get_repmgr_schema_quoted(my_local_conn),
master_options.node,
local_options.node,
monitor_witness_timestamp);
}
else
{
sqlquery_snprintf(sqlquery, sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor " "INSERT INTO %s.repl_monitor "
" (primary_node, standby_node, " " (primary_node, standby_node, "
@@ -738,6 +748,7 @@ witness_monitor(void)
master_options.node, master_options.node,
local_options.node, local_options.node,
monitor_witness_timestamp); monitor_witness_timestamp);
}
/* /*
* Execute the query asynchronously, but don't check for a result. We will * Execute the query asynchronously, but don't check for a result. We will
@@ -1125,6 +1136,27 @@ standby_monitor(void)
* If receive_location is less than replay location, we were streaming WAL but are * If receive_location is less than replay location, we were streaming WAL but are
* somehow disconnected and evidently in archive recovery * somehow disconnected and evidently in archive recovery
*/ */
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery,
" SELECT ts, "
" CASE WHEN (receive_location IS NULL OR receive_location < replay_location) "
" THEN replay_location "
" ELSE receive_location"
" END AS receive_location,"
" replay_location, "
" replay_timestamp, "
" COALESCE(receive_location, '0/0') >= replay_location AS receiving_streamed_wal "
" FROM (SELECT CURRENT_TIMESTAMP AS ts, "
" pg_catalog.pg_last_wal_receive_lsn() AS receive_location, "
" pg_catalog.pg_last_wal_replay_lsn() AS replay_location, "
" pg_catalog.pg_last_xact_replay_timestamp() AS replay_timestamp "
" ) q ");
}
else
{
sqlquery_snprintf(sqlquery, sqlquery_snprintf(sqlquery,
" SELECT ts, " " SELECT ts, "
" CASE WHEN (receive_location IS NULL OR receive_location < replay_location) " " CASE WHEN (receive_location IS NULL OR receive_location < replay_location) "
@@ -1139,7 +1171,7 @@ standby_monitor(void)
" pg_catalog.pg_last_xlog_replay_location() AS replay_location, " " pg_catalog.pg_last_xlog_replay_location() AS replay_location, "
" pg_catalog.pg_last_xact_replay_timestamp() AS replay_timestamp " " pg_catalog.pg_last_xact_replay_timestamp() AS replay_timestamp "
" ) q "); " ) q ");
}
res = PQexec(my_local_conn, sqlquery); res = PQexec(my_local_conn, sqlquery);
@@ -1173,6 +1205,10 @@ standby_monitor(void)
* TODO: investigate whether pg_current_xlog_insert_location() would be a better * TODO: investigate whether pg_current_xlog_insert_location() would be a better
* choice; see: https://github.com/2ndQuadrant/repmgr/issues/189 * choice; see: https://github.com/2ndQuadrant/repmgr/issues/189
*/ */
if (server_version_num >= 100000)
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_wal_lsn()");
else
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()"); sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()");
res = PQexec(master_conn, sqlquery); res = PQexec(master_conn, sqlquery);
@@ -1256,9 +1292,24 @@ standby_monitor(void)
log_verbose(LOG_DEBUG, "standby_monitor:() %s\n", sqlquery); log_verbose(LOG_DEBUG, "standby_monitor:() %s\n", sqlquery);
if (PQsendQuery(master_conn, sqlquery) == 0) if (PQsendQuery(master_conn, sqlquery) == 0)
log_warning(_("query could not be sent to master. %s\n"), {
log_warning(_("query could not be sent to master: %s\n"),
PQerrorMessage(master_conn)); PQerrorMessage(master_conn));
} }
else
{
sqlquery_snprintf(sqlquery,
"SELECT %s.repmgr_update_last_updated();",
get_repmgr_schema_quoted(my_local_conn));
res = PQexec(my_local_conn, sqlquery);
/* not critical if the above query fails*/
if (PQresultStatus(res) != PGRES_TUPLES_OK)
log_warning(_("unable to set last_updated: %s\n"), PQerrorMessage(my_local_conn));
PQclear(res);
}
}
/* /*
@@ -1449,7 +1500,11 @@ do_master_failover(void)
terminate(ERR_FAILOVER_FAIL); terminate(ERR_FAILOVER_FAIL);
} }
if (server_version_num >= 100000)
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_last_wal_receive_lsn()");
else
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()"); sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()");
res = PQexec(node_conn, sqlquery); res = PQexec(node_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
@@ -1481,7 +1536,12 @@ do_master_failover(void)
} }
/* last we get info about this node, and update shared memory */ /* last we get info about this node, and update shared memory */
if (server_version_num >= 100000)
sprintf(sqlquery, "SELECT pg_catalog.pg_last_wal_receive_lsn()");
else
sprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()"); sprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()");
res = PQexec(my_local_conn, sqlquery); res = PQexec(my_local_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {

4
sql/repmgr_test.sql Normal file
View File

@@ -0,0 +1,4 @@
select * from repmgr_update_standby_location('');
select * from repmgr_get_last_standby_location();
select * from repmgr_update_last_updated();
select * from repmgr_get_last_updated();

View File

@@ -1,6 +1,6 @@
#ifndef _VERSION_H_ #ifndef _VERSION_H_
#define _VERSION_H_ #define _VERSION_H_
#define REPMGR_VERSION "3.3.1" #define REPMGR_VERSION "3.3.2"
#endif #endif