Compare commits

..

4 Commits

Author SHA1 Message Date
Ian Barwick
374f19675d Bump version to 5.1.0 2020-04-10 16:55:58 +09:00
Ian Barwick
ce88f3ec43 doc: finalize release notes 2020-04-10 16:40:23 +09:00
Ian Barwick
5acdd69add doc: update release notes 2020-04-10 13:39:03 +09:00
Ian Barwick
71e23107e7 Add upgrade route for repmgr 3.x to repmgr 5.1
The removal of some extensions functions means it's not possible to
follow the conventional incremental upgrade path; instead we'll
create a script for direct upgrades to 5.1.
2020-04-09 16:08:35 +09:00
63 changed files with 2000 additions and 6061 deletions

40
HISTORY
View File

@@ -1,41 +1,3 @@
5.2.1 2020-12-07
config: fix parsing of "replication_type"; GitHub #672 (Ian)
standby clone: handle missing "postgresql.auto.conf" (Ian)
standby clone: add option --recovery-min-apply-delay (Ian)
standby clone: fix data directory permissions handling for
PostgreSQL 11 and later (Ian)
repmgrd: prevent termination when local node not available and
standby_disconnect_on_failover; GitHub #675 (Ian)
repmgrd: ensure reconnect_interval" is correctly handled;
GitHub #673 (Ian)
5.2.0 2020-10-22
general: add support for PostgreSQL 13 (Ian)
general: remove support for PostgreSQL 9.3 (Ian)
config: add support for file inclusion directives (Ian)
repmgr: "primary unregister --force" will unregister an active primary
with no registered standby nodes (Ian)
repmgr: add option --verify-backup to "standby clone" (Ian)
repmgr: "standby clone" honours --waldir option if set in
"pg_basebackup_options" (Ian)
repmgr: add option --db-connection to "node check" (Ian)
repmgr: report database connection error if the --optformat option was
provided to "node check" (Ian)
repmgr: improve "node rejoin" checks (Ian)
repmgr: enable "node rejoin" to join a target with a lower timeline (Ian)
repmgr: support pg_rewind's automatic crash recovery in Pg13 and later (Ian)
repmgr: improve output formatting for cluster matrix/crosscheck (Ian)
repmgr: improve database connection failure error checking on the
demotion candidate during "standby switchover" (Ian)
repmgr: make repmgr metadata tables dumpable (Ian)
repmgr: fix issue with tablespace mapping when cloning from Barman;
GitHub #650 (Ian)
repmgr: improve handling of pg_control read errors (Ian)
repmgrd: add additional optional parameters to "failover_validation command"
(spaskalev; GitHub #651)
repmgrd: ensure primary connection is reset if same as upstream;
GitHub #633 (Ian)
5.1.0 2020-04-13
repmgr: remove BDR 2.x support
repmgr: don't query upstream's data directory (Ian)
@@ -47,7 +9,7 @@
"standby clone" (Ian)
repmgr: add --upstream option to "node check"
repmgr: report error code on follow/rejoin failure due to non-available
0 replication slot (Ian)
replication slot (Ian)
repmgr: ensure "node rejoin" checks for available replication slots (Ian)
repmgr: improve "standby switchover" completion checks (Ian)
repmgr: add replication configuration file ownership check to

View File

@@ -12,7 +12,6 @@ EXTENSION = repmgr
DATA = \
repmgr--unpackaged--4.0.sql \
repmgr--unpackaged--5.1.sql \
repmgr--unpackaged--5.2.sql \
repmgr--4.0.sql \
repmgr--4.0--4.1.sql \
repmgr--4.1.sql \
@@ -25,9 +24,7 @@ DATA = \
repmgr--4.4--5.0.sql \
repmgr--5.0.sql \
repmgr--5.0--5.1.sql \
repmgr--5.1.sql \
repmgr--5.1--5.2.sql \
repmgr--5.2.sql
repmgr--5.1.sql
REGRESS = repmgr_extension
@@ -60,11 +57,8 @@ $(info Building against PostgreSQL $(MAJORVERSION))
REPMGR_CLIENT_OBJS = repmgr-client.o \
repmgr-action-primary.o repmgr-action-standby.o repmgr-action-witness.o \
repmgr-action-cluster.o repmgr-action-node.o repmgr-action-service.o repmgr-action-daemon.o \
configdata.o configfile.o configfile-scan.o log.o strutil.o controldata.o dirutil.o compat.o \
dbutils.o sysutils.o
REPMGRD_OBJS = repmgrd.o repmgrd-physical.o configdata.o configfile.o configfile-scan.o log.o \
dbutils.o strutil.o controldata.o compat.o sysutils.o
configfile.o configfile-scan.o log.o strutil.o controldata.o dirutil.o compat.o dbutils.o sysutils.o
REPMGRD_OBJS = repmgrd.o repmgrd-physical.o configfile.o configfile-scan.o log.o dbutils.o strutil.o controldata.o compat.o sysutils.o
DATE=$(shell date "+%Y-%m-%d")
repmgr_version.h: repmgr_version.h.in
@@ -76,10 +70,10 @@ configfile-scan.c: configfile-scan.l
$(REPMGR_CLIENT_OBJS): repmgr-client.h repmgr_version.h
repmgr: $(REPMGR_CLIENT_OBJS)
$(CC) $(CFLAGS) $(REPMGR_CLIENT_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) -o $@$(X)
$(CC) $(CFLAGS) $(REPMGR_CLIENT_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
repmgrd: $(REPMGRD_OBJS)
$(CC) $(CFLAGS) $(REPMGRD_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) -o $@$(X)
$(CC) $(CFLAGS) $(REPMGRD_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
$(REPMGR_CLIENT_OBJS): $(HEADERS)
$(REPMGRD_OBJS): $(HEADERS)

View File

@@ -7,8 +7,8 @@ replication capabilities with utilities to set up standby servers, monitor
replication, and perform administrative tasks such as failover or switchover
operations.
The most recent `repmgr` version (5.2.0) supports all PostgreSQL versions from
9.5 to 13. PostgreSQL 9.4 is also supported, with some restrictions.
PostgreSQL 12, 11, 10, 9.6 and 9.5 are fully supported.
PostgreSQL 9.4 and 9.3 are supported, with some restrictions.
`repmgr` is distributed under the GNU GPL 3 and maintained by 2ndQuadrant.
@@ -24,8 +24,7 @@ The old `README` file for `repmgr` 3.x is available here:
> https://github.com/2ndQuadrant/repmgr/blob/REL3_3_STABLE/README.md
Note that the `repmgr` 3.x series is no longer supported and contains known bugs;
please upgrade to the [current repmgr version](https://repmgr.org/docs/current/appendix-release-notes.html)
as soon as possible.
please upgrade to the current `repmgr` version as soon as possible.
Versions
--------
@@ -71,6 +70,8 @@ Please report bugs and other issues to:
* https://github.com/2ndQuadrant/repmgr
See
Further information is available at https://repmgr.org/
We'd love to hear from you about how you use repmgr. Case studies and
@@ -99,3 +100,7 @@ Further reading
* [repmgr documentation](https://repmgr.org/docs/current/index.html)
* [How to Automate PostgreSQL 12 Replication and Failover with repmgr - Part 1](https://www.2ndquadrant.com/en/blog/how-to-automate-postgresql-12-replication-and-failover-with-repmgr-part-1/)
* [How to Automate PostgreSQL 12 Replication and Failover with repmgr - Part 2](https://www.2ndquadrant.com/en/blog/how-to-automate-postgresql-12-replication-and-failover-with-repmgr-part-2/)
* https://blog.2ndquadrant.com/repmgr-3-2-is-here-barman-support-brand-new-high-availability-features/
* https://blog.2ndquadrant.com/improvements-in-repmgr-3-1-4/
* https://blog.2ndquadrant.com/managing-useful-clusters-repmgr/
* https://blog.2ndquadrant.com/easier_postgresql_90_clusters/

View File

@@ -1,936 +0,0 @@
/*
* configdata.c - contains structs with parsed configuration data
*
* Copyright (c) 2ndQuadrant, 2010-2020
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "repmgr.h"
#include "configfile.h"
/*
* Parsed configuration settings are stored here
*/
t_configuration_options config_file_options;
/*
* Configuration settings are defined here
*/
struct ConfigFileSetting config_file_settings[] =
{
/* ================
* node information
* ================
*/
/* node_id */
{
"node_id",
CONFIG_INT,
{ .intptr = &config_file_options.node_id },
{ .intdefault = UNKNOWN_NODE_ID },
{ .intminval = MIN_NODE_ID },
{},
{}
},
/* node_name */
{
"node_name",
CONFIG_STRING,
{ .strptr = config_file_options.node_name },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.node_name) },
{}
},
/* conninfo */
{
"conninfo",
CONFIG_STRING,
{ .strptr = config_file_options.conninfo },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.conninfo) },
{}
},
/* replication_user */
{
"replication_user",
CONFIG_STRING,
{ .strptr = config_file_options.replication_user },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.replication_user) },
{}
},
/* data_directory */
{
"data_directory",
CONFIG_STRING,
{ .strptr = config_file_options.data_directory },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.data_directory) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* config_directory */
{
"config_directory",
CONFIG_STRING,
{ .strptr = config_file_options.config_directory },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.config_directory) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* pg_bindir */
{
"pg_bindir",
CONFIG_STRING,
{ .strptr = config_file_options.pg_bindir },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.pg_bindir) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* repmgr_bindir */
{
"repmgr_bindir",
CONFIG_STRING,
{ .strptr = config_file_options.repmgr_bindir },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.repmgr_bindir) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* replication_type */
{
"replication_type",
CONFIG_REPLICATION_TYPE,
{ .replicationtypeptr = &config_file_options.replication_type },
{ .replicationtypedefault = DEFAULT_REPLICATION_TYPE },
{},
{},
{}
},
/* ================
* logging settings
* ================
*/
/*
* log_level
* NOTE: the default for "log_level" is set in log.c and does not need
* to be initialised here
*/
{
"log_level",
CONFIG_STRING,
{ .strptr = config_file_options.log_level },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.log_level) },
{}
},
/* log_facility */
{
"log_facility",
CONFIG_STRING,
{ .strptr = config_file_options.log_facility },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.log_facility) },
{}
},
/* log_file */
{
"log_file",
CONFIG_STRING,
{ .strptr = config_file_options.log_file },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.log_file) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* log_status_interval */
{
"log_status_interval",
CONFIG_INT,
{ .intptr = &config_file_options.log_status_interval },
{ .intdefault = DEFAULT_LOG_STATUS_INTERVAL, },
{ .intminval = 0 },
{},
{}
},
/* ======================
* standby clone settings
* ======================
*/
/* use_replication_slots */
{
"use_replication_slots",
CONFIG_BOOL,
{ .boolptr = &config_file_options.use_replication_slots },
{ .booldefault = DEFAULT_USE_REPLICATION_SLOTS },
{},
{},
{}
},
/* pg_basebackup_options */
{
"pg_basebackup_options",
CONFIG_STRING,
{ .strptr = config_file_options.pg_basebackup_options },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.pg_basebackup_options) },
{}
},
/* restore_command */
{
"restore_command",
CONFIG_STRING,
{ .strptr = config_file_options.restore_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.restore_command) },
{}
},
/* tablespace_mapping */
{
"tablespace_mapping",
CONFIG_TABLESPACE_MAPPING,
{ .tablespacemappingptr = &config_file_options.tablespace_mapping },
{},
{},
{},
{}
},
/* recovery_min_apply_delay */
{
"recovery_min_apply_delay",
CONFIG_STRING,
{ .strptr = config_file_options.recovery_min_apply_delay },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.recovery_min_apply_delay) },
{
.process_func = &parse_time_unit_parameter,
.providedptr = &config_file_options.recovery_min_apply_delay_provided
}
},
/* archive_cleanup_command */
{
"archive_cleanup_command",
CONFIG_STRING,
{ .strptr = config_file_options.archive_cleanup_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.archive_cleanup_command) },
{}
},
/* use_primary_conninfo_password */
{
"use_primary_conninfo_password",
CONFIG_BOOL,
{ .boolptr = &config_file_options.use_primary_conninfo_password },
{ .booldefault = DEFAULT_USE_PRIMARY_CONNINFO_PASSWORD },
{},
{},
{}
},
/* passfile */
{
"passfile",
CONFIG_STRING,
{ .strptr = config_file_options.passfile },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.passfile) },
{}
},
/* ======================
* standby clone settings
* ======================
*/
/* promote_check_timeout */
{
"promote_check_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.promote_check_timeout },
{ .intdefault = DEFAULT_PROMOTE_CHECK_TIMEOUT },
{ .intminval = 1 },
{},
{}
},
/* promote_check_interval */
{
"promote_check_interval",
CONFIG_INT,
{ .intptr = &config_file_options.promote_check_interval },
{ .intdefault = DEFAULT_PROMOTE_CHECK_INTERVAL },
{ .intminval = 1 },
{},
{}
},
/* =======================
* standby follow settings
* =======================
*/
/* primary_follow_timeout */
{
"primary_follow_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.primary_follow_timeout },
{ .intdefault = DEFAULT_PRIMARY_FOLLOW_TIMEOUT, },
{ .intminval = 1 },
{},
{}
},
/* standby_follow_timeout */
{
"standby_follow_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.standby_follow_timeout },
{ .intdefault = DEFAULT_STANDBY_FOLLOW_TIMEOUT },
{ .intminval = 1 },
{},
{}
},
/* standby_follow_restart */
{
"standby_follow_restart",
CONFIG_BOOL,
{ .boolptr = &config_file_options.standby_follow_restart },
{ .booldefault = DEFAULT_STANDBY_FOLLOW_RESTART },
{},
{},
{}
},
/* ===========================
* standby switchover settings
* ===========================
*/
/* shutdown_check_timeout */
{
"shutdown_check_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.shutdown_check_timeout },
{ .intdefault = DEFAULT_SHUTDOWN_CHECK_TIMEOUT },
{ .intminval = 1 },
{},
{}
},
/* standby_reconnect_timeout */
{
"standby_reconnect_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.standby_reconnect_timeout },
{ .intdefault = DEFAULT_STANDBY_RECONNECT_TIMEOUT },
{ .intminval = 1 },
{},
{}
},
/* wal_receive_check_timeout */
{
"wal_receive_check_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.wal_receive_check_timeout },
{ .intdefault = DEFAULT_WAL_RECEIVE_CHECK_TIMEOUT },
{ .intminval = 1 },
{},
{}
},
/* ====================
* node rejoin settings
* ====================
*/
/* node_rejoin_timeout */
{
"node_rejoin_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.node_rejoin_timeout },
{ .intdefault = DEFAULT_NODE_REJOIN_TIMEOUT },
{ .intminval = 1 },
{},
{}
},
/* ===================
* node check settings
* ===================
*/
/* archive_ready_warning */
{
"archive_ready_warning",
CONFIG_INT,
{ .intptr = &config_file_options.archive_ready_warning },
{ .intdefault = DEFAULT_ARCHIVE_READY_WARNING },
{ .intminval = 1 },
{},
{}
},
/* archive_ready_critical */
{
"archive_ready_critical",
CONFIG_INT,
{ .intptr = &config_file_options.archive_ready_critical },
{ .intdefault = DEFAULT_ARCHIVE_READY_CRITICAL },
{ .intminval = 1 },
{},
{}
},
/* replication_lag_warning */
{
"replication_lag_warning",
CONFIG_INT,
{ .intptr = &config_file_options.replication_lag_warning },
{ .intdefault = DEFAULT_REPLICATION_LAG_WARNING },
{ .intminval = 1 },
{},
{}
},
/* replication_lag_critical */
{
"replication_lag_critical",
CONFIG_INT,
{ .intptr = &config_file_options.replication_lag_critical },
{ .intdefault = DEFAULT_REPLICATION_LAG_CRITICAL },
{ .intminval = 1 },
{},
{}
},
/* ================
* witness settings
* ================
*/
/* witness_sync_interval */
{
"witness_sync_interval",
CONFIG_INT,
{ .intptr = &config_file_options.witness_sync_interval },
{ .intdefault = DEFAULT_WITNESS_SYNC_INTERVAL },
{ .intminval = 1 },
{},
{}
},
/* ================
* repmgrd settings
* ================
*/
/* failover */
{
"failover",
CONFIG_FAILOVER_MODE,
{ .failovermodeptr = &config_file_options.failover },
{ .failovermodedefault = FAILOVER_MANUAL },
{},
{},
{}
},
/* location */
{
"location",
CONFIG_STRING,
{ .strptr = config_file_options.location },
{ .strdefault = DEFAULT_LOCATION },
{},
{ .strmaxlen = sizeof(config_file_options.location) },
{}
},
/* priority */
{
"priority",
CONFIG_INT,
{ .intptr = &config_file_options.priority },
{ .intdefault = DEFAULT_PRIORITY, },
{ .intminval = 0 },
{},
{}
},
/* promote_command */
{
"promote_command",
CONFIG_STRING,
{ .strptr = config_file_options.promote_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.promote_command) },
{}
},
/* follow_command */
{
"follow_command",
CONFIG_STRING,
{ .strptr = config_file_options.follow_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.follow_command) },
{}
},
/* monitor_interval_secs */
{
"monitor_interval_secs",
CONFIG_INT,
{ .intptr = &config_file_options.monitor_interval_secs },
{ .intdefault = DEFAULT_MONITORING_INTERVAL },
{ .intminval = 1 },
{},
{}
},
/* reconnect_attempts */
{
"reconnect_attempts",
CONFIG_INT,
{ .intptr = &config_file_options.reconnect_attempts },
{ .intdefault = DEFAULT_RECONNECTION_ATTEMPTS },
{ .intminval = 0 },
{},
{}
},
/* reconnect_interval */
{
"reconnect_interval",
CONFIG_INT,
{ .intptr = &config_file_options.reconnect_interval },
{ .intdefault = DEFAULT_RECONNECTION_INTERVAL },
{ .intminval = 0 },
{},
{}
},
/* monitoring_history */
{
"monitoring_history",
CONFIG_BOOL,
{ .boolptr = &config_file_options.monitoring_history },
{ .booldefault = DEFAULT_MONITORING_HISTORY },
{},
{},
{}
},
/* degraded_monitoring_timeout */
{
"degraded_monitoring_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.degraded_monitoring_timeout },
{ .intdefault = DEFAULT_DEGRADED_MONITORING_TIMEOUT },
{ .intminval = -1 },
{},
{}
},
/* async_query_timeout */
{
"async_query_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.async_query_timeout },
{ .intdefault = DEFAULT_ASYNC_QUERY_TIMEOUT },
{ .intminval = 0 },
{},
{}
},
/* primary_notification_timeout */
{
"primary_notification_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.primary_notification_timeout },
{ .intdefault = DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT },
{ .intminval = 0 },
{},
{}
},
/* repmgrd_standby_startup_timeout */
{
"repmgrd_standby_startup_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.repmgrd_standby_startup_timeout },
{ .intdefault = DEFAULT_REPMGRD_STANDBY_STARTUP_TIMEOUT },
{ .intminval = 0 },
{},
{}
},
/* repmgrd_pid_file */
{
"repmgrd_pid_file",
CONFIG_STRING,
{ .strptr = config_file_options.repmgrd_pid_file },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.repmgrd_pid_file) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* standby_disconnect_on_failover */
{
"standby_disconnect_on_failover",
CONFIG_BOOL,
{ .boolptr = &config_file_options.standby_disconnect_on_failover },
{ .booldefault = DEFAULT_STANDBY_DISCONNECT_ON_FAILOVER },
{},
{},
{}
},
/* sibling_nodes_disconnect_timeout */
{
"sibling_nodes_disconnect_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.sibling_nodes_disconnect_timeout },
{ .intdefault = DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT },
{ .intminval = 0 },
{},
{}
},
/* connection_check_type */
{
"connection_check_type",
CONFIG_CONNECTION_CHECK_TYPE,
{ .checktypeptr = &config_file_options.connection_check_type },
{ .checktypedefault = DEFAULT_CONNECTION_CHECK_TYPE },
{},
{},
{}
},
/* primary_visibility_consensus */
{
"primary_visibility_consensus",
CONFIG_BOOL,
{ .boolptr = &config_file_options.primary_visibility_consensus },
{ .booldefault = DEFAULT_PRIMARY_VISIBILITY_CONSENSUS },
{},
{},
{}
},
/* always_promote */
{
"always_promote",
CONFIG_BOOL,
{ .boolptr = &config_file_options.always_promote },
{ .booldefault = DEFAULT_ALWAYS_PROMOTE },
{},
{},
{}
},
/* failover_validation_command */
{
"failover_validation_command",
CONFIG_STRING,
{ .strptr = config_file_options.failover_validation_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.failover_validation_command) },
{}
},
/* election_rerun_interval */
{
"election_rerun_interval",
CONFIG_INT,
{ .intptr = &config_file_options.election_rerun_interval },
{ .intdefault = DEFAULT_ELECTION_RERUN_INTERVAL },
{ .intminval = 1 },
{},
{}
},
/* child_nodes_check_interval */
{
"child_nodes_check_interval",
CONFIG_INT,
{ .intptr = &config_file_options.child_nodes_check_interval },
{ .intdefault = DEFAULT_CHILD_NODES_CHECK_INTERVAL },
{ .intminval = 1 },
{},
{}
},
/* child_nodes_disconnect_min_count */
{
"child_nodes_disconnect_min_count",
CONFIG_INT,
{ .intptr = &config_file_options.child_nodes_disconnect_min_count },
{ .intdefault = DEFAULT_CHILD_NODES_DISCONNECT_MIN_COUNT },
{ .intminval = -1 },
{},
{}
},
/* child_nodes_connected_min_count */
{
"child_nodes_connected_min_count",
CONFIG_INT,
{ .intptr = &config_file_options.child_nodes_connected_min_count },
{ .intdefault = DEFAULT_CHILD_NODES_CONNECTED_MIN_COUNT},
{ .intminval = -1 },
{},
{}
},
/* child_nodes_connected_include_witness */
{
"child_nodes_connected_include_witness",
CONFIG_BOOL,
{ .boolptr = &config_file_options.child_nodes_connected_include_witness },
{ .booldefault = DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS },
{},
{},
{}
},
/* child_nodes_disconnect_timeout */
{
"child_nodes_disconnect_timeout",
CONFIG_INT,
{ .intptr = &config_file_options.child_nodes_disconnect_timeout },
{ .intdefault = DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT },
{ .intminval = 0 },
{},
{}
},
/* child_nodes_disconnect_command */
{
"child_nodes_disconnect_command",
CONFIG_STRING,
{ .strptr = config_file_options.child_nodes_disconnect_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.child_nodes_disconnect_command) },
{}
},
/* ================
* service settings
* ================
*/
/* pg_ctl_options */
{
"pg_ctl_options",
CONFIG_STRING,
{ .strptr = config_file_options.pg_ctl_options },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.pg_ctl_options) },
{}
},
/* service_start_command */
{
"service_start_command",
CONFIG_STRING,
{ .strptr = config_file_options.service_start_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.service_start_command) },
{}
},
/* service_stop_command */
{
"service_stop_command",
CONFIG_STRING,
{ .strptr = config_file_options.service_stop_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.service_stop_command) },
{}
},
/* service_restart_command */
{
"service_restart_command",
CONFIG_STRING,
{ .strptr = config_file_options.service_restart_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.service_restart_command) },
{}
},
/* service_reload_command */
{
"service_reload_command",
CONFIG_STRING,
{ .strptr = config_file_options.service_reload_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.service_reload_command) },
{}
},
/* service_promote_command */
{
"service_promote_command",
CONFIG_STRING,
{ .strptr = config_file_options.service_promote_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.service_promote_command) },
{}
},
/* ========================
* repmgrd service settings
* ========================
*/
/* repmgrd_service_start_command */
{
"repmgrd_service_start_command",
CONFIG_STRING,
{ .strptr = config_file_options.repmgrd_service_start_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.repmgrd_service_start_command) },
{}
},
/* repmgrd_service_stop_command */
{
"repmgrd_service_stop_command",
CONFIG_STRING,
{ .strptr = config_file_options.repmgrd_service_stop_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.repmgrd_service_stop_command) },
{}
},
/* ===========================
* event notification settings
* ===========================
*/
/* event_notification_command */
{
"event_notification_command",
CONFIG_STRING,
{ .strptr = config_file_options.event_notification_command },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.event_notification_command) },
{}
},
{
"event_notifications",
CONFIG_EVENT_NOTIFICATION_LIST,
{ .notificationlistptr = &config_file_options.event_notifications },
{},
{},
{},
{}
},
/* ===============
* barman settings
* ===============
*/
/* barman_host */
{
"barman_host",
CONFIG_STRING,
{ .strptr = config_file_options.barman_host },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.barman_host) },
{}
},
/* barman_server */
{
"barman_server",
CONFIG_STRING,
{ .strptr = config_file_options.barman_server },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.barman_server) },
{}
},
/* barman_config */
{
"barman_config",
CONFIG_STRING,
{ .strptr = config_file_options.barman_config },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.barman_config) },
{}
},
/* ==================
* rsync/ssh settings
* ==================
*/
/* rsync_options */
{
"rsync_options",
CONFIG_STRING,
{ .strptr = config_file_options.rsync_options },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.rsync_options) },
{}
},
/* ssh_options */
{
"ssh_options",
CONFIG_STRING,
{ .strptr = config_file_options.ssh_options },
{ .strdefault = DEFAULT_SSH_OPTIONS },
{},
{ .strmaxlen = sizeof(config_file_options.ssh_options) },
{}
},
/* ==================================
* undocumented experimental settings
* ==================================
*/
/* reconnect_loop_sync */
{
"reconnect_loop_sync",
CONFIG_BOOL,
{ .boolptr = &config_file_options.reconnect_loop_sync },
{ .booldefault = false },
{},
{},
{}
},
/* ==========================
* undocumented test settings
* ==========================
*/
/* promote_delay */
{
"promote_delay",
CONFIG_INT,
{ .intptr = &config_file_options.promote_delay },
{ .intdefault = 0 },
{ .intminval = 1 },
{},
{}
},
/* failover_delay */
{
"failover_delay",
CONFIG_INT,
{ .intptr = &config_file_options.failover_delay },
{ .intdefault = 0 },
{ .intminval = 1 },
{},
{}
},
{
"connection_check_query",
CONFIG_STRING,
{ .strptr = config_file_options.connection_check_query },
{ .strdefault = "SELECT 1" },
{},
{ .strmaxlen = sizeof(config_file_options.connection_check_query) },
{}
},
/* End-of-list marker */
{
NULL, CONFIG_INT, {}, {}, {}, {}, {}
}
};

View File

@@ -5,8 +5,6 @@
%{
#include <setjmp.h>
#include <sys/stat.h>
#include <dirent.h>
#include "repmgr.h"
#include "configfile.h"
@@ -40,13 +38,7 @@ static sigjmp_buf *CONF_flex_fatal_jmp;
static char *CONF_scanstr(const char *s);
static int CONF_flex_fatal(const char *msg);
static bool ProcessConfigFile(const char *base_dir, const char *config_file, const char *calling_file, bool strict, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
static bool ProcessConfigFp(FILE *fp, const char *config_file, const char *calling_file, int depth, const char *base_dir, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
static bool ProcessConfigDirectory(const char *base_dir, const char *includedir, const char *calling_file, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
static char *AbsoluteConfigLocation(const char *base_dir, const char *location, const char *calling_file);
static bool ProcessConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, t_configuration_options *options, ItemList *error_list, ItemList *warning_list);
%}
@@ -98,91 +90,20 @@ STRING \'([^'\\\n]|\\.|\'\')*\'
%%
extern bool
ProcessRepmgrConfigFile(const char *config_file, const char *base_dir, ItemList *error_list, ItemList *warning_list)
ProcessRepmgrConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list)
{
return ProcessConfigFile(base_dir, config_file, NULL, true, 0, NULL, error_list, warning_list);
return ProcessConfigFile(fp, config_file, NULL, options, error_list, warning_list);
}
extern bool
ProcessPostgresConfigFile(const char *config_file, const char *base_dir, bool strict, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
ProcessPostgresConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
{
return ProcessConfigFile(base_dir, config_file, NULL, strict, 0, contents, error_list, warning_list);
return ProcessConfigFile(fp, config_file, contents, NULL, error_list, warning_list);
}
static bool
ProcessConfigFile(const char *base_dir, const char *config_file, const char *calling_file, bool strict, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
{
char *abs_path;
bool success = true;
FILE *fp;
/*
* Reject file name that is all-blank (including empty), as that leads to
* confusion --- we'd try to read the containing directory as a file.
*/
if (strspn(config_file, " \t\r\n") == strlen(config_file))
{
return false;
}
/*
* Reject too-deep include nesting depth. This is just a safety check to
* avoid dumping core due to stack overflow if an include file loops back
* to itself. The maximum nesting depth is pretty arbitrary.
*/
if (depth > 10)
{
item_list_append_format(error_list,
_("could not open configuration file \"%s\": maximum nesting depth exceeded"),
config_file);
return false;
}
abs_path = AbsoluteConfigLocation(base_dir, config_file, calling_file);
/* Reject direct recursion */
if (calling_file && strcmp(abs_path, calling_file) == 0)
{
item_list_append_format(error_list,
_("configuration file recursion in \"%s\""),
calling_file);
pfree(abs_path);
return false;
}
fp = fopen(abs_path, "r");
if (!fp)
{
if (strict == false)
{
item_list_append_format(error_list,
"skipping configuration file \"%s\"",
abs_path);
}
else
{
item_list_append_format(error_list,
"could not open configuration file \"%s\": %s",
abs_path,
strerror(errno));
success = false;
}
}
else
{
success = ProcessConfigFp(fp, abs_path, calling_file, depth + 1, base_dir, contents, error_list, warning_list);
}
free(abs_path);
return success;
}
static bool
ProcessConfigFp(FILE *fp, const char *config_file, const char *calling_file, int depth, const char *base_dir, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
ProcessConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, t_configuration_options *options, ItemList *error_list, ItemList *warning_list)
{
volatile bool OK = true;
volatile YY_BUFFER_STATE lex_buffer = NULL;
@@ -258,62 +179,22 @@ ProcessConfigFp(FILE *fp, const char *config_file, const char *calling_file, int
ConfigFileLineno++;
}
/* Handle include files */
if (base_dir != NULL && strcasecmp(opt_name, "include_dir") == 0)
/* OK, process the option name and value */
if (contents != NULL)
{
/*
* An include_dir directive isn't a variable and should be
* processed immediately.
*/
if (!ProcessConfigDirectory(base_dir, opt_value, config_file,
depth + 1, contents,
error_list, warning_list))
OK = false;
yy_switch_to_buffer(lex_buffer);
pfree(opt_name);
pfree(opt_value);
}
else if (base_dir != NULL && strcasecmp(opt_name, "include_if_exists") == 0)
{
if (!ProcessConfigFile(base_dir, opt_value, config_file,
false, depth + 1, contents,
error_list, warning_list))
OK = false;
yy_switch_to_buffer(lex_buffer);
pfree(opt_name);
pfree(opt_value);
}
else if (base_dir != NULL && strcasecmp(opt_name, "include") == 0)
{
if (!ProcessConfigFile(base_dir, opt_value, config_file,
true, depth + 1, contents,
error_list, warning_list))
OK = false;
yy_switch_to_buffer(lex_buffer);
pfree(opt_name);
pfree(opt_value);
}
else
{
/* OK, process the option name and value */
if (contents != NULL)
{
key_value_list_replace_or_set(contents,
opt_name,
opt_value);
}
else
{
parse_configuration_item(error_list,
warning_list,
opt_name,
opt_value);
}
key_value_list_replace_or_set(contents,
opt_name,
opt_value);
}
if (options != NULL)
{
parse_configuration_item(options,
error_list,
warning_list,
opt_name,
opt_value);
}
/* break out of loop if read EOF, else loop for next line */
if (token == 0)
@@ -372,132 +253,6 @@ cleanup:
return OK;
}
/*
* Read and parse all config files in a subdirectory in alphabetical order
*
* includedir is the absolute or relative path to the subdirectory to scan.
*
* See ProcessConfigFp for further details.
*/
static bool
ProcessConfigDirectory(const char *base_dir, const char *includedir, const char *calling_file, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
{
char *directory;
DIR *d;
struct dirent *de;
char **filenames;
int num_filenames;
int size_filenames;
bool status;
/*
* Reject directory name that is all-blank (including empty), as that
* leads to confusion --- we'd read the containing directory, typically
* resulting in recursive inclusion of the same file(s).
*/
if (strspn(includedir, " \t\r\n") == strlen(includedir))
{
item_list_append_format(error_list,
_("empty configuration directory name: \"%s\""),
includedir);
return false;
}
directory = AbsoluteConfigLocation(base_dir, includedir, calling_file);
d = opendir(directory);
if (d == NULL)
{
item_list_append_format(error_list,
_("could not open configuration directory \"%s\": %s"),
directory,
strerror(errno));
status = false;
goto cleanup;
}
/*
* Read the directory and put the filenames in an array, so we can sort
* them prior to processing the contents.
*/
size_filenames = 32;
filenames = (char **) palloc(size_filenames * sizeof(char *));
num_filenames = 0;
while ((de = readdir(d)) != NULL)
{
struct stat st;
char filename[MAXPGPATH];
/*
* Only parse files with names ending in ".conf". Explicitly reject
* files starting with ".". This excludes things like "." and "..",
* as well as typical hidden files, backup files, and editor debris.
*/
if (strlen(de->d_name) < 6)
continue;
if (de->d_name[0] == '.')
continue;
if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
continue;
join_path_components(filename, directory, de->d_name);
canonicalize_path(filename);
if (stat(filename, &st) == 0)
{
if (!S_ISDIR(st.st_mode))
{
/* Add file to array, increasing its size in blocks of 32 */
if (num_filenames >= size_filenames)
{
size_filenames += 32;
filenames = (char **) repalloc(filenames,
size_filenames * sizeof(char *));
}
filenames[num_filenames] = pstrdup(filename);
num_filenames++;
}
}
else
{
/*
* stat does not care about permissions, so the most likely reason
* a file can't be accessed now is if it was removed between the
* directory listing and now.
*/
item_list_append_format(error_list,
_("could not stat file \"%s\": %s"),
filename, strerror(errno));
status = false;
goto cleanup;
}
}
if (num_filenames > 0)
{
int i;
qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
for (i = 0; i < num_filenames; i++)
{
if (!ProcessConfigFile(base_dir, filenames[i], calling_file,
true, depth, contents,
error_list, warning_list))
{
status = false;
goto cleanup;
}
}
}
status = true;
cleanup:
if (d)
closedir(d);
pfree(directory);
return status;
}
/*
* scanstr
@@ -593,39 +348,6 @@ CONF_scanstr(const char *s)
return newStr;
}
/*
* Given a configuration file or directory location that may be a relative
* path, return an absolute one. We consider the location to be relative to
* the directory holding the calling file, or to DataDir if no calling file.
*/
static char *
AbsoluteConfigLocation(const char *base_dir, const char *location, const char *calling_file)
{
char abs_path[MAXPGPATH];
if (is_absolute_path(location))
return strdup(location);
if (calling_file != NULL)
{
strlcpy(abs_path, calling_file, sizeof(abs_path));
get_parent_directory(abs_path);
join_path_components(abs_path, abs_path, location);
canonicalize_path(abs_path);
}
else if (base_dir != NULL)
{
join_path_components(abs_path, base_dir, location);
canonicalize_path(abs_path);
}
else
{
strlcpy(abs_path, location, sizeof(abs_path));
}
return strdup(abs_path);
}
/*
* Flex fatal errors bring us here. Stash the error message and jump back to

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@
#define TARGET_TIMELINE_LATEST 0
/*
* This is defined in src/include/utils.h, however it's not practical
* This is defined src/include/utils.h, however it's not practical
* to include that from a frontend application.
*/
#define PG_AUTOCONF_FILENAME "postgresql.auto.conf"
@@ -50,11 +50,6 @@ typedef enum
CHECK_CONNECTION
} ConnectionCheckType;
typedef enum
{
REPLICATION_TYPE_PHYSICAL
} ReplicationType;
typedef struct EventNotificationListCell
{
struct EventNotificationListCell *next;
@@ -83,58 +78,6 @@ typedef struct TablespaceList
} TablespaceList;
typedef enum
{
CONFIG_BOOL,
CONFIG_INT,
CONFIG_STRING,
CONFIG_FAILOVER_MODE,
CONFIG_CONNECTION_CHECK_TYPE,
CONFIG_EVENT_NOTIFICATION_LIST,
CONFIG_TABLESPACE_MAPPING,
CONFIG_REPLICATION_TYPE
} ConfigItemType;
typedef struct ConfigFileSetting
{
const char *name;
ConfigItemType type;
union
{
int *intptr;
char *strptr;
bool *boolptr;
failover_mode_opt *failovermodeptr;
ConnectionCheckType *checktypeptr;
EventNotificationList *notificationlistptr;
TablespaceList *tablespacemappingptr;
ReplicationType *replicationtypeptr;
} val;
union {
int intdefault;
const char *strdefault;
bool booldefault;
failover_mode_opt failovermodedefault;
ConnectionCheckType checktypedefault;
ReplicationType replicationtypedefault;
} defval;
union {
int intminval;
} minval;
union {
int strmaxlen;
} maxval;
struct {
void (*process_func)(const char *, const char *, char *, ItemList *errors);
void (*postprocess_func)(const char *, const char *, char *, ItemList *errors);
bool *providedptr;
} process;
} ConfigFileSetting;
/* Declare the main configfile structure for client applications */
extern ConfigFileSetting config_file_settings[];
typedef struct
{
/* node information */
@@ -146,7 +89,7 @@ typedef struct
char config_directory[MAXPGPATH];
char pg_bindir[MAXPGPATH];
char repmgr_bindir[MAXPGPATH];
ReplicationType replication_type;
int replication_type;
/* log settings */
char log_level[MAXLEN];
@@ -172,7 +115,6 @@ typedef struct
/* standby follow settings */
int primary_follow_timeout;
int standby_follow_timeout;
bool standby_follow_restart;
/* standby switchover settings */
int shutdown_check_timeout;
@@ -210,7 +152,6 @@ typedef struct
int sibling_nodes_disconnect_timeout;
ConnectionCheckType connection_check_type;
bool primary_visibility_consensus;
bool always_promote;
char failover_validation_command[MAXPGPATH];
int election_rerun_interval;
int child_nodes_check_interval;
@@ -246,35 +187,77 @@ typedef struct
char rsync_options[MAXLEN];
char ssh_options[MAXLEN];
/*
* undocumented settings
*
* These settings are for testing or experimential features
* and may be changed without notice.
*/
/* experimental settings */
bool reconnect_loop_sync;
/* test settings */
/* undocumented test settings */
int promote_delay;
int failover_delay;
char connection_check_query[MAXLEN];
} t_configuration_options;
/*
* The following will initialize the structure with a minimal set of options;
* actual defaults are set in parse_config() before parsing the configuration file
*/
#define T_CONFIGURATION_OPTIONS_INITIALIZER { \
/* node information */ \
UNKNOWN_NODE_ID, "", "", "", "", "", "", "", REPLICATION_TYPE_PHYSICAL, \
/* log settings */ \
"", "", "", DEFAULT_LOG_STATUS_INTERVAL, \
/* standby clone settings */ \
false, "", "", { NULL, NULL }, "", false, "", false, "", \
/* standby promote settings */ \
DEFAULT_PROMOTE_CHECK_TIMEOUT, DEFAULT_PROMOTE_CHECK_INTERVAL, \
/* standby follow settings */ \
DEFAULT_PRIMARY_FOLLOW_TIMEOUT, \
DEFAULT_STANDBY_FOLLOW_TIMEOUT, \
/* standby switchover settings */ \
DEFAULT_SHUTDOWN_CHECK_TIMEOUT, \
DEFAULT_STANDBY_RECONNECT_TIMEOUT, \
DEFAULT_WAL_RECEIVE_CHECK_TIMEOUT, \
/* node rejoin settings */ \
DEFAULT_NODE_REJOIN_TIMEOUT, \
/* node check settings */ \
DEFAULT_ARCHIVE_READY_WARNING, DEFAULT_ARCHIVE_READY_CRITICAL, \
DEFAULT_REPLICATION_LAG_WARNING, DEFAULT_REPLICATION_LAG_CRITICAL, \
/* witness settings */ \
DEFAULT_WITNESS_SYNC_INTERVAL, \
/* repmgrd settings */ \
FAILOVER_MANUAL, DEFAULT_LOCATION, DEFAULT_PRIORITY, "", "", \
DEFAULT_MONITORING_INTERVAL, \
DEFAULT_RECONNECTION_ATTEMPTS, \
DEFAULT_RECONNECTION_INTERVAL, \
false, -1, \
DEFAULT_ASYNC_QUERY_TIMEOUT, \
DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT, \
-1, "", false, DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT, \
CHECK_PING, true, "", DEFAULT_ELECTION_RERUN_INTERVAL, \
DEFAULT_CHILD_NODES_CHECK_INTERVAL, \
DEFAULT_CHILD_NODES_DISCONNECT_MIN_COUNT, \
DEFAULT_CHILD_NODES_CONNECTED_MIN_COUNT, \
DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS, \
DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT, "", \
/* service settings */ \
"", "", "", "", "", "", \
/* repmgrd service settings */ \
"", "", \
/* event notification settings */ \
"", "", { NULL, NULL }, \
/* barman settings */ \
"", "", "", \
/* rsync/ssh settings */ \
"", "", \
/* undocumented test settings */ \
0 \
}
/* Declare the main configfile structure for client applications */
extern t_configuration_options config_file_options;
typedef struct
{
char slot[MAXLEN];
char wal_method[MAXLEN];
char waldir[MAXPGPATH];
bool no_slot; /* from PostgreSQL 10 */
} t_basebackup_options;
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", "", false }
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", false }
typedef enum
@@ -331,11 +314,10 @@ typedef struct
void set_progname(const char *argv0);
const char *progname(void);
void load_config(const char *config_file, bool verbose, bool terse, char *argv0);
bool reload_config(t_server_type server_type);
void dump_config(void);
void load_config(const char *config_file, bool verbose, bool terse, t_configuration_options *options, char *argv0);
bool reload_config(t_configuration_options *orig_options, t_server_type server_type);
void parse_configuration_item(ItemList *error_list, ItemList *warning_list, const char *name, const char *value);
void parse_configuration_item(t_configuration_options *options, ItemList *error_list, ItemList *warning_list, const char *name, const char *value);
bool parse_recovery_conf(const char *data_dir, t_recovery_conf *conf);
@@ -348,9 +330,6 @@ int repmgr_atoi(const char *s,
ItemList *error_list,
int minval);
void parse_time_unit_parameter(const char *name, const char *value, char *dest, ItemList *errors);
void repmgr_canonicalize_path(const char *name, const char *value, char *config_item, ItemList *errors);
bool parse_pg_basebackup_options(const char *pg_basebackup_options,
t_basebackup_options *backup_options,
int server_version_num,
@@ -358,21 +337,17 @@ bool parse_pg_basebackup_options(const char *pg_basebackup_options,
int parse_output_to_argv(const char *string, char ***argv_array);
void free_parsed_argv(char ***argv_array);
const char *format_failover_mode(failover_mode_opt failover);
/* called by repmgr-client and repmgrd */
void exit_with_cli_errors(ItemList *error_list, const char *repmgr_command);
void print_item_list(ItemList *item_list);
const char *print_replication_type(ReplicationType type);
const char *print_connection_check_type(ConnectionCheckType type);
char *print_event_notification_list(EventNotificationList *list);
char *print_tablespace_mapping(TablespaceList *tablespacemappingptr);
extern bool modify_auto_conf(const char *data_dir, KeyValueList *items);
extern bool ProcessRepmgrConfigFile(const char *config_file, const char *base_dir, ItemList *error_list, ItemList *warning_list);
extern bool ProcessRepmgrConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list);
extern bool ProcessPostgresConfigFile(const char *config_file, const char *base_dir, bool strict, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
extern bool ProcessPostgresConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
#endif /* _REPMGR_CONFIGFILE_H_ */

20
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for repmgr 5.2.1.
# Generated by GNU Autoconf 2.69 for repmgr 5.1.0.
#
# Report bugs to <repmgr@googlegroups.com>.
#
@@ -582,8 +582,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='repmgr'
PACKAGE_TARNAME='repmgr'
PACKAGE_VERSION='5.2.1'
PACKAGE_STRING='repmgr 5.2.1'
PACKAGE_VERSION='5.1.0'
PACKAGE_STRING='repmgr 5.1.0'
PACKAGE_BUGREPORT='repmgr@googlegroups.com'
PACKAGE_URL='https://repmgr.org/'
@@ -1181,7 +1181,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures repmgr 5.2.1 to adapt to many kinds of systems.
\`configure' configures repmgr 5.1.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1242,7 +1242,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of repmgr 5.2.1:";;
short | recursive ) echo "Configuration of repmgr 5.1.0:";;
esac
cat <<\_ACEOF
@@ -1316,7 +1316,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
repmgr configure 5.2.1
repmgr configure 5.1.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1335,7 +1335,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by repmgr $as_me 5.2.1, which was
It was created by repmgr $as_me 5.1.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -1824,7 +1824,7 @@ if test "$major_version_num" -lt '10'; then
version_num_int=$(echo "$version_num"|
$SED -e 's/^\([0-9]*\)\.\([0-9]*\)$/\1\2/')
if test "$version_num_int" -lt '94'; then
if test "$version_num_int" -lt '93'; then
as_fn_error $? "repmgr is not compatible with detected PostgreSQL version: $version_num" "$LINENO" 5
fi
else
@@ -2487,7 +2487,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by repmgr $as_me 5.2.1, which was
This file was extended by repmgr $as_me 5.1.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -2550,7 +2550,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
repmgr config.status 5.2.1
repmgr config.status 5.1.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([repmgr], [5.2.1], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
AC_INIT([repmgr], [5.1.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
AC_COPYRIGHT([Copyright (c) 2010-2020, 2ndQuadrant Ltd.])
@@ -32,7 +32,7 @@ if test "$major_version_num" -lt '10'; then
version_num_int=$(echo "$version_num"|
$SED -e 's/^\([[0-9]]*\)\.\([[0-9]]*\)$/\1\2/')
if test "$version_num_int" -lt '94'; then
if test "$version_num_int" -lt '93'; then
AC_MSG_ERROR([repmgr is not compatible with detected PostgreSQL version: $version_num])
fi
else

View File

@@ -90,9 +90,7 @@ get_system_identifier(const char *data_directory)
uint64 system_identifier = UNKNOWN_SYSTEM_IDENTIFIER;
control_file_info = get_controlfile(data_directory);
if (control_file_info->control_file_processed == true)
system_identifier = control_file_info->system_identifier;
system_identifier = control_file_info->system_identifier;
pfree(control_file_info);
@@ -100,21 +98,19 @@ get_system_identifier(const char *data_directory)
}
bool
get_db_state(const char *data_directory, DBState *state)
DBState
get_db_state(const char *data_directory)
{
ControlFileInfo *control_file_info = NULL;
bool control_file_processed;
DBState state;
control_file_info = get_controlfile(data_directory);
control_file_processed = control_file_info->control_file_processed;
if (control_file_processed == true)
*state = control_file_info->state;
state = control_file_info->state;
pfree(control_file_info);
return control_file_processed;
return state;
}
@@ -126,8 +122,7 @@ get_latest_checkpoint_location(const char *data_directory)
control_file_info = get_controlfile(data_directory);
if (control_file_info->control_file_processed == true)
checkPoint = control_file_info->checkPoint;
checkPoint = control_file_info->checkPoint;
pfree(control_file_info);
@@ -139,12 +134,11 @@ int
get_data_checksum_version(const char *data_directory)
{
ControlFileInfo *control_file_info = NULL;
int data_checksum_version = UNKNOWN_DATA_CHECKSUM_VERSION;
int data_checksum_version = -1;
control_file_info = get_controlfile(data_directory);
if (control_file_info->control_file_processed == true)
data_checksum_version = (int) control_file_info->data_checksum_version;
data_checksum_version = (int) control_file_info->data_checksum_version;
pfree(control_file_info);
@@ -283,19 +277,8 @@ get_controlfile(const char *DataDir)
return control_file_info;
}
if (version_num >= 120000)
{
#if PG_ACTUAL_VERSION_NUM >= 120000
expected_size = sizeof(ControlFileData12);
ControlFileDataPtr = palloc0(expected_size);
#endif
}
else if (version_num >= 110000)
{
expected_size = sizeof(ControlFileData11);
ControlFileDataPtr = palloc0(expected_size);
}
else if (version_num >= 90500)
if (version_num >= 90500)
{
expected_size = sizeof(ControlFileData95);
ControlFileDataPtr = palloc0(expected_size);
@@ -305,6 +288,12 @@ get_controlfile(const char *DataDir)
expected_size = sizeof(ControlFileData94);
ControlFileDataPtr = palloc0(expected_size);
}
else if (version_num >= 90300)
{
expected_size = sizeof(ControlFileData93);
ControlFileDataPtr = palloc0(expected_size);
}
if (read(fd, ControlFileDataPtr, expected_size) != expected_size)
{
@@ -333,7 +322,7 @@ get_controlfile(const char *DataDir)
control_file_info->minRecoveryPointTLI = ptr->minRecoveryPointTLI;
control_file_info->minRecoveryPoint = ptr->minRecoveryPoint;
#else
fprintf(stderr, "ERROR: please use a repmgr version built for PostgreSQL 12 or later\n");
fprintf(stderr, "ERROR: please use a repmgr version built for PostgreSQL 12\n");
exit(ERR_BAD_CONFIG);
#endif
}
@@ -370,6 +359,17 @@ get_controlfile(const char *DataDir)
control_file_info->minRecoveryPointTLI = ptr->minRecoveryPointTLI;
control_file_info->minRecoveryPoint = ptr->minRecoveryPoint;
}
else if (version_num >= 90300)
{
ControlFileData93 *ptr = (struct ControlFileData93 *)ControlFileDataPtr;
control_file_info->system_identifier = ptr->system_identifier;
control_file_info->state = ptr->state;
control_file_info->checkPoint = ptr->checkPoint;
control_file_info->data_checksum_version = ptr->data_checksum_version;
control_file_info->timeline = ptr->checkPointCopy.ThisTimeLineID;
control_file_info->minRecoveryPointTLI = ptr->minRecoveryPointTLI;
control_file_info->minRecoveryPoint = ptr->minRecoveryPoint;
}
pfree(ControlFileDataPtr);

View File

@@ -31,7 +31,9 @@ typedef struct
} ControlFileInfo;
typedef struct CheckPoint94
/* Same for 9.3, 9.4 */
typedef struct CheckPoint93
{
XLogRecPtr redo; /* next RecPtr available when we began to
* create CheckPoint (i.e. REDO start point) */
@@ -51,7 +53,7 @@ typedef struct CheckPoint94
pg_time_t time; /* time stamp of checkpoint */
TransactionId oldestActiveXid;
} CheckPoint94;
} CheckPoint93;
/* Same for 9.5, 9.6, 10, 11 */
@@ -126,6 +128,65 @@ typedef struct CheckPoint12
} CheckPoint12;
#endif
typedef struct ControlFileData93
{
uint64 system_identifier;
uint32 pg_control_version; /* PG_CONTROL_VERSION */
uint32 catalog_version_no; /* see catversion.h */
DBState state; /* see enum above */
pg_time_t time; /* time stamp of last pg_control update */
XLogRecPtr checkPoint; /* last check point record ptr */
XLogRecPtr prevCheckPoint; /* previous check point record ptr */
CheckPoint93 checkPointCopy; /* copy of last check point record */
XLogRecPtr unloggedLSN; /* current fake LSN value, for unlogged rels */
XLogRecPtr minRecoveryPoint;
TimeLineID minRecoveryPointTLI;
XLogRecPtr backupStartPoint;
XLogRecPtr backupEndPoint;
bool backupEndRequired;
int wal_level;
int MaxConnections;
int max_prepared_xacts;
int max_locks_per_xact;
uint32 maxAlign; /* alignment requirement for tuples */
double floatFormat; /* constant 1234567.0 */
uint32 blcksz; /* data block size for this DB */
uint32 relseg_size; /* blocks per segment of large relation */
uint32 xlog_blcksz; /* block size within WAL files */
uint32 xlog_seg_size; /* size of each WAL segment */
uint32 nameDataLen; /* catalog name field width */
uint32 indexMaxKeys; /* max number of columns in an index */
uint32 toast_max_chunk_size; /* chunk size in TOAST tables */
/* flag indicating internal format of timestamp, interval, time */
bool enableIntTimes; /* int64 storage enabled? */
/* flags indicating pass-by-value status of various types */
bool float4ByVal; /* float4 pass-by-value? */
bool float8ByVal; /* float8, int8, etc pass-by-value? */
/* Are data pages protected by checksums? Zero if no checksum version */
uint32 data_checksum_version;
} ControlFileData93;
/*
* Following field added since 9.3:
*
* int max_worker_processes;
*/
typedef struct ControlFileData94
{
@@ -139,7 +200,7 @@ typedef struct ControlFileData94
XLogRecPtr checkPoint; /* last check point record ptr */
XLogRecPtr prevCheckPoint; /* previous check point record ptr */
CheckPoint94 checkPointCopy; /* copy of last check point record */
CheckPoint93 checkPointCopy; /* copy of last check point record */
XLogRecPtr unloggedLSN; /* current fake LSN value, for unlogged rels */
@@ -377,7 +438,7 @@ typedef struct ControlFileData12
#endif
extern int get_pg_version(const char *data_directory, char *version_string);
extern bool get_db_state(const char *data_directory, DBState *state);
extern DBState get_db_state(const char *data_directory);
extern const char *describe_db_state(DBState state);
extern int get_data_checksum_version(const char *data_directory);
extern uint64 get_system_identifier(const char *data_directory);

300
dbutils.c
View File

@@ -153,9 +153,6 @@ _establish_db_connection(const char *conninfo, const bool exit_on_error, const b
if (param_get(&conninfo_params, "replication") != NULL)
is_replication_connection = true;
/* use a secure search_path */
param_set(&conninfo_params, "options", "-csearch_path=");
connection_string = param_list_to_string(&conninfo_params);
log_debug(_("connecting to: \"%s\""), connection_string);
@@ -306,9 +303,6 @@ establish_db_connection_by_params(t_conninfo_param_list *param_list,
param_set_ine(param_list, "connect_timeout", "2");
param_set_ine(param_list, "fallback_application_name", "repmgr");
/* use a secure search_path */
param_set(param_list, "options", "-csearch_path=");
/* Connect to the database using the provided parameters */
conn = PQconnectdbParams((const char **) param_list->keywords, (const char **) param_list->values, true);
@@ -711,35 +705,10 @@ param_get(t_conninfo_param_list *param_list, const char *param)
}
/*
* Validate a conninfo string by attempting to parse it.
*
* "errmsg": passed to PQconninfoParse(), may be NULL
*
* NOTE: PQconninfoParse() verifies the string format and checks for
* valid options but does not sanity check values.
*/
bool
validate_conninfo_string(const char *conninfo_str, char **errmsg)
{
PQconninfoOption *connOptions = NULL;
connOptions = PQconninfoParse(conninfo_str, errmsg);
if (connOptions == NULL)
return false;
return true;
}
/*
* Parse a conninfo string into a t_conninfo_param_list
*
* See conn_to_param_list() to do the same for a PGconn.
*
* "errmsg": passed to PQconninfoParse(), may be NULL
* See conn_to_param_list() to do the same for a PGconn
*
* "ignore_local_params": ignores those parameters specific
* to a local installation, i.e. when parsing an upstream
@@ -760,7 +729,8 @@ parse_conninfo_string(const char *conninfo_str, t_conninfo_param_list *param_lis
for (option = connOptions; option && option->keyword; option++)
{
/* Ignore non-set or blank parameter values */
if (option->val == NULL || option->val[0] == '\0')
if ((option->val == NULL) ||
(option->val != NULL && option->val[0] == '\0'))
continue;
/* Ignore settings specific to the upstream node */
@@ -805,7 +775,8 @@ conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list)
for (option = connOptions; option && option->keyword; option++)
{
/* Ignore non-set or blank parameter values */
if (option->val == NULL || option->val[0] == '\0')
if ((option->val == NULL) ||
(option->val != NULL && option->val[0] == '\0'))
continue;
/* Ignore "password" */
@@ -1255,7 +1226,7 @@ bool
pg_reload_conf(PGconn *conn)
{
PGresult *res = NULL;
bool success = true;
bool success = false;
res = PQexec(conn, "SELECT pg_catalog.pg_reload_conf()");
@@ -1672,12 +1643,7 @@ identify_system(PGconn *repl_conn, t_system_identification *identification)
return false;
}
#if defined(__i386__) || defined(__i386)
identification->system_identifier = atoll(PQgetvalue(res, 0, 0));
#else
identification->system_identifier = atol(PQgetvalue(res, 0, 0));
#endif
identification->timeline = atoi(PQgetvalue(res, 0, 1));
identification->xlogpos = parse_lsn(PQgetvalue(res, 0, 2));
@@ -1714,11 +1680,7 @@ system_identifier(PGconn *conn)
}
else
{
#if defined(__i386__) || defined(__i386)
system_identifier = atoll(PQgetvalue(res, 0, 0));
#else
system_identifier = atol(PQgetvalue(res, 0, 0));
#endif
}
PQclear(res);
@@ -1837,7 +1799,7 @@ can_execute_pg_promote(PGconn *conn)
bool has_pg_promote= false;
/* pg_promote() available from PostgreSQL 12 */
if (PQserverVersion(conn) < 120000)
if(PQserverVersion(conn) < 120000)
return false;
initPQExpBuffer(&query);
@@ -1865,59 +1827,48 @@ can_execute_pg_promote(PGconn *conn)
}
/*
* Determine if the user associated with the current connection is
* a member of the "pg_monitor" default role, or optionally one
* of its three constituent "subroles".
*/
bool
connection_has_pg_monitor_role(PGconn *conn, const char *subrole)
connection_has_pg_settings(PGconn *conn)
{
PQExpBufferData query;
PGresult *res;
bool has_pg_monitor_role = false;
bool has_pg_settings = false;
/* superusers can read anything, no role check needed */
/* superusers can always read pg_settings */
if (is_superuser_connection(conn, NULL) == true)
return true;
/* pg_monitor and associated "subroles" introduced in PostgreSQL 10 */
if (PQserverVersion(conn) < 100000)
return false;
initPQExpBuffer(&query);
appendPQExpBufferStr(&query,
" SELECT CASE "
" WHEN pg_catalog.pg_has_role('pg_monitor','MEMBER') "
" THEN TRUE ");
if (subrole != NULL)
{
appendPQExpBuffer(&query,
" WHEN pg_catalog.pg_has_role('%s','MEMBER') "
" THEN TRUE ",
subrole);
has_pg_settings = true;
}
/* from PostgreSQL 10, a non-superuser may have been granted access */
else if(PQserverVersion(conn) >= 100000)
{
PQExpBufferData query;
PGresult *res;
initPQExpBuffer(&query);
appendPQExpBufferStr(&query,
" SELECT CASE "
" WHEN pg_catalog.pg_has_role('pg_monitor','MEMBER') "
" THEN TRUE "
" WHEN pg_catalog.pg_has_role('pg_read_all_settings','MEMBER') "
" THEN TRUE "
" ELSE FALSE "
" END AS has_pg_settings");
res = PQexec(conn, query.data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_db_error(conn, query.data,
_("connection_has_pg_settings(): unable to query user roles"));
}
else
{
has_pg_settings = atobool(PQgetvalue(res, 0, 0));
}
termPQExpBuffer(&query);
PQclear(res);
}
appendPQExpBufferStr(&query,
" ELSE FALSE "
" END AS has_pg_monitor");
res = PQexec(conn, query.data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_db_error(conn, query.data,
_("connection_has_pg_monitor_role(): unable to query user roles"));
}
else
{
has_pg_monitor_role = atobool(PQgetvalue(res, 0, 0));
}
termPQExpBuffer(&query);
PQclear(res);
return has_pg_monitor_role;
return has_pg_settings;
}
@@ -2546,10 +2497,7 @@ resume_wal_replay(PGconn *conn)
/* Node record functions */
/* ===================== */
/*
* Note: init_defaults may only be false when the caller is refreshing a previously
* populated record.
*/
static RecordStatus
_get_node_record(PGconn *conn, char *sqlquery, t_node_info *node_info, bool init_defaults)
{
@@ -2580,10 +2528,6 @@ _get_node_record(PGconn *conn, char *sqlquery, t_node_info *node_info, bool init
}
/*
* Note: init_defaults may only be false when the caller is refreshing a previously
* populated record.
*/
static void
_populate_node_record(PGresult *res, t_node_info *node_info, int row, bool init_defaults)
{
@@ -2914,37 +2858,6 @@ get_all_node_records(PGconn *conn, NodeInfoList *node_list)
return success;
}
bool
get_all_nodes_count(PGconn *conn, int *count)
{
PQExpBufferData query;
PGresult *res = NULL;
bool success = true;
initPQExpBuffer(&query);
appendPQExpBufferStr(&query,
" SELECT count(*) "
" FROM repmgr.nodes n ");
log_verbose(LOG_DEBUG, "get_all_nodes_count():\n%s", query.data);
res = PQexec(conn, query.data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_db_error(conn, query.data, _("get_all_nodes_count(): unable to execute query"));
success = false;
}
else
{
*count = atoi(PQgetvalue(res, 0, 0));
}
PQclear(res);
termPQExpBuffer(&query);
return success;
}
void
get_downstream_node_records(PGconn *conn, int node_id, NodeInfoList *node_list)
@@ -3038,7 +2951,7 @@ get_child_nodes(PGconn *conn, int node_id, NodeInfoList *node_list)
" WHERE n.upstream_node_id = %i ",
node_id);
log_verbose(LOG_DEBUG, "get_child_nodes():\n%s", query.data);
log_verbose(LOG_DEBUG, "get_active_sibling_node_records():\n%s", query.data);
res = PQexec(conn, query.data);
@@ -3601,21 +3514,11 @@ witness_copy_node_records(PGconn *primary_conn, PGconn *witness_conn)
return false;
}
if (get_all_node_records(primary_conn, &nodes) == false)
{
rollback_transaction(witness_conn);
return false;
}
get_all_node_records(primary_conn, &nodes);
for (cell = nodes.head; cell; cell = cell->next)
{
if (create_node_record(witness_conn, NULL, cell->node_info) == false)
{
rollback_transaction(witness_conn);
return false;
}
create_node_record(witness_conn, NULL, cell->node_info);
}
/* and done */
@@ -5576,9 +5479,21 @@ get_replication_info(PGconn *conn, t_server_type node_type, ReplInfo *replicatio
}
else
{
if (PQserverVersion(conn) >= 90400)
{
appendPQExpBufferStr(&query,
" COALESCE(pg_catalog.pg_last_xlog_receive_location(), '0/0'::PG_LSN) AS last_wal_receive_lsn, "
" COALESCE(pg_catalog.pg_last_xlog_replay_location(), '0/0'::PG_LSN) AS last_wal_replay_lsn, ");
}
else
{
/* 9.3 does not have "pg_lsn" datatype */
appendPQExpBufferStr(&query,
" COALESCE(pg_catalog.pg_last_xlog_receive_location(), '0/0') AS last_wal_receive_lsn, "
" COALESCE(pg_catalog.pg_last_xlog_replay_location(), '0/0') AS last_wal_replay_lsn, ");
}
appendPQExpBufferStr(&query,
" COALESCE(pg_catalog.pg_last_xlog_receive_location(), '0/0'::PG_LSN) AS last_wal_receive_lsn, "
" COALESCE(pg_catalog.pg_last_xlog_replay_location(), '0/0'::PG_LSN) AS last_wal_replay_lsn, "
" CASE WHEN pg_catalog.pg_is_in_recovery() IS FALSE "
" THEN FALSE "
" ELSE pg_catalog.pg_is_xlog_replay_paused() "
@@ -5749,11 +5664,28 @@ get_node_replication_stats(PGconn *conn, t_node_info *node_info)
appendPQExpBufferStr(&query,
" SELECT pg_catalog.current_setting('max_wal_senders')::INT AS max_wal_senders, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_stat_replication) AS attached_wal_receivers, "
" current_setting('max_replication_slots')::INT AS max_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_replication_slots WHERE slot_type='physical') AS total_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_replication_slots WHERE active IS TRUE AND slot_type='physical') AS active_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_replication_slots WHERE active IS FALSE AND slot_type='physical') AS inactive_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_stat_replication) AS attached_wal_receivers, ");
/* no replication slots in PostgreSQL 9.3 */
if (PQserverVersion(conn) < 90400)
{
appendPQExpBufferStr(&query,
" 0 AS max_replication_slots, "
" 0 AS total_replication_slots, "
" 0 AS active_replication_slots, "
" 0 AS inactive_replication_slots, ");
}
else
{
appendPQExpBufferStr(&query,
" current_setting('max_replication_slots')::INT AS max_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_replication_slots WHERE slot_type='physical') AS total_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_replication_slots WHERE active IS TRUE AND slot_type='physical') AS active_replication_slots, "
" (SELECT pg_catalog.count(*) FROM pg_catalog.pg_replication_slots WHERE active IS FALSE AND slot_type='physical') AS inactive_replication_slots, ");
}
appendPQExpBufferStr(&query,
" pg_catalog.pg_is_in_recovery() AS in_recovery");
log_verbose(LOG_DEBUG, "get_node_replication_stats():\n%s", query.data);
@@ -5788,15 +5720,16 @@ get_node_replication_stats(PGconn *conn, t_node_info *node_info)
NodeAttached
is_downstream_node_attached(PGconn *conn, char *node_name, char **node_state)
is_downstream_node_attached(PGconn *conn, char *node_name)
{
PQExpBufferData query;
PGresult *res = NULL;
int c = 0;
initPQExpBuffer(&query);
appendPQExpBuffer(&query,
" SELECT pid, state "
" SELECT pg_catalog.count(*) "
" FROM pg_catalog.pg_stat_replication "
" WHERE application_name = '%s'",
node_name);
@@ -5815,68 +5748,31 @@ is_downstream_node_attached(PGconn *conn, char *node_name, char **node_state)
return NODE_ATTACHED_UNKNOWN;
}
termPQExpBuffer(&query);
/*
* If there's more than one entry in pg_stat_application, there's no
* way we can reliably determine which one belongs to the node we're
* checking, so there's nothing more we can do.
*/
if (PQntuples(res) > 1)
if (PQntuples(res) != 1)
{
log_error(_("multiple entries with \"application_name\" set to \"%s\" found in \"pg_stat_replication\""),
node_name);
log_hint(_("verify that a unique node name is configured for each node"));
log_verbose(LOG_WARNING, _("unexpected number of tuples (%i) returned"), PQntuples(res));
termPQExpBuffer(&query);
PQclear(res);
return NODE_ATTACHED_UNKNOWN;
}
if (PQntuples(res) == 0)
{
log_warning(_("node \"%s\" not found in \"pg_stat_replication\""), node_name);
c = atoi(PQgetvalue(res, 0, 0));
PQclear(res);
termPQExpBuffer(&query);
PQclear(res);
if (c == 0)
{
log_verbose(LOG_WARNING, _("node \"%s\" not found in \"pg_stat_replication\""), node_name);
return NODE_DETACHED;
}
/*
* If the connection is not a superuser or member of pg_read_all_stats, we
* won't be able to retrieve the "state" column, so we'll assume
* the node is attached.
*/
if (connection_has_pg_monitor_role(conn, "pg_read_all_stats"))
{
const char *state = PQgetvalue(res, 0, 1);
if (node_state != NULL)
{
int state_len = strlen(state);
*node_state = palloc0(state_len + 1);
strncpy(*node_state, state, state_len);
}
if (strcmp(state, "streaming") != 0)
{
log_warning(_("node \"%s\" attached in state \"%s\""),
node_name,
state);
PQclear(res);
return NODE_NOT_ATTACHED;
}
}
else if (node_state != NULL)
{
*node_state = palloc0(1);
*node_state[0] = '\0';
}
PQclear(res);
if (c > 1)
log_verbose(LOG_WARNING, _("multiple entries with \"application_name\" set to \"%s\" found in \"pg_stat_replication\""),
node_name);
return NODE_ATTACHED;
}

View File

@@ -119,14 +119,9 @@ typedef enum
typedef enum
{
/* unable to query "pg_stat_replication" or other error */
NODE_ATTACHED_UNKNOWN = -1,
/* node has record in "pg_stat_replication" and state is not "streaming" */
NODE_ATTACHED,
/* node has record in "pg_stat_replication" but state is not "streaming" */
NODE_NOT_ATTACHED,
/* node has no record in "pg_stat_replication" */
NODE_DETACHED
NODE_DETACHED,
NODE_ATTACHED
} NodeAttached;
typedef enum
@@ -418,7 +413,6 @@ void conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list);
void param_set(t_conninfo_param_list *param_list, const char *param, const char *value);
void param_set_ine(t_conninfo_param_list *param_list, const char *param, const char *value);
char *param_get(t_conninfo_param_list *param_list, const char *param);
bool validate_conninfo_string(const char *conninfo_str, char **errmsg);
bool parse_conninfo_string(const char *conninfo_str, t_conninfo_param_list *param_list, char **errmsg, bool ignore_local_params);
char *param_list_to_string(t_conninfo_param_list *param_list);
char *normalize_conninfo_string(const char *conninfo_str);
@@ -453,7 +447,7 @@ TimeLineHistoryEntry *get_timeline_history(PGconn *repl_conn, TimeLineID tli);
/* user/role information functions */
bool can_execute_pg_promote(PGconn *conn);
bool connection_has_pg_monitor_role(PGconn *conn, const char *subrole);
bool connection_has_pg_settings(PGconn *conn);
bool is_replication_role(PGconn *conn, char *rolname);
bool is_superuser_connection(PGconn *conn, t_connection_user *userinfo);
@@ -496,7 +490,6 @@ bool get_local_node_record(PGconn *conn, int node_id, t_node_info *node_info);
bool get_primary_node_record(PGconn *conn, t_node_info *node_info);
bool get_all_node_records(PGconn *conn, NodeInfoList *node_list);
bool get_all_nodes_count(PGconn *conn, int *count);
void get_downstream_node_records(PGconn *conn, int node_id, NodeInfoList *nodes);
void get_active_sibling_node_records(PGconn *conn, int node_id, int upstream_node_id, NodeInfoList *node_list);
bool get_child_nodes(PGconn *conn, int node_id, NodeInfoList *node_list);
@@ -596,7 +589,7 @@ bool get_replication_info(PGconn *conn, t_server_type node_type, ReplInfo *repl
int get_replication_lag_seconds(PGconn *conn);
TimeLineID get_node_timeline(PGconn *conn, char *timeline_id_str);
void get_node_replication_stats(PGconn *conn, t_node_info *node_info);
NodeAttached is_downstream_node_attached(PGconn *conn, char *node_name, char **node_state);
NodeAttached is_downstream_node_attached(PGconn *conn, char *node_name);
void set_upstream_last_seen(PGconn *conn, int upstream_node_id);
int get_upstream_last_seen(PGconn *conn, t_server_type node_type);

View File

@@ -109,56 +109,9 @@ create_dir(const char *path)
bool
set_dir_permissions(const char *path, int server_version_num)
set_dir_permissions(const char *path)
{
struct stat stat_buf;
bool no_group_access =
(server_version_num != UNKNOWN_SERVER_VERSION_NUM) &&
(server_version_num < 110000);
/*
* At this point the path should exist, so this check is very
* much just-in-case.
*/
if (stat(path, &stat_buf) != 0)
{
if (errno == ENOENT)
{
log_warning(_("directory \"%s\" does not exist"), path);
}
else
{
log_warning(_("could not read permissions of directory \"%s\""),
path);
log_detail("%s", strerror(errno));
}
return false;
}
/*
* If mode is not 0700 or 0750, attempt to change.
*/
if ((no_group_access == true && (stat_buf.st_mode & (S_IRWXG | S_IRWXO)))
|| (no_group_access == false && (stat_buf.st_mode & (S_IWGRP | S_IRWXO))))
{
/*
* Currently we default to 0700.
* There is no facility to override this directly,
* but the user can manually create the directory with
* the desired permissions.
*/
if (chmod(path, 0700) != 0) {
log_error(_("unable to change permissions of directory \"%s\""), path);
log_detail("%s", strerror(errno));
return false;
}
return true;
}
/* Leave as-is */
return true;
return (chmod(path, 0700) != 0) ? false : true;
}
@@ -350,7 +303,7 @@ create_pg_dir(const char *path, bool force)
switch (check_dir(path))
{
case DIR_NOENT:
/* Directory does not exist, attempt to create it. */
/* directory does not exist, attempt to create it */
log_info(_("creating directory \"%s\"..."), path);
if (!create_dir(path))
@@ -361,23 +314,14 @@ create_pg_dir(const char *path, bool force)
}
break;
case DIR_EMPTY:
/*
* Directory exists but empty, fix permissions and use it.
*
* Note that at this point the caller might not know the server
* version number, so in this case "set_dir_permissions()" will
* accept 0750 as a valid setting. As this is invalid in Pg10 and
* earlier, the caller should call "set_dir_permissions()" again
* when it has the number.
*
* We need to do the permissions check here in any case to catch
* fatal permissions early.
*/
/* exists but empty, fix permissions and use it */
log_info(_("checking and correcting permissions on existing directory \"%s\""),
path);
if (!set_dir_permissions(path, UNKNOWN_SERVER_VERSION_NUM))
if (!set_dir_permissions(path))
{
log_error(_("unable to change permissions of directory \"%s\""), path);
log_detail("%s", strerror(errno));
return false;
}
break;

View File

@@ -35,7 +35,7 @@ typedef enum
} PgDirState;
extern int mkdir_p(char *path, mode_t omode);
extern bool set_dir_permissions(const char *path, int server_version_num);
extern bool set_dir_permissions(const char *path);
extern DataDirState check_dir(const char *path);
extern bool create_dir(const char *path);

View File

@@ -95,7 +95,6 @@ clean:
rm -f repmgr.html
rm -f repmgr-A4.pdf
rm -f repmgr-US.pdf
rm -f html/*
maintainer-clean:
rm -rf html

View File

@@ -22,9 +22,6 @@
&repmgr; 5 is fundamentally the same code base as &repmgr; 4, but provides
support for the revised replication configuration mechanism in PostgreSQL 12.
</para>
<para>
Support for PostgreSQL 9.3 is no longer available from &repmgr; 5.2.
</para>
</note>
<para>
&repmgr; 3.x builds on the improved replication facilities added
@@ -350,9 +347,6 @@
and earlier) with the absolute path to the WAL directory in <varname>pg_basebackup_options</varname>.
For more details see <xref linkend="cloning-advanced-pg-basebackup-options"/>.
</para>
<para>
In &repmgr; 5.2 and later, this setting will also be honoured when cloning from Barman.
</para>
</sect2>
<sect2 id="faq-repmgr-events-no-fkey" xreflabel="No foreign key on node_id in repmgr.events">
@@ -379,22 +373,6 @@
</para>
</sect2>
<sect2 id="faq-repmgr-exclude-metadata-from-dump" xreflabel="Excluding repmgr metadata from pg_dump output">
<title>How can I exclude &repmgr; metadata from <application>pg_dump</application> output?</title>
<para>
Beginning with &repmgr; 5.2, the metadata tables associated with the &repmgr; extension
(<literal>repmgr.nodes</literal>, <literal>repmgr.events</literal> and <literal>repmgr.monitoring_history</literal>)
have been marked as dumpable as they contain configuration and user-generated data.
</para>
<para>
To exclude these from <application>pg_dump</application> output, add the flag <option>--exclude-schema=repmgr</option>.
</para>
<para>
To exclude individual &repmgr; metadata tables from <application>pg_dump</application> output, add the flag
e.g. <option>--exclude-table=repmgr.monitoring_history</option>. This flag can be provided multiple times
to exclude individual tables,
</para>
</sect2>
</sect1>

View File

@@ -471,7 +471,7 @@ repmgr96-4.1.1-0.0git320.g5113ab0.1.el7.x86_64.rpm</programlisting>
<title>Debian/Ubuntu</title>
<para>
An archive of old packages (<literal>3.3.2</literal> and later) for Debian/Ubuntu-based systems is available here:
<ulink url="https://apt-archive.postgresql.org/">https://apt-archive.postgresql.org/</ulink>
<ulink url="http://atalia.postgresql.org/morgue/r/repmgr/">http://atalia.postgresql.org/morgue/r/repmgr/</ulink>
</para>
</sect2>

View File

@@ -17,285 +17,7 @@
<!-- remember to update the release date in ../repmgr_version.h.in -->
<sect1 id="release-5.2.1">
<title id="release-current">Release 5.2.1</title>
<para><emphasis>Mon 7 December, 2020</emphasis></para>
<para>
&repmgr; 5.2.1 is a minor release.
</para>
<sect2>
<title>Improvements</title>
<para>
<itemizedlist>
<listitem>
<para>
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
option <option>--recovery-min-apply-delay</option> added, overriding any
setting present in <filename>repmgr.conf</filename>.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Bug fixes</title>
<para>
<itemizedlist>
<listitem>
<para>
Configuration: fix parsing of <option>replication_type</option> configuration parameter. GitHub #672.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
handle case where <filename>postgresql.auto.conf</filename> is absent on the
source node.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
in PostgreSQL 11 and later, an existing data directory's permissions
will not be changed to <option>0700</option> if they are already set to
<option>0750</option>.
</para>
</listitem>
<listitem>
<para>
&repmgrd;: prevent termination when local node not available and
<option>standby_disconnect_on_failover</option> is set. GitHub #675.
</para>
</listitem>
<listitem>
<para>
&repmgrd;: ensure <option>reconnect_interval</option> is correctly handled.
GitHub #673.
</para>
</listitem>
<listitem>
<para>
<command>repmgr witness --help</command>: fix <command>witness unregister</command>
description. GitHub #676.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
<sect1 id="release-5.2.0">
<title>Release 5.2.0</title>
<para><emphasis>Thu 22 October, 2020</emphasis></para>
<para>
&repmgr; 5.2.0 is a major release.
</para>
<para>
This release provides support for <ulink url="https://www.postgresql.org/docs/13/release-13.html">PostgreSQL 13</ulink>, released in September 2020.
</para>
<para>
This release removes support for PostgreSQL 9.3, which was
<ulink url="https://www.postgresql.org/docs/9.3/release-9-3-25.html">designated EOL in November 2018</ulink>.
</para>
<sect2>
<title>General improvements</title>
<para>
<itemizedlist>
<listitem>
<para>
Configuration: support <command>include</command>, <command>include_dir</command> and
<command>include_if_exists</command> directives (see <xref linkend="configuration-file-include-directives"/>).
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
Improve sanity check failure log output from the demotion candidate.
</para>
<para>
If database connection configuration is not consistent across all nodes, it's
possible remote &repmgr; invocations (e.g. during switchover, from the promotion candidate
to the demotion candidate) will not be able to connect to the database. This will
now be explicitly reported as a database connection failure, rather than as a failure
of the respective sanity check.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-cluster-crosscheck">repmgr cluster crosscheck</link> /
<link linkend="repmgr-cluster-matrix">repmgr cluster matrix</link>:
improve text mode output format, in particular so that node identifiers of arbitrary length are
displayed correctly.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-primary-unregister">repmgr primary unregister</link>:
the <option>--force</option> can be provided to unregister an active primary node, provided
it has no registered standby nodes.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-clone">repmgr standby clone</link>: new option
<option>--verify-backup</option> to run PostgreSQL's
<ulink url="https://www.postgresql.org/docs/13/app-pgverifybackup.html">pg_verifybackup</ulink>
utility after cloning a standby to verify the integrity of the copied data
(PostgreSQL 13 and later).
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
when cloning from Barman, setting <option>--waldir</option>
(PostgreSQL 9.6 and earlier: <option>--xlogdir</option>) in
<option>pg_basebackup_options</option> will cause &repmgr; to create
a WAL directory outside of the main data directory and symlink
it from there, in the same way as would happen when cloning
using <application>pg_basebackup</application>.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-follow">repmgr standby follow</link>:
In PostgreSQL 13 and later, a standby no longer requires a restart to
follow a new upstream node.
</para>
<para>
The old behaviour (always restarting the standby to follow a new node)
can be restored by setting the configuration file parameter
<varname>standby_follow_restart</varname> to <literal>true</literal>.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-node-rejoin">repmgr node rejoin</link>:
enable a node to attach to a target node even the target node
has a lower timeline (PostgreSQL 9.6 and later).
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-node-rejoin">repmgr node rejoin</link>:
in PostgreSQL 13 and later, support <application>pg_rewind</application>'s
ability to automatically run crash recovery on a PostgreSQL instance
which was not shut down cleanly.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-node-check">repmgr node check</link>:
option <option>--db-connection</option> added to check if &repmgr;
can connect to the database on the local node.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-node-check">repmgr node check</link>:
report database connection error if the <option>--optformat</option> was provided.
</para>
</listitem>
<listitem>
<para>
Improve handling of pg_control read errors.
</para>
</listitem>
<listitem>
<para>
It is now possible to dump the contents of &repmgr; metadata tables with
<application>pg_dump</application>.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>repmgrd enhancements</title>
<para>
<itemizedlist>
<listitem>
<para>
Following additional parameters can be provided to <varname>failover_validation_command</varname>:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara><literal>%n</literal>: node ID</simpara>
</listitem>
<listitem>
<simpara><literal>%a</literal>: node name</simpara>
</listitem>
<listitem>
<simpara><literal>%v</literal>: number of visible nodes</simpara>
</listitem>
<listitem>
<simpara><literal>%u</literal>: number of shared upstream nodes</simpara>
</listitem>
<listitem>
<simpara><literal>%t</literal>: total number of nodes</simpara>
</listitem>
</itemizedlist>
</para>
</listitem>
<listitem>
<para>
Configuration option <varname>always_promote</varname> (default: <literal>false</literal>)
to control whether a node should be promoted if the &repmgr; metadata is not up-to-date
on that node.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Bug fixes</title>
<para>
<itemizedlist>
<listitem>
<para>
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
fix issue with cloning from Barman where the tablespace mapping file was
not flushed to disk before attempting to retrieve files from Barman. GitHub #650.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-node-rejoin">repmgr node rejoin</link>:
ensure that when verifying a standby node has attached to its upstream, the
node has started streaming before confirming the success of the rejoin operation.
</para>
</listitem>
<listitem>
<para>
&repmgrd;: ensure primary connection is reset if same as upstream. GitHub #633.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
<sect1 id="release-5.1.0">
<sect1 id="release-5.1-0">
<title>Release 5.1.0</title>
<para><emphasis>Mon 13 April, 2020</emphasis></para>
@@ -489,7 +211,7 @@
</sect1>
<sect1 id="release-5.0">
<title>Release 5.0</title>
<title id="release-current">Release 5.0</title>
<para><emphasis>Tue 15 October, 2019</emphasis></para>
<para>

View File

@@ -46,7 +46,6 @@
<para>
WAL management on the primary becomes much easier as there's no need
to use replication slots, and <varname>wal_keep_segments</varname>
(PostgreSQL 13 and later: <varname>wal_keep_size</varname>)
does not need to be set.
</para>
</listitem>
@@ -148,15 +147,6 @@ description = "Main cluster"
section in <command>man 5 ssh_config</command> for more details.
</simpara>
</tip>
<para>
If you wish to place WAL files in a location outside the main
PostgreSQL data directory, set <option>--waldir</option>
(PostgreSQL 9.6 and earlier: <option>--xlogdir</option>) in
<option>pg_basebackup_options</option> to the target directory
(must be an absolute filepath). &repmgr; will create and
symlink to this directory in exactly the same way
<application>pg_basebackup</application> would.
</para>
<para>
It's now possible to clone a standby from Barman, e.g.:
<programlisting>
@@ -244,8 +234,7 @@ description = "Main cluster"
that any standby connected to the primary using a replication slot will always
be able to retrieve the required WAL files. This removes the need to manually
manage WAL file retention by estimating the number of WAL files that need to
be maintained on the primary using <varname>wal_keep_segments</varname>
(PostgreSQL 13 and later: <varname>wal_keep_size</varname>).
be maintained on the primary using <varname>wal_keep_segments</varname>.
Do however be aware that if a standby is disconnected, WAL will continue to
accumulate on the primary until either the standby reconnects or the replication
slot is dropped.
@@ -449,13 +438,6 @@ description = "Main cluster"
WAL directory. Any WALs generated during the cloning process will be copied here, and
a symlink will automatically be created from the main data directory.
</para>
<tip>
<para>
The <literal>--waldir</literal> (<literal>--xlogdir</literal>) option,
if present in <varname>pg_basebackup_options</varname>, will be honoured by &repmgr;
when cloning from Barman (&repmgr; 5.2 and later).
</para>
</tip>
<para>
See the <ulink url="https://www.postgresql.org/docs/current/app-pgbasebackup.html">PostgreSQL pg_basebackup documentation</ulink>
for more details of available options.

View File

@@ -80,51 +80,6 @@ conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlistin
</para>
</note>
<sect3 id="configuration-file-include-directives" xreflabel="configuration file include directives">
<title>Configuration file include directives</title>
<indexterm>
<primary>repmgr.conf</primary>
<secondary>include directives</secondary>
</indexterm>
<para>
From &repmgr; 5.2, the configuration file can contain the following include directives:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara>
<option>include</option>: include the specified file,
either as an absolute path or path relative to the current file
</simpara>
</listitem>
<listitem>
<simpara>
<option>include_if_exists</option>: include the specified file.
The file is specified as an absolute path or path relative to the current file.
However, if it does not exist, an error will not be raised.
</simpara>
</listitem>
<listitem>
<simpara>
<option>include_dir</option>: include files in the specified directory
which have the <filename>.conf</filename> suffix.
The directory is specified either as an absolute path or path
relative to the current file
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
These behave in exactly the same way as the PostgreSQL configuration file processing;
see the <ulink url="https://www.postgresql.org/docs/current/config-setting.html#CONFIG-INCLUDES">PostgreSQL documentation</ulink>
for additional details.
</para>
</sect3>
</sect2>
@@ -227,14 +182,6 @@ conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlistin
</itemizedlist>
</para>
<para>
In examples provided in this documentation, it is assumed the configuration file is located
at <filename>/etc/repmgr.conf</filename>. If &repmgr; is installed from a package, the
configuration file will probably be located at another location specified by the packager;
see appendix <xref linkend="appendix-packages"/> for configuration file locations in
different packaging systems.
</para>
<para>
Note that if a file is explicitly specified with <literal>-f/--config-file</literal>,
an error will be raised if it is not found or not readable, and no attempt will be made to
@@ -255,61 +202,6 @@ conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlistin
<filename>/path/to/repmgr.conf</filename>).
</para>
</note>
</sect2>
<sect2 id="configuration-file-postgresql-major-upgrades" xreflabel="configuration file and PostgreSQL major version upgrades">
<title>Configuration file and PostgreSQL major version upgrades</title>
<indexterm>
<primary>repmgr.conf</primary>
<secondary>ostgreSQL major version upgrades</secondary>
</indexterm>
<para>
When upgrading the PostgreSQL cluster to a new major version, <filename>repmgr.conf</filename>
will probably needed to be updated.
</para>
<para>
Usually <option>pg_bindir</option> and <option>data_directory</option> will need to be modified,
particularly if the default package locations are used, as these usually change.
</para>
<para>
It's also possible the location of <filename>repmgr.conf</filename> itself will change
(e.g. from <filename>/etc/repmgr/11/repmgr.conf</filename> to <filename>/etc/repmgr/12/repmgr.conf</filename>).
This is stored as part of the &repmgr; metadata and is used by &repmgr; to execute &repmgr; remotely
(e.g. during a <link linkend="performing-switchover">switchover operation</link>).
</para>
<para>
If the content and/or location of <filename>repmgr.conf</filename> has changed, the &repmgr; metadata
needs to be updated to reflect this. The &repmgr; metadata can be updated on each node with:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara>
<link linkend="repmgr-primary-register">
<command>repmgr primary register --force -f /path/to/repmgr.conf</command>
</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="repmgr-standby-register">
<command>repmgr standby register --force -f /path/to/repmgr.conf</command>
</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="repmgr-witness-register">
<command>repmgr witness register --force -f /path/to/repmgr.conf -h primary_host</command>
</link>
</simpara>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>

View File

@@ -127,31 +127,8 @@ node2:5432:repmgr:repmgr:foo
node2:5432:replication:repluser:foo
node3:5432:repmgr:repmgr:foo
node3:5432:replication:repluser:foo</programlisting>
If you are planning to use the <option>-S</option>/<option>--superuser</option> option,
there must also be an entry enabling the superuser to connect to the &repmgr; database.
Assuming the superuser is <literal>postgres</literal>, the file would look like this:
<programlisting>
node1:5432:repmgr:repmgr:foo
node1:5432:repmgr:postgres:foo
node1:5432:replication:repluser:foo
node2:5432:repmgr:repmgr:foo
node2:5432:repmgr:postgres:foo
node2:5432:replication:repluser:foo
node3:5432:repmgr:repmgr:foo
node3:5432:repmgr:postgres:foo
node3:5432:replication:repluser:foo</programlisting>
</para>
<para>
The <filename>~/.pgpass</filename> file can be simplified with the use of wildcards if
there is no requirement to restrict provision of passwords to particular hosts, ports
or databases. The preceding file could then be formatted like this:
<programlisting>
*:*:*:repmgr:foo
*:*:*:postgres:foo
</programlisting>
</para>
<note>
<para>
It's possible to specify an alternative location for the <filename>~/.pgpass</filename> file, either via
@@ -163,11 +140,6 @@ node3:5432:replication:repluser:foo</programlisting>
location on all nodes, as when connecting to a remote node, the file referenced is the one on the
local node.
</para>
<para>
Additionally, you <emphasis>must</emphasis> specify the passfile location in <filename>repmgr.conf</filename>
with the <option>passfile</option> option so &repmgr; can write the correct path when creating the
<option>primary_conninfo</option> parameter for replication configuration on standbys.
</para>
</note>
</sect2>

View File

@@ -270,7 +270,7 @@
<varlistentry>
<term><option>wal_keep_segments</option> / <option>wal_keep_size</option></term>
<term><option>wal_keep_segments</option></term>
<listitem>
@@ -279,36 +279,25 @@
<secondary>PostgreSQL configuration</secondary>
</indexterm>
<indexterm>
<primary>wal_keep_size</primary>
<secondary>PostgreSQL configuration</secondary>
</indexterm>
<para>
Normally there is no need to set <option>wal_keep_segments</option>
(PostgreSQL 13 and later: <varname>wal_keep_size</varname>; default: <literal>0</literal>),
as it is <emphasis>not</emphasis> a reliable way of ensuring that all required WAL
segments are available to standbys. Replication slots and/or an archiving solution
such as Barman are recommended to ensure standbys have a reliable
Normally there is no need to set <option>wal_keep_segments</option> (default: <literal>0</literal>), as it
is <emphasis>not</emphasis> a reliable way of ensuring that all required WAL segments are available to standbys.
Replication slots and/or an archiving solution such as Barman are recommended to ensure standbys have a reliable
source of WAL segments at all times.
</para>
<para>
The only reason ever to set <option>wal_keep_segments</option> / <option>wal_keep_size</option>
is you have you have configured <option>pg_basebackup_options</option>
The only reason ever to set <option>wal_keep_segments</option> is you have
you have configured <option>pg_basebackup_options</option>
in <filename>repmgr.conf</filename> to include the setting <literal>--wal-method=fetch</literal>
(PostgreSQL 9.6 and earlier: <literal>--xlog-method=fetch</literal>)
<emphasis>and</emphasis> you have <emphasis>not</emphasis> set <option>restore_command</option>
in <filename>repmgr.conf</filename> to fetch WAL files from a reliable source such as Barman,
in which case you'll need to set <option>wal_keep_segments</option>
to a sufficiently high number to ensure that all WAL files required by the standby
are retained. However we do not recommend WAL retention in this way.
are retained. However we do not recommend managing replication in this way.
</para>
<para>
PostgreSQL documentation: <ulink url="https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-WAL-KEEP-SEGMENTS">wal_keep_segments</ulink>.
<!--
PostgreSQL documentation: <ulink url="https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-WAL-KEEP-SIZE">wal_keep_size</ulink>.
-->
</para>
</listitem>
</varlistentry>

View File

@@ -14,7 +14,7 @@
</para>
<para>
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 9.4. See
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 9.3. See
section <link linkend="install-compatibility-matrix">&repmgr; compatibility matrix</link>
for an overview of version compatibility.
</para>
@@ -115,7 +115,7 @@
<row>
<entry>
&repmgr; 5.2
&repmgr; 5.x
</entry>
<entry>
YES
@@ -123,42 +123,11 @@
<entry>
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
</entry>
<entry>
9.4, 9.5, 9.6, 10, 11, 12, 13
</entry>
</row>
<row>
<entry>
&repmgr; 5.1
</entry>
<entry>
NO
</entry>
<entry>
<link linkend="release-5.1.0">5.1.0</link> (2020-04-13)
</entry>
<entry>
9.3, 9.4, 9.5, 9.6, 10, 11, 12
</entry>
</row>
<row>
<entry>
&repmgr; 5.0
</entry>
<entry>
NO
</entry>
<entry>
<link linkend="release-5.0">5.0</link> (2019-10-15)
</entry>
<entry>
9.3, 9.4, 9.5, 9.6, 10, 11, 12
</entry>
</row>
<row>
<entry>
&repmgr; 4.x
@@ -224,49 +193,52 @@
<sect2 id="install-postgresql-93-94">
<title>PostgreSQL 9.4 support</title>
<title>PostgreSQL 9.3 and 9.4 support</title>
<indexterm>
<primary>PostgreSQL 9.4</primary>
<primary>PostgreSQL 9.3</primary>
<secondary>repmgr support</secondary>
</indexterm>
<para>
Note that some &repmgr; functionality is not available in PostgreSQL 9.4:
Note that some &repmgr; functionality is not available in PostgreSQL 9.3 and PostgreSQL 9.4:
</para>
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<para>
In PostgreSQL 9.4, <command>pg_rewind</command> is not part of the core
PostgreSQL 9.3 does not support replication slots, so corresponding &repmgr; functionality
is not available.
</para>
</listitem>
<listitem>
<para>
In PostgreSQL 9.3 and PostgreSQL 9.4, <command>pg_rewind</command> is not part of the core
distribution. <command>pg_rewind</command> will need to be compiled separately to be able
to use any &repmgr; functionality which takes advantage of it.
</para>
</listitem>
</itemizedlist>
<warning>
<important>
<para>
PostgreSQL 9.3 has reached the end of its community support period (final release was
<ulink url="https://www.postgresql.org/docs/9.3/release-9-3-25.html">9.3.25</ulink>
in November 2018) and will no longer be updated with security or bugfixes.
</para>
<para>
Beginning with &repmgr; 5.2, &repmgr; no longer supports PostgreSQL 9.3.
</para>
<para>
PostgreSQL 9.4 has reached the end of its community support period (final release was
<ulink url="https://www.postgresql.org/docs/9.4/release-9-4-26.html">9.4.26</ulink>
in February 2020) and will no longer be updated with security or bugfixes.
</para>
<para>
We recommend that users of these versions migrate to a supported PostgreSQL version
We recommend that users of these versions migrate to a recent PostgreSQL version
as soon as possible.
</para>
<para>
For further details, see the <ulink url="https://www.postgresql.org/support/versioning/">PostgreSQL Versioning Policy</ulink>.
</para>
</warning>
</important>
</sect2>

View File

@@ -131,46 +131,6 @@
</para>
</refsect1>
<refsect1>
<title>Additional checks</title>
<para>
Several checks are provided for diagnostic purposes and are not
included in the general output:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara>
<option>--db-connection</option>: checks if &repmgr; can connect to the
database on the local node.
</simpara>
<simpara>
This option is particularly useful in combination with <command>SSH</command>, as
it can be used to troubleshoot connection issues encountered when &repmgr; is
executed remotely (e.g. during a switchover operation).
</simpara>
</listitem>
<listitem>
<simpara>
<option>--replication-config-owner</option>: checks if the file containing replication
configuration (PostgreSQL 12 and later: <filename>postgresql.auto.conf</filename>;
PostgreSQL 11 and earlier: <filename>recovery.conf</filename>) is
owned by the same user who owns the data directory.
</simpara>
<simpara>
Incorrect ownership of these files (e.g. if they are owned by <literal>root</literal>)
will cause operations which need to update the replication configuration
(e.g. <link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link>
or <link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>)
to fail.
</simpara>
</listitem>
</itemizedlist>
</para>
</refsect1>
<refsect1>
<title>Connection options</title>
<para>

View File

@@ -43,12 +43,7 @@
<programlisting>
repmgr node rejoin -d '$conninfo'</programlisting>
where <literal>$conninfo</literal> is the PostgreSQL <literal>conninfo</literal> string of the
<emphasis>current</emphasis> primary node (or that of any reachable node in the cluster, but
<emphasis>not</emphasis> the local node). This is so that &repmgr; can fetch up-to-date information
about the current state of the cluster.
</para>
<para>
where <literal>$conninfo</literal> is the conninfo string of any reachable node in the cluster.
<filename>repmgr.conf</filename> for the stopped node *must* be supplied explicitly if not
otherwise available.
</para>
@@ -76,7 +71,7 @@
</para>
<para>
It is only necessary to provide the <application>pg_rewind</application> path
if using PostgreSQL 9.4, and <application>pg_rewind</application>
if using PostgreSQL 9.3 or 9.4, and <application>pg_rewind</application>
is not installed in the PostgreSQL <filename>bin</filename> directory.
</para>
</listitem>
@@ -212,18 +207,9 @@
a standby to the current primary, not another standby.
</para>
<para>
The node's PostgreSQL instance must have been shut down cleanly. If this was not the
case, it will need to be started up until it has reached a consistent recovery point,
then shut down cleanly.
</para>
<para>
In PostgreSQL 13 and later, this will be done automatically
if the <option>--force-rewind</option> is provided (even if an actual rewind
is not necessary).
</para>
<para>
With PostgreSQL 12 and earlier, PostgreSQL will need to
be started and shut down manually; see below for the best way to do this.
The node must have been shut down cleanly; if this was not the case, it will
need to be manually started (remove any existing <filename>recovery.conf</filename> file first)
until it has reached a consistent recovery point, then shut down cleanly.
</para>
<tip>
<para>
@@ -235,14 +221,11 @@
rm -f /var/lib/pgsql/data/recovery.conf
postgres --single -D /var/lib/pgsql/data/ &lt; /dev/null</programlisting>
</para>
<para>
Note that <filename>standby.signal</filename> (PostgreSQL 11 and earlier:
<filename>recovery.conf</filename>) <emphasis>must</emphasis> be removed
from the data directory for PostgreSQL to be able to start in single
user mode.
</para>
</tip>
<para>
&repmgr; will attempt to verify whether the node can rejoin as-is, or whether
<command>pg_rewind</command> must be used (see following section).
</para>
</refsect1>
<refsect1 id="repmgr-node-rejoin-pg-rewind" xreflabel="Using pg_rewind">
@@ -258,7 +241,7 @@
<command>repmgr node rejoin</command> can optionally use <command>pg_rewind</command> to re-integrate a
node which has diverged from the rest of the cluster, typically a failed primary.
<command>pg_rewind</command> is available in PostgreSQL 9.5 and later as part of the core distribution,
and can be installed from external sources for PostgreSQL 9.4.
and can be installed from external sources for PostgreSQL 9.3 and 9.4.
</para>
<note>
<para>
@@ -300,15 +283,7 @@
to execute <command>pg_rewind</command> to ensure the node can be rejoined successfully.
</para>
<refsect2 id="repmgr-node-rejoin-pg-rewind-config-files" xreflabel="pg_rewind and configuration files">
<title><command>pg_rewind</command> and configuration file retention</title>
<indexterm>
<primary>pg_rewind</primary>
<secondary>configuration file retention</secondary>
</indexterm>
<important>
<para>
Be aware that if <command>pg_rewind</command> is executed and actually performs a
rewind operation, any configuration files in the PostgreSQL data directory will be
@@ -316,27 +291,17 @@
</para>
<para>
To prevent this happening, provide a comma-separated list of files to retain
using the <option>--config-file</option> command line option; the specified files
using the <literal>--config-file</literal> command line option; the specified files
will be archived in a temporary directory (whose parent directory can be specified with
<option>--config-archive-dir</option>, default: <filename>/tmp</filename>)
and restored once the rewind operation is complete.
<literal>--config-archive-dir</literal>) and restored once the rewind operation is
complete.
</para>
</refsect2>
</important>
<refsect2 id="repmgr-node-rejoin-pg-rewind-example" xreflabel="example using repmgr node rejoin and pg_rewind">
<title>Example using <command>repmgr node rejoin</command> and <command>pg_rewind</command></title>
<indexterm>
<primary>pg_rewind</primary>
<secondary>configuration file retention</secondary>
</indexterm>
<para>
Example, first using <option>--dry-run</option>, then actually executing the
<literal>node rejoin command</literal>.
<programlisting>
<para>
Example, first using <literal>--dry-run</literal>, then actually executing the
<literal>node rejoin command</literal>.
<programlisting>
$ repmgr node rejoin -f /etc/repmgr.conf -d 'host=node3 dbname=repmgr user=repmgr' \
--config-files=postgresql.local.conf,postgresql.conf --verbose --force-rewind --dry-run
INFO: replication connection to the rejoin target node was successful
@@ -352,17 +317,17 @@
pg_rewind -D '/var/lib/postgresql/data' --source-server='host=node3 dbname=repmgr user=repmgr'
INFO: prerequisites for executing NODE REJOIN are met</programlisting>
<note>
<para>
If <option>--force-rewind</option> is used with the <option>--dry-run</option> option,
this checks the prerequisites for using <application>pg_rewind</application>, but is
not an absolute guarantee that actually executing <application>pg_rewind</application>
will succeed. See also section <xref linkend="repmgr-node-rejoin-caveats"/> below.
</para>
<note>
<para>
If <option>--force-rewind</option> is used with the <option>--dry-run</option> option,
this checks the prerequisites for using <application>pg_rewind</application>, but is
not an absolute guarantee that actually executing <application>pg_rewind</application>
will succeed. See also section <xref linkend="repmgr-node-rejoin-caveats"/> below.
</para>
</note>
</note>
<programlisting>
<programlisting>
$ repmgr node rejoin -f /etc/repmgr.conf -d 'host=node3 dbname=repmgr user=repmgr' \
--config-files=postgresql.local.conf,postgresql.conf --verbose --force-rewind
NOTICE: pg_rewind execution required for this node to attach to rejoin target node 3
@@ -374,8 +339,8 @@
NOTICE: starting server using "pg_ctl -l /var/log/postgres/startup.log -w -D '/var/lib/pgsql/data' start"
NOTICE: NODE REJOIN successful
DETAIL: node 2 is now attached to node 3</programlisting>
</para>
</refsect2>
</para>
</refsect1>
<refsect1 id="repmgr-node-rejoin-caveats" xreflabel="Caveats">
@@ -404,11 +369,6 @@
the current standby's PostgreSQL log will contain entries with the text
&quot;<literal>record with incorrect prev-link</literal>&quot;.
</para>
<para>
In PostgreSQL 9.5 and earlier, it is <emphasis>not</emphasis> possible to use
<application>pg_rewind</application> to attach to a target node with a lower
timeline than the local node.
</para>
<para>
We strongly recommend running <command>repmgr node rejoin</command> with the
<option>--dry-run</option> option first. Additionally it might be a good idea
@@ -418,51 +378,6 @@
is running in <option>--dry-run</option> mode.
</para>
<warning>
<para>
In all current PostgreSQL versions (as of September 2020), <application>pg_rewind</application>
contains a corner-case bug which affects standbys in a very specific situation.
</para>
<para>
This situation occurs when a standby was shut down <emphasis>before</emphasis> its
primary node, and an attempt is made to attach this standby to another primary
in the same cluster (following a &quot;split brain&quot; situation where the standby
was connected to the wrong primary). In this case, &repmgr; will correctly determine
that <application>pg_rewind</application> should be executed, however
<application>pg_rewind</application> incorrectly decides that no action is necessary.
</para>
<para>
In this situation, &repmgr; will report something like:
<programlisting>
NOTICE: pg_rewind execution required for this node to attach to rejoin target node 1
DETAIL: rejoin target server's timeline 3 forked off current database system timeline 2 before current recovery point 0/7019C10</programlisting>
but when executed, <application>pg_rewind</application> will report:
<programlisting>
pg_rewind: servers diverged at WAL location 0/7015540 on timeline 2
pg_rewind: no rewind required</programlisting>
and if an attempt is made to attach the standby to the new primary, PostgreSQL logs on the standby
will contain errors like:
<programlisting>
[2020-09-07 15:01:41 UTC] LOG: 00000: replication terminated by primary server
[2020-09-07 15:01:41 UTC] DETAIL: End of WAL reached on timeline 2 at 0/7015540.
[2020-09-07 15:01:41 UTC] LOG: 00000: new timeline 3 forked off current database system timeline 2 before current recovery point 0/7019C10</programlisting>
</para>
<para>
Currently it is not possible to resolve this situation using <application>pg_rewind</application>.
A <ulink url="https://www.postgresql.org/message-id/flat/CABvVfJU-LDWvoz4-Yow3Ay5LZYTuPD7eSjjE4kGyNZpXC6FrVQ@mail.gmail.com">patch</ulink>
has been submitted and will hopefully be included in a forthcoming PostgreSQL minor release.
</para>
<para>
As a workaround, start the primary server the standby was previously attached to,
and ensure the standby can be attached to it. If <application>pg_rewind</application> was actually executed,
it will have copied in the <filename>.history</filename> file from the target primary server; this must
be removed. <command>repmgr node rejoin</command> can then be used to attach the standby to the original
primary. Ensure any changes pending on the primary have propogated to the standby. Then shut down the primary
server <emphasis>first</emphasis>, before shutting down the standby. It should then be possible to
use <command>repmgr node rejoin</command> to attach the standby to the new primary.
</para>
</warning>
</refsect1>
<refsect1>

View File

@@ -60,17 +60,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--force</option></term>
<listitem>
<para>
Forcibly unregister the node if it is registered as an active primary,
as long as it has no registered standbys; or if it is registered as
a primary but running as a standby.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@@ -104,6 +104,14 @@
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara><varname>standby_mode</varname> (always <literal>'on'</literal>)</simpara>
</listitem>
<listitem>
<simpara><varname>recovery_target_timeline</varname> (always <literal>'latest'</literal>)</simpara>
</listitem>
<listitem>
<simpara><varname>primary_conninfo</varname></simpara>
</listitem>
@@ -114,21 +122,6 @@
</itemizedlist>
<para>
For PostgreSQL 11 and earlier, these parameters will also be set:
</para>
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara><varname>standby_mode</varname> (always <literal>'on'</literal>)</simpara>
</listitem>
<listitem>
<simpara><varname>recovery_target_timeline</varname> (always <literal>'latest'</literal>)</simpara>
</listitem>
</itemizedlist>
<para>
The following additional parameters can be specified in <filename>repmgr.conf</filename>
for inclusion in the replication configuration:
@@ -182,10 +175,7 @@
<programlisting>
pg_basebackup_options='--wal-method=fetch'</programlisting>
and ensure that <literal>wal_keep_segments</literal> (PostgreSQL 13 and later:
<literal>wal_keep_size</literal>) is set to an appropriately high value. Note
however that this is not a particularly reliable way of ensuring sufficient
WAL is retained and is not recommended.
and ensure that <literal>wal_keep_segments</literal> is set to an appropriately high value.
See the <ulink url="https://www.postgresql.org/docs/current/app-pgbasebackup.html">
pg_basebackup</ulink> documentation for details.
</para>
@@ -198,25 +188,6 @@
</note>
</refsect1>
<refsect1 id="repmgr-standby-clone-wal-directory">
<title>Placing WAL files into a different directory</title>
<para>
To ensure that WAL files are placed in a directory outside of the main data
directory (e.g. to keep them on a separate disk for performance reasons),
specify the location with <option>--waldir</option>
(PostgreSQL 9.6 and earlier: <option>--xlogdir</option>) in
the <filename>repmgr.conf</filename> parameter <option>pg_basebackup_options</option>,
e.g.:
<programlisting>
pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
This setting will also be honored by &repmgr; when cloning from Barman
(&repmgr; 5.2 and later).
</para>
</refsect1>
<!-- don't rename this id as it may be used in external links -->
<refsect1 id="repmgr-standby-create-recovery-conf">
@@ -260,29 +231,16 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
upstream node if required.
</para>
<para>
The upstream node must be running so the correct replication configuration can be obtained.
</para>
<para>
If the standby is running, the replication configuration will not be written unless the
Note that the upstream node must be running. In PostgreSQL 11 and earlier, an existing
<filename>recovery.conf</filename> will not be overwritten unless the
<option>-F/--force</option> option is provided.
</para>
<tip>
<para>
Execute <command>repmgr standby clone --replication-conf-only --dry-run</command>
to check the prerequisites for creating the recovery configuration,
and display the configuration changes which would be made without actually
making any changes.
</para>
</tip>
<para>
In PostgreSQL 13 and later, the PostgreSQL configuration must be reloaded for replication
configuration changes to take effect.
Execute <command>repmgr standby clone --replication-conf-only --dry-run</command>
to check the prerequisites for creating the recovery configuration,
and display the contents of the configuration which would be added without actually
making any changes.
</para>
<para>
In PostgreSQL 12 and earlier, the PostgreSQL instance must be restarted for replication
configuration changes to take effect.
</para>
</refsect1>
@@ -344,25 +302,6 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--recovery-min-apply-delay</option></term>
<listitem>
<para>
Set PostgreSQL configuration <option>recovery_min_apply_delay</option> parameter
to the provided value.
</para>
<para>
This overrides any <option>recovery_min_apply_delay</option> provided via
<filename>repmgr.conf</filename>.
</para>
<para>
For more details on this parameter, see:
<ulink url="https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-RECOVERY-MIN-APPLY-DELAY">recovery_min_apply_delay</ulink>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-R, --remote-user=USERNAME</option></term>
<listitem>
@@ -378,14 +317,14 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
<para>
Create recovery configuration for a previously cloned instance.
</para>
<para>
In PostgreSQL 12 and later, the replication configuration will be
written to <filename>postgresql.auto.conf</filename>.
</para>
<para>
In PostgreSQL 11 and earlier, the replication configuration will be
written to <filename>recovery.conf</filename>.
</para>
<para>
In PostgreSQL 12 and later, the replication configuration will be
written to <filename>postgresql.auto.conf</filename>.
</para>
</listitem>
</varlistentry>
@@ -431,23 +370,6 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--verify-backup</option></term>
<listitem>
<para>
<!-- update link after Pg13 release -->
Verify a cloned node using the
<ulink url="https://www.postgresql.org/docs/13/app-pgverifybackup.html">pg_verifybackup</ulink>
utility (PostgreSQL 13 and later).
</para>
<para>
This option can currently only be used when cloning directly from an upstream
node.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--without-barman </option></term>
<listitem>

View File

@@ -47,15 +47,7 @@
</para>
<para>
In PostgreSQL 12 and earlier, this command will force a restart of PostgreSQL on the standby node.
</para>
<para>
In PostgreSQL 13 and later, by default this command will signal PostgreSQL to reload its
configuration, which will cause PostgreSQL to follow the new upstream without
a restart. If this behaviour is not desired for whatever reason, the configuration
file parameter <varname>standby_follow_restart</varname> can be set <literal>true</literal>
to always force a restart.
This command will force a restart of PostgreSQL on the standby node.
</para>
<para>

View File

@@ -66,10 +66,10 @@
Both values can be defined in <filename>repmgr.conf</filename>.
</para>
<warning>
<note>
<para>
In PostgreSQL 12 and earlier, if WAL replay is paused on the standby, and not all
WAL files on the standby have been replayed, &repmgr; will not attempt to promote it.
If WAL replay is paused on the standby, and not all WAL files on the standby have been
replayed, &repmgr; will not attempt to promote it.
</para>
<para>
This is because if WAL replay is paused, PostgreSQL itself will not react to a promote command
@@ -81,10 +81,7 @@
Note that if the standby is in archive recovery, &repmgr; will not be able to determine
if more WAL is pending replay, and will abort the promotion attempt if WAL replay is paused.
</para>
<para>
This restriction does <emphasis>not</emphasis> apply to PostgreSQL 13 and later.
</para>
</warning>
</note>
</refsect1>
@@ -98,6 +95,7 @@
NOTICE: promoting standby to primary
DETAIL: promoting server "node2" (ID: 2) using "pg_ctl -l /var/log/postgres/startup.log -w -D '/var/lib/postgres/data' promote"
server promoting
DEBUG: setting node 2 as primary and marking existing primary as failed
NOTICE: STANDBY PROMOTE successful
DETAIL: server "node2" (ID: 2) was successfully promoted to primary</programlisting>
</para>
@@ -172,42 +170,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-F</option></term>
<term><option>--force</option></term>
<listitem>
<para>
Ignore warnings and continue anyway.
</para>
<para>
This option is relevant in the following situations if <option>--siblings-follow</option> was specified:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara>
If one or more sibling nodes was not reachable via SSH, the standby will be promoted anyway.
</simpara>
</listitem>
<listitem>
<simpara>
If the promotion candidate has insufficient free walsenders to accomodate the standbys which will
be attached to it, the standby will be promoted anyway.
</simpara>
</listitem>
<listitem>
<simpara>
If replication slots are in use but the promotion candidate has insufficient free replication slots
to accomodate the standbys which will be attached to it, the standby will be promoted anyway.
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
Note that if the <option>-F</option>/<option>--force</option> option is used when any of the above
situations is encountered, the onus is on the user to manually resolve any resulting issues.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@@ -147,7 +147,7 @@
<para>
Use <application>pg_rewind</application> to reintegrate the old primary if necessary
(and the prerequisites for using <application>pg_rewind</application> are met).
If using PostgreSQL 9.4, and the <application>pg_rewind</application>
If using PostgreSQL 9.3 or 9.4, and the <application>pg_rewind</application>
binary is not installed in the PostgreSQL <filename>bin</filename> directory,
provide its full path. For more details see also <xref linkend="switchover-pg-rewind"/>.
</para>

View File

@@ -63,34 +63,6 @@
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>--dry-run</option></term>
<listitem>
<para>
Check prerequisites but don't actually register the witness
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-F</option>/<option>--force</option></term>
<listitem>
<para>
Overwrite an existing node record
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="repmgr-witness-register-events">
<title>Event notifications</title>
<para>

View File

@@ -26,7 +26,7 @@
<abstract>
<para>
This is the official documentation of &repmgr; &repmgrversion; for
use with PostgreSQL 9.4 - PostgreSQL 13.
use with PostgreSQL 9.3 - PostgreSQL 12.
</para>
<para>
&repmgr; is being continually developed and we strongly recommend using the

View File

@@ -331,12 +331,11 @@
To use this, <option>failover_validation_command</option> in <filename>repmgr.conf</filename>
to a script executable by the <literal>postgres</literal> system user, e.g.:
<programlisting>
failover_validation_command=/path/to/script.sh %n</programlisting>
failover_validation_command=/path/to/script.sh %n %a</programlisting>
</para>
<para>
The <literal>%n</literal> parameter will be replaced with the node ID when the script is
executed. A number of other parameters are also available, see section
&quot;<xref linkend="repmgrd-automatic-failover-configuration-optional"/>&quot; for details.
The <literal>%n</literal> parameter will be replaced with the node ID, and the
<literal>%a</literal> parameter will be replaced by the node name when the script is executed.
</para>
<para>
This script must return an exit code of <literal>0</literal> to indicate the node should promote itself.

View File

@@ -15,13 +15,9 @@
</para>
<para>
&repmgrd; can be configured to provide failover
capability in case the primary or upstream node becomes unreachable, and/or
capability in case the primary upstream node becomes unreachable, and/or
provide monitoring data to the &repmgr; metadatabase.
</para>
<para>
From &repmgr; 4.4, when running on the primary node, &repmgrd; can also monitor
standby disconnections/reconnections (see <xref linkend="repmgrd-primary-child-disconnection"/>).
</para>
<sect1 id="repmgrd-basic-configuration">
<title>repmgrd configuration</title>
@@ -325,7 +321,7 @@
</sect2>
<sect2 id="repmgrd-automatic-failover-configuration-optional" xreflabel="Optional configuration for automatic failover">
<sect2 id="repmgrd-automatic-failover-configuration-optional">
<title>Optional configuration for automatic failover</title>
<para>
@@ -370,8 +366,8 @@
</para>
</note>
<para>
One or more of the following parameter placeholders
may be provided, which will be replaced by repmgrd with the appropriate
One or both of the following parameter placeholders
should be provided, which will be replaced by repmgrd with the appropriate
value:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
@@ -380,15 +376,6 @@
<listitem>
<simpara><literal>%a</literal>: node name</simpara>
</listitem>
<listitem>
<simpara><literal>%v</literal>: number of visible nodes</simpara>
</listitem>
<listitem>
<simpara><literal>%u</literal>: number of shared upstream nodes</simpara>
</listitem>
<listitem>
<simpara><literal>%t</literal>: total number of nodes</simpara>
</listitem>
</itemizedlist>
</para>
<para>
@@ -419,33 +406,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>always_promote</option></term>
<listitem>
<indexterm>
<primary>always_promote</primary>
</indexterm>
<para>
Default: <literal>false</literal>.
</para>
<para>
If <literal>true</literal>, promote the local node even if its
&repmgr; metadata is not up-to-date.
</para>
<para>
Normally &repmgr; expects its metadata (stored in the <varname>repmgr.nodes</varname>
table) to be up-to-date so &repmgrd; can take the correct action during a failover.
However it's possible that updates made on the primary may not
have propagated to the standby (promotion candidate). In this case &repmgrd; will
default to not promoting the standby. This behaviour can be overridden by setting
<option>always_promote</option> to <literal>true</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>standby_disconnect_on_failover</option></term>
@@ -623,8 +583,7 @@ repmgrd_service_stop_command='sudo systemctl repmgr12 stop'
the option <option>monitor_interval_secs</option> (see above).
</para>
<para>
For more details on monitoring, see <xref linkend="repmgrd-monitoring"/>. For information on
monitoring standby disconnections, see <xref linkend="repmgrd-primary-child-disconnection"/>.
For more details on monitoring, see <xref linkend="repmgrd-monitoring"/>.
</para>
</sect2>
@@ -792,12 +751,6 @@ repmgrd_service_stop_command='sudo systemctl repmgr12 stop'
</simpara>
</listitem>
<listitem>
<simpara>
<varname>always_promote</varname>
</simpara>
</listitem>
<listitem>
<simpara>
<varname>promote_command</varname>

View File

@@ -137,7 +137,7 @@ NOTICE: node 3 (node3) paused</programlisting>
If the primary becomes available again (e.g. following a software upgrade), &repmgrd;
will automatically reconnect, e.g.:
<programlisting>
[2019-08-28 12:25:41] [NOTICE] reconnected to upstream node "node1" (ID: 1) after 8 seconds, resuming monitoring</programlisting>
[2019-08-28 12:25:41] [NOTICE] reconnected to upstream node 1 after 8 seconds, resuming monitoring</programlisting>
</para>
<para>
@@ -295,7 +295,7 @@ NOTICE: node 3 (node3) unpaused</programlisting>
[2017-08-29 10:59:37] [HINT] use "repmgr standby promote" to manually promote this node
[2017-08-29 10:59:37] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in degraded state (automatic failover disabled)
[2017-08-29 10:59:53] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in degraded state (automatic failover disabled)
[2017-08-29 11:00:45] [NOTICE] reconnected to upstream node "node1" (ID: 1) after 68 seconds, resuming monitoring
[2017-08-29 11:00:45] [NOTICE] reconnected to upstream node 1 after 68 seconds, resuming monitoring
[2017-08-29 11:00:57] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in normal state (automatic failover disabled)</programlisting>
</para>

View File

@@ -244,15 +244,16 @@
</para>
<para>
<application>pg_rewind</application> has been part of the core PostgreSQL distribution since
version 9.5. Users of PostgreSQL 9.4 will need to manually install it; the source code is available here:
version 9.5. Users of versions 9.3 and 9.4 will need to manually install it; the source code is available here:
<ulink url="https://github.com/vmware/pg_rewind">https://github.com/vmware/pg_rewind</ulink>.
If the <application>pg_rewind</application>
binary is not installed in the PostgreSQL <filename>bin</filename> directory, provide
its full path on the demotion candidate with <option>--force-rewind</option>.
</para>
<para>
Note that building the 9.4 version of <application>pg_rewind</application> requires the PostgreSQL
source code.
Note that building the 9.3/9.4 version of <application>pg_rewind</application> requires the PostgreSQL
source code. Also, PostgreSQL 9.3 does not provide <varname>wal_log_hints</varname>,
meaning data checksums must have been enabled when the database was initialized.
</para>
</sect2>
@@ -342,7 +343,7 @@
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara>
If using PostgreSQL 9.4, you should ensure that the shutdown command
If using PostgreSQL 9.3 or 9.4, you should ensure that the shutdown command
is configured to use PostgreSQL's <varname>fast</varname> shutdown mode (the default in 9.5
and later). If relying on <command>pg_ctl</command> to perform database server operations,
you should include <literal>-m fast</literal> in <varname>pg_ctl_options</varname>

View File

@@ -201,13 +201,9 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
</para>
<tip>
<para>
If the &repmgr; upgrade requires a PostgreSQL restart, combine the &repmgr; upgrade
with a PostgreSQL minor version upgrade, which will require a restart in any case.
</para>
<para>
New PostgreSQL minor versions are usually released every couple of months;
see the <ulink url="https://www.postgresql.org/developer/roadmap/">Roadmap</ulink>
for the current schedule.
If the &repmgr; upgrade requires a PostgreSQL restart, combine the &repmgr; upgrade
with a PostgreSQL minor version upgrade, which will require a restart in any case.
New PostgreSQL minor version are usually released every couple of months.
</para>
</tip>
</sect2>
@@ -312,12 +308,12 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<orderedlist>
<listitem>
<simpara>
converting the <filename>repmgr.conf</filename> configuration files
converting the repmgr.conf configuration files
</simpara>
</listitem>
<listitem>
<simpara>
upgrading the repmgr schema using <command>CREATE EXTENSION</command> (PostgreSQL 12 and earlier)
upgrading the repmgr schema using <command>CREATE EXTENSION</command>
</simpara>
</listitem>
</orderedlist>
@@ -461,31 +457,22 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<para>
Please note that the the conversion script will add an empty
placeholder parameter for <varname>data_directory</varname>, which
is a required parameter from &repmgr; 4. This must be manually modified to contain
the correct data directory.
is a required parameter from &repmgr; 4.
</para>
</sect3>
</sect2>
<sect2>
<title>Upgrading the repmgr schema (PostgreSQL 12 and earlier)</title>
<title>Upgrading the repmgr schema</title>
<para>
Ensure &repmgrd; is not running, or any cron jobs which execute the
<command>repmgr</command> binary.
</para>
<para>
Install the latest &repmgr; package; any <literal>repmgr 3.x</literal> packages
Install <literal>repmgr 4</literal> packages; any <literal>repmgr 3.x</literal> packages
should be uninstalled (if not automatically uninstalled already by your packaging system).
</para>
<sect3>
<title>Upgrading from repmgr 3.1.1 or earlier</title>
<tip>
<simpara>
If you don't care about any data from the existing &repmgr; installation,
(e.g. the contents of the <structname>events</structname> and <structname>monitoring</structname>
tables), the follwing steps can be skipped; proceed to <xref linkend="upgrade-reregister-nodes"/>.
</simpara>
</tip>
<para>
If your repmgr version is 3.1.1 or earlier, you will need to update
the schema to the latest version in the 3.x series (3.3.2) before
@@ -514,37 +501,19 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<para>
In the database used by the existing &repmgr; installation, execute:
<programlisting>
CREATE EXTENSION repmgr FROM unpackaged</programlisting>
CREATE EXTENSION repmgr FROM unpackaged;</programlisting>
</para>
<para>
This will move and convert all objects from the existing schema
into the new, standard <literal>repmgr</literal> schema.
</para>
<note>
<simpara>There must be only one schema matching <literal>repmgr_%</literal> in the
<simpara>there must be only one schema matching <literal>repmgr_%</literal> in the
database, otherwise this step may not work.
</simpara>
</note>
</sect3>
</sect2>
<sect2>
<title>Upgrading the repmgr schema (PostgreSQL 13 and later)</title>
<para>
Beginning with PostgreSQL 13, the <command>CREATE EXTENSION ... FROM unpackaged</command>
syntax is no longer available. In the unlikely event you have ended up with an
installation running PostgreSQL 13 or later and containing the legacy &repmgr;
schema, there is no convenient way of upgrading this; instead you'll just need
to re-register the nodes as detailed in <link linkend="upgrade-reregister-nodes">the following section</link>,
which will create the &repmgr; extension automatically.
</para>
<para>
Any historical data you wish to retain (e.g. the contents of the <structname>events</structname>
and <structname>monitoring</structname> tables) will need to be exported manually.
</para>
</sect2>
<sect2 id="upgrade-reregister-nodes">
<sect3>
<title>Re-register each node</title>
<para>
This is necessary to update the <literal>repmgr</literal> metadata with some additional items.
@@ -554,10 +523,6 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<programlisting>
repmgr primary register -f /etc/repmgr.conf --force</programlisting>
</para>
<para>
If not already present (e.g. after executing <command>CREATE EXTENSION repmgr FROM unpackaged</command>),
the &repmgr; extension will be automatically created by <command>repmgr primary register</command>.
</para>
<para>
On each standby node, execute e.g.
<programlisting>
@@ -570,20 +535,18 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<para>
The original <literal>repmgr_$cluster</literal> schema can be dropped at any time.
</para>
<tip>
<simpara>
If you don't care about any data from the existing &repmgr; installation,
(e.g. the contents of the <structname>events</structname> and <structname>monitoring</structname>
tables), the manual <command>CREATE EXTENSION</command> step can be skipped; just re-register
each node, starting with the primary node, and the <literal>repmgr</literal> extension will be
automatically created.
</simpara>
</tip>
</sect3>
</sect2>
<sect2 id="upgrade-drop-repmgr-cluster-schema">
<title>Drop the legacy repmgr schema</title>
<para>
Once the cluster has been registered with the current &repmgr; version, the legacy
<literal>repmgr_$cluster</literal> schema can be dropped at any time with:
<programlisting>
DROP SCHEMA repmgr_$cluster CASCADE</programlisting>
(substitute <literal>$cluster</literal> with the value of the <varname>clustername</varname>
variable used in &repmgr; 3.x).
</para>
</sect2>
</sect1>
</chapter>

View File

@@ -1,7 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
SELECT pg_catalog.pg_extension_config_dump('repmgr.nodes', '');
SELECT pg_catalog.pg_extension_config_dump('repmgr.events', '');
SELECT pg_catalog.pg_extension_config_dump('repmgr.monitoring_history', '');

View File

@@ -1,192 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
CREATE TABLE repmgr.nodes (
node_id INTEGER PRIMARY KEY,
upstream_node_id INTEGER NULL REFERENCES nodes (node_id) DEFERRABLE,
active BOOLEAN NOT NULL DEFAULT TRUE,
node_name TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN('primary','standby','witness','bdr')),
location TEXT NOT NULL DEFAULT 'default',
priority INT NOT NULL DEFAULT 100,
conninfo TEXT NOT NULL,
repluser VARCHAR(63) NOT NULL,
slot_name TEXT NULL,
config_file TEXT NOT NULL
);
SELECT pg_catalog.pg_extension_config_dump('repmgr.nodes', '');
CREATE TABLE repmgr.events (
node_id INTEGER NOT NULL,
event TEXT NOT NULL,
successful BOOLEAN NOT NULL DEFAULT TRUE,
event_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
details TEXT NULL
);
SELECT pg_catalog.pg_extension_config_dump('repmgr.events', '');
CREATE TABLE repmgr.monitoring_history (
primary_node_id INTEGER NOT NULL,
standby_node_id INTEGER NOT NULL,
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
last_apply_time TIMESTAMP WITH TIME ZONE,
last_wal_primary_location PG_LSN NOT NULL,
last_wal_standby_location PG_LSN,
replication_lag BIGINT NOT NULL,
apply_lag BIGINT NOT NULL
);
CREATE INDEX idx_monitoring_history_time
ON repmgr.monitoring_history (last_monitor_time, standby_node_id);
SELECT pg_catalog.pg_extension_config_dump('repmgr.monitoring_history', '');
CREATE VIEW repmgr.show_nodes AS
SELECT n.node_id,
n.node_name,
n.active,
n.upstream_node_id,
un.node_name AS upstream_node_name,
n.type,
n.priority,
n.conninfo
FROM repmgr.nodes n
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);
CREATE UNIQUE INDEX voting_term_restrict
ON repmgr.voting_term ((TRUE));
CREATE RULE voting_term_delete AS
ON DELETE TO repmgr.voting_term
DO INSTEAD NOTHING;
/* ================= */
/* repmgrd functions */
/* ================= */
/* monitoring functions */
CREATE FUNCTION set_local_node_id(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_local_node_id'
LANGUAGE C STRICT;
CREATE FUNCTION get_local_node_id()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_local_node_id'
LANGUAGE C STRICT;
CREATE FUNCTION standby_set_last_updated()
RETURNS TIMESTAMP WITH TIME ZONE
AS 'MODULE_PATHNAME', 'standby_set_last_updated'
LANGUAGE C STRICT;
CREATE FUNCTION standby_get_last_updated()
RETURNS TIMESTAMP WITH TIME ZONE
AS 'MODULE_PATHNAME', 'standby_get_last_updated'
LANGUAGE C STRICT;
CREATE FUNCTION set_upstream_last_seen(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_upstream_last_seen'
LANGUAGE C STRICT;
CREATE FUNCTION get_upstream_last_seen()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_upstream_last_seen'
LANGUAGE C STRICT;
CREATE FUNCTION get_upstream_node_id()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_upstream_node_id'
LANGUAGE C STRICT;
CREATE FUNCTION set_upstream_node_id(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_upstream_node_id'
LANGUAGE C STRICT;
/* failover functions */
CREATE FUNCTION notify_follow_primary(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'notify_follow_primary'
LANGUAGE C STRICT;
CREATE FUNCTION get_new_primary()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_new_primary'
LANGUAGE C STRICT;
CREATE FUNCTION reset_voting_status()
RETURNS VOID
AS 'MODULE_PATHNAME', 'reset_voting_status'
LANGUAGE C STRICT;
CREATE FUNCTION get_repmgrd_pid()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
LANGUAGE C STRICT;
CREATE FUNCTION get_repmgrd_pidfile()
RETURNS TEXT
AS 'MODULE_PATHNAME', 'get_repmgrd_pidfile'
LANGUAGE C STRICT;
CREATE FUNCTION set_repmgrd_pid(INT, TEXT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_repmgrd_pid'
LANGUAGE C CALLED ON NULL INPUT;
CREATE FUNCTION repmgrd_is_running()
RETURNS BOOL
AS 'MODULE_PATHNAME', 'repmgrd_is_running'
LANGUAGE C STRICT;
CREATE FUNCTION repmgrd_pause(BOOL)
RETURNS VOID
AS 'MODULE_PATHNAME', 'repmgrd_pause'
LANGUAGE C STRICT;
CREATE FUNCTION repmgrd_is_paused()
RETURNS BOOL
AS 'MODULE_PATHNAME', 'repmgrd_is_paused'
LANGUAGE C STRICT;
CREATE FUNCTION get_wal_receiver_pid()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_wal_receiver_pid'
LANGUAGE C STRICT;
/* views */
CREATE VIEW repmgr.replication_status AS
SELECT m.primary_node_id, m.standby_node_id, n.node_name AS standby_name,
n.type AS node_type, n.active, last_monitor_time,
CASE WHEN n.type='standby' THEN m.last_wal_primary_location ELSE NULL END AS last_wal_primary_location,
m.last_wal_standby_location,
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.replication_lag) ELSE NULL END AS replication_lag,
CASE WHEN n.type='standby' THEN
CASE WHEN replication_lag > 0 THEN age(now(), m.last_apply_time) ELSE '0'::INTERVAL END
ELSE NULL
END AS replication_time_lag,
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.apply_lag) ELSE NULL END AS apply_lag,
AGE(NOW(), CASE WHEN pg_catalog.pg_is_in_recovery() THEN repmgr.standby_get_last_updated() ELSE m.last_monitor_time END) AS communication_time_lag
FROM repmgr.monitoring_history m
JOIN repmgr.nodes n ON m.standby_node_id = n.node_id
WHERE (m.standby_node_id, m.last_monitor_time) IN (
SELECT m1.standby_node_id, MAX(m1.last_monitor_time)
FROM repmgr.monitoring_history m1 GROUP BY 1
);

View File

@@ -1,245 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
-- extract the current schema name
-- NOTE: this assumes there will be only one schema matching 'repmgr_%';
-- user is responsible for ensuring this is the case
CREATE TEMPORARY TABLE repmgr_old_schema (schema_name TEXT);
INSERT INTO repmgr_old_schema (schema_name)
SELECT nspname AS schema_name
FROM pg_catalog.pg_namespace
WHERE nspname LIKE 'repmgr_%'
LIMIT 1;
-- move old objects into new schema
DO $repmgr$
DECLARE
old_schema TEXT;
BEGIN
SELECT schema_name FROM repmgr_old_schema
INTO old_schema;
EXECUTE format('ALTER TABLE %I.repl_nodes SET SCHEMA repmgr', old_schema);
EXECUTE format('ALTER TABLE %I.repl_events SET SCHEMA repmgr', old_schema);
EXECUTE format('ALTER TABLE %I.repl_monitor SET SCHEMA repmgr', old_schema);
EXECUTE format('DROP VIEW IF EXISTS %I.repl_show_nodes', old_schema);
EXECUTE format('DROP VIEW IF EXISTS %I.repl_status', old_schema);
END$repmgr$;
-- convert "repmgr_$cluster.repl_nodes" to "repmgr.nodes"
CREATE TABLE repmgr.nodes (
node_id INTEGER PRIMARY KEY,
upstream_node_id INTEGER NULL REFERENCES repmgr.nodes (node_id) DEFERRABLE,
active BOOLEAN NOT NULL DEFAULT TRUE,
node_name TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN('primary','standby','witness','bdr')),
location TEXT NOT NULL DEFAULT 'default',
priority INT NOT NULL DEFAULT 100,
conninfo TEXT NOT NULL,
repluser VARCHAR(63) NOT NULL,
slot_name TEXT NULL,
config_file TEXT NOT NULL
);
INSERT INTO repmgr.nodes
(node_id, upstream_node_id, active, node_name, type, location, priority, conninfo, repluser, slot_name, config_file)
SELECT id, upstream_node_id, active, name,
CASE WHEN type = 'master' THEN 'primary' ELSE type END,
'default', priority, conninfo, 'unknown', slot_name, 'unknown'
FROM repmgr.repl_nodes
ORDER BY id;
-- convert "repmgr_$cluster.repl_event" to "event"
CREATE TABLE repmgr.events (
node_id INTEGER NOT NULL,
event TEXT NOT NULL,
successful BOOLEAN NOT NULL DEFAULT TRUE,
event_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
details TEXT NULL
);
INSERT INTO repmgr.events
(node_id, event, successful, event_timestamp, details)
SELECT node_id, event, successful, event_timestamp, details
FROM repmgr.repl_events;
-- create new table "repmgr.voting_term"
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);
CREATE UNIQUE INDEX voting_term_restrict
ON repmgr.voting_term ((TRUE));
CREATE RULE voting_term_delete AS
ON DELETE TO repmgr.voting_term
DO INSTEAD NOTHING;
INSERT INTO repmgr.voting_term (term) VALUES (1);
-- convert "repmgr_$cluster.repl_monitor" to "monitoring_history"
CREATE TABLE repmgr.monitoring_history (
primary_node_id INTEGER NOT NULL,
standby_node_id INTEGER NOT NULL,
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
last_apply_time TIMESTAMP WITH TIME ZONE,
last_wal_primary_location PG_LSN NOT NULL,
last_wal_standby_location PG_LSN,
replication_lag BIGINT NOT NULL,
apply_lag BIGINT NOT NULL
);
INSERT INTO repmgr.monitoring_history
(primary_node_id, standby_node_id, last_monitor_time, last_apply_time, last_wal_primary_location, last_wal_standby_location, replication_lag, apply_lag)
SELECT primary_node, standby_node, last_monitor_time, last_apply_time, last_wal_primary_location::pg_lsn, last_wal_standby_location::pg_lsn, replication_lag, apply_lag
FROM repmgr.repl_monitor;
CREATE INDEX idx_monitoring_history_time
ON repmgr.monitoring_history (last_monitor_time, standby_node_id);
CREATE VIEW repmgr.show_nodes AS
SELECT n.node_id,
n.node_name,
n.active,
n.upstream_node_id,
un.node_name AS upstream_node_name,
n.type,
n.priority,
n.conninfo
FROM repmgr.nodes n
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
/* ================= */
/* repmgrd functions */
/* ================= */
/* monitoring functions */
CREATE FUNCTION set_local_node_id(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_local_node_id'
LANGUAGE C STRICT;
CREATE FUNCTION get_local_node_id()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_local_node_id'
LANGUAGE C STRICT;
CREATE FUNCTION standby_set_last_updated()
RETURNS TIMESTAMP WITH TIME ZONE
AS 'MODULE_PATHNAME', 'standby_set_last_updated'
LANGUAGE C STRICT;
CREATE FUNCTION standby_get_last_updated()
RETURNS TIMESTAMP WITH TIME ZONE
AS 'MODULE_PATHNAME', 'standby_get_last_updated'
LANGUAGE C STRICT;
CREATE FUNCTION set_upstream_last_seen(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_upstream_last_seen'
LANGUAGE C STRICT;
CREATE FUNCTION get_upstream_last_seen()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_upstream_last_seen'
LANGUAGE C STRICT;
CREATE FUNCTION get_upstream_node_id()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_upstream_node_id'
LANGUAGE C STRICT;
CREATE FUNCTION set_upstream_node_id(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_upstream_node_id'
LANGUAGE C STRICT;
/* failover functions */
CREATE FUNCTION notify_follow_primary(INT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'notify_follow_primary'
LANGUAGE C STRICT;
CREATE FUNCTION get_new_primary()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_new_primary'
LANGUAGE C STRICT;
CREATE FUNCTION reset_voting_status()
RETURNS VOID
AS 'MODULE_PATHNAME', 'reset_voting_status'
LANGUAGE C STRICT;
CREATE FUNCTION get_repmgrd_pid()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
LANGUAGE C STRICT;
CREATE FUNCTION get_repmgrd_pidfile()
RETURNS TEXT
AS 'MODULE_PATHNAME', 'get_repmgrd_pidfile'
LANGUAGE C STRICT;
CREATE FUNCTION set_repmgrd_pid(INT, TEXT)
RETURNS VOID
AS 'MODULE_PATHNAME', 'set_repmgrd_pid'
LANGUAGE C CALLED ON NULL INPUT;
CREATE FUNCTION repmgrd_is_running()
RETURNS BOOL
AS 'MODULE_PATHNAME', 'repmgrd_is_running'
LANGUAGE C STRICT;
CREATE FUNCTION repmgrd_pause(BOOL)
RETURNS VOID
AS 'MODULE_PATHNAME', 'repmgrd_pause'
LANGUAGE C STRICT;
CREATE FUNCTION repmgrd_is_paused()
RETURNS BOOL
AS 'MODULE_PATHNAME', 'repmgrd_is_paused'
LANGUAGE C STRICT;
CREATE FUNCTION get_wal_receiver_pid()
RETURNS INT
AS 'MODULE_PATHNAME', 'get_wal_receiver_pid'
LANGUAGE C STRICT;
/* views */
CREATE VIEW repmgr.replication_status AS
SELECT m.primary_node_id, m.standby_node_id, n.node_name AS standby_name,
n.type AS node_type, n.active, last_monitor_time,
CASE WHEN n.type='standby' THEN m.last_wal_primary_location ELSE NULL END AS last_wal_primary_location,
m.last_wal_standby_location,
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.replication_lag) ELSE NULL END AS replication_lag,
CASE WHEN n.type='standby' THEN
CASE WHEN replication_lag > 0 THEN age(now(), m.last_apply_time) ELSE '0'::INTERVAL END
ELSE NULL
END AS replication_time_lag,
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.apply_lag) ELSE NULL END AS apply_lag,
AGE(NOW(), CASE WHEN pg_catalog.pg_is_in_recovery() THEN repmgr.standby_get_last_updated() ELSE m.last_monitor_time END) AS communication_time_lag
FROM repmgr.monitoring_history m
JOIN repmgr.nodes n ON m.standby_node_id = n.node_id
WHERE (m.standby_node_id, m.last_monitor_time) IN (
SELECT m1.standby_node_id, MAX(m1.last_monitor_time)
FROM repmgr.monitoring_history m1 GROUP BY 1
);
/* drop old tables */
DROP TABLE repmgr.repl_nodes;
DROP TABLE repmgr.repl_monitor;
DROP TABLE repmgr.repl_events;
-- remove temporary table
DROP TABLE repmgr_old_schema;

View File

@@ -55,8 +55,10 @@ typedef enum
struct ColHeader headers_show[SHOW_HEADER_COUNT];
struct ColHeader headers_event[EVENT_HEADER_COUNT];
static int build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, int *error_code);
static int build_cluster_crosscheck(t_node_status_cube ***cube_dest, ItemList *warnings, int *error_code);
static int build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, ItemList *warnings, int *error_code);
static int build_cluster_crosscheck(t_node_status_cube ***cube_dest, int *name_length, ItemList *warnings, int *error_code);
static void cube_set_node_status(t_node_status_cube **cube, int n, int node_id, int matrix_node_id, int connection_node_id, int connection_status);
/*
@@ -536,6 +538,9 @@ do_cluster_crosscheck(void)
{
int i = 0,
n = 0;
char c;
const char *node_header = "Name";
int name_length = strlen(node_header);
t_node_status_cube **cube;
@@ -543,7 +548,7 @@ do_cluster_crosscheck(void)
int error_code = SUCCESS;
ItemList warnings = {NULL, NULL};
n = build_cluster_crosscheck(&cube, &warnings, &error_code);
n = build_cluster_crosscheck(&cube, &name_length, &warnings, &error_code);
if (runtime_options.output_mode == OM_CSV)
{
@@ -577,56 +582,24 @@ do_cluster_crosscheck(void)
}
else
{
/* output header contains node name, node ID and one column for each node in the cluster */
struct ColHeader *headers_crosscheck = NULL;
int header_count = n + 2;
int header_id = 2;
headers_crosscheck = palloc0(sizeof(ColHeader) * header_count);
/* Initialize column headers */
strncpy(headers_crosscheck[0].title, _("Name"), MAXLEN);
strncpy(headers_crosscheck[1].title, _("ID"), MAXLEN);
printf("%*s | Id ", name_length, node_header);
for (i = 0; i < n; i++)
{
maxlen_snprintf(headers_crosscheck[header_id].title, "%i", cube[i]->node_id);
header_id++;
}
/* Initialize column max values */
for (i = 0; i < header_count; i++)
{
headers_crosscheck[i].display = true;
headers_crosscheck[i].max_length = strlen(headers_crosscheck[i].title);
headers_crosscheck[i].cur_length = headers_crosscheck[i].max_length;
/* We can derive the maximum node ID length for the ID column from
* the generated matrix node ID headers
*/
if (i >= 2 && headers_crosscheck[i].max_length > headers_crosscheck[1].max_length)
headers_crosscheck[1].max_length = headers_crosscheck[i].max_length;
}
printf("| %2d ", cube[i]->node_id);
printf("\n");
for (i = 0; i < name_length; i++)
printf("-");
printf("-+----");
for (i = 0; i < n; i++)
{
if (strlen(cube[i]->node_name) > headers_crosscheck[0].max_length)
{
headers_crosscheck[0].max_length = strlen(cube[i]->node_name);
}
}
print_status_header(header_count, headers_crosscheck);
printf("+----");
printf("\n");
for (i = 0; i < n; i++)
{
int column_node_ix;
printf(" %-*s | %-*i ",
headers_crosscheck[0].max_length,
printf("%*s | %2d ", name_length,
cube[i]->node_name,
headers_crosscheck[1].max_length,
cube[i]->node_id);
for (column_node_ix = 0; column_node_ix < n; column_node_ix++)
@@ -634,8 +607,6 @@ do_cluster_crosscheck(void)
int max_node_status = -2;
int node_ix = 0;
char c;
/*
* The value of entry (i,j) is equal to the maximum value of all
* the (i,j,k). Indeed:
@@ -675,14 +646,12 @@ do_cluster_crosscheck(void)
exit(ERR_INTERNAL);
}
printf("| %-*c ", headers_crosscheck[column_node_ix + 2].max_length, c);
printf("| %c ", c);
}
printf("\n");
}
pfree(headers_crosscheck);
if (warnings.head != NULL && runtime_options.terse == false)
{
log_warning(_("following problems detected:"));
@@ -739,13 +708,16 @@ do_cluster_matrix()
j = 0,
n = 0;
const char *node_header = "Name";
int name_length = strlen(node_header);
t_node_matrix_rec **matrix_rec_list;
bool connection_error_found = false;
int error_code = SUCCESS;
ItemList warnings = {NULL, NULL};
n = build_cluster_matrix(&matrix_rec_list, &warnings, &error_code);
n = build_cluster_matrix(&matrix_rec_list, &name_length, &warnings, &error_code);
if (runtime_options.output_mode == OM_CSV)
{
@@ -768,60 +740,27 @@ do_cluster_matrix()
}
else
{
/* output header contains node name, node ID and one column for each node in the cluster */
struct ColHeader *headers_matrix = NULL;
char c;
int header_count = n + 2;
int header_id = 2;
printf("%*s | Id ", name_length, node_header);
for (i = 0; i < n; i++)
printf("| %2d ", matrix_rec_list[i]->node_id);
printf("\n");
headers_matrix = palloc0(sizeof(ColHeader) * header_count);
/* Initialize column headers */
strncpy(headers_matrix[0].title, _("Name"), MAXLEN);
strncpy(headers_matrix[1].title, _("ID"), MAXLEN);
for (i = 0; i < name_length; i++)
printf("-");
printf("-+----");
for (i = 0; i < n; i++)
printf("+----");
printf("\n");
for (i = 0; i < n; i++)
{
maxlen_snprintf(headers_matrix[header_id].title, "%i", matrix_rec_list[i]->node_id);
header_id++;
}
/* Initialize column max values */
for (i = 0; i < header_count; i++)
{
headers_matrix[i].display = true;
headers_matrix[i].max_length = strlen(headers_matrix[i].title);
headers_matrix[i].cur_length = headers_matrix[i].max_length;
/* We can derive the maximum node ID length for the ID column from
* the generated matrix node ID headers
*/
if (i >= 2 && headers_matrix[i].max_length > headers_matrix[1].max_length)
headers_matrix[1].max_length = headers_matrix[i].max_length;
}
for (i = 0; i < n; i++)
{
if (strlen(matrix_rec_list[i]->node_name) > headers_matrix[0].max_length)
{
headers_matrix[0].max_length = strlen(matrix_rec_list[i]->node_name);
}
}
print_status_header(header_count, headers_matrix);
for (i = 0; i < n; i++)
{
printf(" %-*s | %-*i ",
headers_matrix[0].max_length,
printf("%*s | %2d ", name_length,
matrix_rec_list[i]->node_name,
headers_matrix[1].max_length,
matrix_rec_list[i]->node_id);
for (j = 0; j < n; j++)
{
char c;
switch (matrix_rec_list[i]->node_status_list[j]->node_status)
{
case -2:
@@ -839,13 +778,11 @@ do_cluster_matrix()
exit(ERR_INTERNAL);
}
printf("| %-*c ", headers_matrix[j + 2].max_length, c);
printf("| %c ", c);
}
printf("\n");
}
pfree(headers_matrix);
if (warnings.head != NULL && runtime_options.terse == false)
{
log_warning(_("following problems detected:"));
@@ -901,7 +838,7 @@ matrix_set_node_status(t_node_matrix_rec **matrix_rec_list, int n, int node_id,
static int
build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, int *error_code)
build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, ItemList *warnings, int *error_code)
{
PGconn *conn = NULL;
int i = 0,
@@ -959,6 +896,7 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, i
/* Initialise matrix structure for each node */
for (cell = nodes.head; cell; cell = cell->next)
{
int name_length_cur;
NodeInfoListCell *cell_j;
matrix_rec_list[i] = (t_node_matrix_rec *) pg_malloc0(sizeof(t_node_matrix_rec));
@@ -968,6 +906,13 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, i
cell->node_info->node_name,
sizeof(matrix_rec_list[i]->node_name));
/*
* Find the maximum length of a node name
*/
name_length_cur = strlen(matrix_rec_list[i]->node_name);
if (name_length_cur > *name_length)
*name_length = name_length_cur;
matrix_rec_list[i]->node_status_list = (t_node_status_rec **) pg_malloc0(sizeof(t_node_status_rec) * nodes.node_count);
j = 0;
@@ -1132,7 +1077,7 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, i
static int
build_cluster_crosscheck(t_node_status_cube ***dest_cube, ItemList *warnings, int *error_code)
build_cluster_crosscheck(t_node_status_cube ***dest_cube, int *name_length, ItemList *warnings, int *error_code)
{
PGconn *conn = NULL;
int h,
@@ -1181,12 +1126,20 @@ build_cluster_crosscheck(t_node_status_cube ***dest_cube, ItemList *warnings, in
for (cell = nodes.head; cell; cell = cell->next)
{
int name_length_cur = 0;
NodeInfoListCell *cell_i = NULL;
cube[h] = (t_node_status_cube *) pg_malloc(sizeof(t_node_status_cube));
cube[h]->node_id = cell->node_info->node_id;
strncpy(cube[h]->node_name, cell->node_info->node_name, sizeof(cube[h]->node_name));
/*
* Find the maximum length of a node name
*/
name_length_cur = strlen(cube[h]->node_name);
if (name_length_cur > *name_length)
*name_length = name_length_cur;
cube[h]->matrix_list_rec = (t_node_matrix_rec **) pg_malloc(sizeof(t_node_matrix_rec) * nodes.node_count);
i = 0;
@@ -1554,5 +1507,4 @@ do_cluster_help(void)
printf(_(" -k, --keep-history=VALUE retain indicated number of days of history (default: 0)\n"));
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
}

View File

@@ -339,5 +339,4 @@ void do_daemon_help(void)
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
}

View File

@@ -36,8 +36,6 @@ static bool copy_file(const char *src_file, const char *dest_file);
static void format_archive_dir(PQExpBufferData *archive_dir);
static t_server_action parse_server_action(const char *action);
static void exit_optformat_error(const char *error, int errcode);
static void _do_node_service_list_actions(t_server_action action);
static void _do_node_status_is_shutdown_cleanly(void);
static void _do_node_archive_config(void);
@@ -45,7 +43,7 @@ static void _do_node_restore_config(void);
static void do_node_check_replication_connection(void);
static CheckStatus do_node_check_archive_ready(PGconn *conn, OutputMode mode, CheckStatusList *list_output);
static CheckStatus do_node_check_downstream(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
static CheckStatus do_node_check_downstream(PGconn *conn, OutputMode mode, CheckStatusList *list_output);
static CheckStatus do_node_check_upstream(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
static CheckStatus do_node_check_replication_lag(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
static CheckStatus do_node_check_role(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
@@ -53,8 +51,6 @@ static CheckStatus do_node_check_slots(PGconn *conn, OutputMode mode, t_node_inf
static CheckStatus do_node_check_missing_slots(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
static CheckStatus do_node_check_data_directory(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
static CheckStatus do_node_check_replication_config_owner(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output);
static CheckStatus do_node_check_db_connection(PGconn *conn, OutputMode mode);
/*
* NODE STATUS
*
@@ -85,6 +81,7 @@ do_node_status(void)
t_recovery_conf recovery_conf = T_RECOVERY_CONF_INITIALIZER;
char data_dir[MAXPGPATH] = "";
int server_version_num = UNKNOWN_SERVER_VERSION_NUM;
char server_version_str[MAXVERSIONSTR] = "";
/*
@@ -102,7 +99,7 @@ do_node_status(void)
conn = establish_db_connection(config_file_options.conninfo, true);
strncpy(data_dir, config_file_options.data_directory, MAXPGPATH);
(void)get_server_version(conn, server_version_str);
server_version_num = get_server_version(conn, server_version_str);
/* check node exists */
@@ -134,22 +131,13 @@ do_node_status(void)
if (runtime_options.verbose == true)
{
uint64 local_system_identifier = get_system_identifier(config_file_options.data_directory);
uint64 local_system_identifier = UNKNOWN_SYSTEM_IDENTIFIER;
if (local_system_identifier == UNKNOWN_SYSTEM_IDENTIFIER)
{
key_value_list_set(&node_status,
"System identifier",
"unknown");
item_list_append_format(&warnings,
_("unable to retrieve system identifier from pg_control"));
}
else
{
key_value_list_set_format(&node_status,
"System identifier",
"%lu", local_system_identifier);
}
local_system_identifier = get_system_identifier(config_file_options.data_directory);
key_value_list_set_format(&node_status,
"System identifier",
"%lu", local_system_identifier);
}
key_value_list_set(&node_status,
@@ -216,16 +204,7 @@ do_node_status(void)
if (enabled == false && recovery_type == RECTYPE_STANDBY)
{
if (PQserverVersion(conn) >= 90500)
{
appendPQExpBufferStr(&archiving_status,
" (on standbys \"archive_mode\" must be set to \"always\" to be effective)");
}
else
{
appendPQExpBufferStr(&archiving_status,
" (\"archive_mode\" has no effect on standbys)");
}
appendPQExpBufferStr(&archiving_status, " (on standbys \"archive_mode\" must be set to \"always\" to be effective)");
}
key_value_list_set(&node_status,
@@ -315,7 +294,7 @@ do_node_status(void)
continue;
}
if (is_downstream_node_attached(conn, node_cell->node_info->node_name, NULL) != NODE_ATTACHED)
if (is_downstream_node_attached(conn, node_cell->node_info->node_name) != NODE_ATTACHED)
{
missing_nodes_count++;
item_list_append_format(&missing_nodes,
@@ -342,7 +321,13 @@ do_node_status(void)
}
}
if (node_info.max_replication_slots == 0)
if (server_version_num < 90400)
{
key_value_list_set(&node_status,
"Replication slots",
"not available");
}
else if (node_info.max_replication_slots == 0)
{
key_value_list_set(&node_status,
"Replication slots",
@@ -647,17 +632,9 @@ _do_node_status_is_shutdown_cleanly(void)
break;
}
/* check what pg_control says */
/* check what pg_controldata says */
if (get_db_state(config_file_options.data_directory, &db_state) == false)
{
/*
* Unable to retrieve the database state from pg_control
*/
node_status = NODE_STATUS_UNKNOWN;
log_verbose(LOG_DEBUG, "unable to determine db state");
goto return_state;
}
db_state = get_db_state(config_file_options.data_directory);
log_verbose(LOG_DEBUG, "db state now: %s", describe_db_state(db_state));
@@ -676,23 +653,21 @@ _do_node_status_is_shutdown_cleanly(void)
checkPoint = get_latest_checkpoint_location(config_file_options.data_directory);
/* unable to read pg_control, don't know what's happening */
if (checkPoint == InvalidXLogRecPtr)
{
/* unable to read pg_control, don't know what's happening */
node_status = NODE_STATUS_UNKNOWN;
}
/*
* if still "UNKNOWN" at this point, then the node must be cleanly shut
* down
*/
else if (node_status == NODE_STATUS_UNKNOWN)
{
/*
* if still "UNKNOWN" at this point, then the node must be cleanly shut
* down
*/
node_status = NODE_STATUS_DOWN;
}
return_state:
log_verbose(LOG_DEBUG, "node status determined as: %s",
print_node_status(node_status));
@@ -711,26 +686,6 @@ return_state:
return;
}
static void
exit_optformat_error(const char *error, int errcode)
{
PQExpBufferData output;
Assert(runtime_options.output_mode == OM_OPTFORMAT);
initPQExpBuffer(&output);
appendPQExpBuffer(&output,
"--error=%s",
error);
printf("%s\n", output.data);
termPQExpBuffer(&output);
exit(errcode);
}
/*
* Configuration file required
*/
@@ -747,7 +702,6 @@ do_node_check(void)
CheckStatusListCell *cell = NULL;
bool issue_detected = false;
bool exit_on_connection_error = true;
/* for internal use */
if (runtime_options.has_passfile == true)
@@ -757,28 +711,12 @@ do_node_check(void)
exit(return_code);
}
/* for use by "standby switchover" */
if (runtime_options.replication_connection == true)
{
do_node_check_replication_connection();
exit(SUCCESS);
}
if (runtime_options.db_connection == true)
{
exit_on_connection_error = false;
}
/*
* If --optformat was provided, we'll assume this is a remote invocation
* and instead of exiting with an error, we'll return an error string to
* so the remote invoker will know what's happened.
*/
if (runtime_options.output_mode == OM_OPTFORMAT)
{
exit_on_connection_error = false;
}
if (config_file_options.conninfo[0] != '\0')
{
@@ -794,12 +732,6 @@ do_node_check(void)
if (parse_success == false)
{
if (runtime_options.output_mode == OM_OPTFORMAT)
{
exit_optformat_error("CONNINFO_PARSE",
ERR_BAD_CONFIG);
}
log_error(_("unable to parse conninfo string \"%s\" for local node"),
config_file_options.conninfo);
log_detail("%s", errmsg);
@@ -817,36 +749,16 @@ do_node_check(void)
config_file_options.conninfo,
"user",
runtime_options.superuser,
exit_on_connection_error);
true);
}
else
{
conn = establish_db_connection_by_params(&node_conninfo, exit_on_connection_error);
conn = establish_db_connection_by_params(&node_conninfo, true);
}
}
else
{
conn = establish_db_connection_by_params(&source_conninfo, exit_on_connection_error);
}
/*
* --db-connection option provided
*/
if (runtime_options.db_connection == true)
{
return_code = do_node_check_db_connection(conn, runtime_options.output_mode);
PQfinish(conn);
exit(return_code);
}
/*
* If we've reached here, and the connection is invalid, then --optformat was provided
*/
if (PQstatus(conn) != CONNECTION_OK)
{
exit_optformat_error("DB_CONNECTION",
ERR_DB_CONN);
conn = establish_db_connection_by_params(&source_conninfo, true);
}
if (get_node_record(conn, config_file_options.node_id, &node_info) != RECORD_FOUND)
@@ -885,7 +797,6 @@ do_node_check(void)
{
return_code = do_node_check_downstream(conn,
runtime_options.output_mode,
&node_info,
NULL);
PQfinish(conn);
exit(return_code);
@@ -977,7 +888,7 @@ do_node_check(void)
if (do_node_check_upstream(conn, runtime_options.output_mode, &node_info, &status_list) != CHECK_STATUS_OK)
issue_detected = true;
if (do_node_check_downstream(conn, runtime_options.output_mode, &node_info, &status_list) != CHECK_STATUS_OK)
if (do_node_check_downstream(conn, runtime_options.output_mode, &status_list) != CHECK_STATUS_OK)
issue_detected = true;
if (do_node_check_slots(conn, runtime_options.output_mode, &node_info, &status_list) != CHECK_STATUS_OK)
@@ -1075,15 +986,7 @@ do_node_check_replication_connection(void)
}
/* retrieve remote node record from local database */
local_conn = establish_db_connection(config_file_options.conninfo, false);
if (PQstatus(local_conn) != CONNECTION_OK)
{
appendPQExpBufferStr(&output, "CONNECTION_ERROR");
printf("%s\n", output.data);
termPQExpBuffer(&output);
return;
}
local_conn = establish_db_connection(config_file_options.conninfo, true);
record_status = get_node_record(local_conn, runtime_options.remote_node_id, &node_record);
PQfinish(local_conn);
@@ -1280,7 +1183,7 @@ do_node_check_archive_ready(PGconn *conn, OutputMode mode, CheckStatusList *list
static CheckStatus
do_node_check_downstream(PGconn *conn, OutputMode mode, t_node_info *node_info, CheckStatusList *list_output)
do_node_check_downstream(PGconn *conn, OutputMode mode, CheckStatusList *list_output)
{
NodeInfoList downstream_nodes = T_NODE_INFO_LIST_INITIALIZER;
NodeInfoListCell *cell = NULL;
@@ -1314,7 +1217,7 @@ do_node_check_downstream(PGconn *conn, OutputMode mode, t_node_info *node_info,
continue;
}
if (is_downstream_node_attached(conn, cell->node_info->node_name, NULL) != NODE_ATTACHED)
if (is_downstream_node_attached(conn, cell->node_info->node_name) != NODE_ATTACHED)
{
missing_nodes_count++;
item_list_append_format(&missing_nodes,
@@ -1331,13 +1234,7 @@ do_node_check_downstream(PGconn *conn, OutputMode mode, t_node_info *node_info,
}
}
if (node_info->type == WITNESS)
{
/* witness is not connecting to any upstream */
appendPQExpBufferStr(&details,
_("N/A - node is a witness"));
}
else if (missing_nodes_count == 0)
if (missing_nodes_count == 0)
{
if (expected_nodes_count == 0)
appendPQExpBufferStr(&details,
@@ -1470,13 +1367,7 @@ do_node_check_upstream(PGconn *conn, OutputMode mode, t_node_info *node_info, Ch
initPQExpBuffer(&details);
if (node_info->type == WITNESS)
{
/* witness is not connecting to any upstream */
appendPQExpBufferStr(&details,
_("N/A - node is a witness"));
}
else if (get_node_record(conn, node_info->upstream_node_id, &upstream_node_info) != RECORD_FOUND)
if (get_node_record(conn, node_info->upstream_node_id, &upstream_node_info) != RECORD_FOUND)
{
if (get_recovery_type(conn) == RECTYPE_STANDBY)
{
@@ -1497,7 +1388,7 @@ do_node_check_upstream(PGconn *conn, OutputMode mode, t_node_info *node_info, Ch
upstream_conn = establish_db_connection(upstream_node_info.conninfo, true);
/* check our node is connected */
if (is_downstream_node_attached(upstream_conn, config_file_options.node_name, NULL) != NODE_ATTACHED)
if (is_downstream_node_attached(upstream_conn, config_file_options.node_name) != NODE_ATTACHED)
{
appendPQExpBuffer(&details,
_("node \"%s\" (ID: %i) is not attached to expected upstream node \"%s\" (ID: %i)"),
@@ -1526,7 +1417,6 @@ do_node_check_upstream(PGconn *conn, OutputMode mode, t_node_info *node_info, Ch
output_check_status(status),
details.data);
}
break;
case OM_TEXT:
if (list_output != NULL)
{
@@ -1856,7 +1746,12 @@ do_node_check_slots(PGconn *conn, OutputMode mode, t_node_info *node_info, Check
initPQExpBuffer(&details);
if (node_info->total_replication_slots == 0)
if (PQserverVersion(conn) < 90400)
{
appendPQExpBufferStr(&details,
_("replication slots not available for this PostgreSQL version"));
}
else if (node_info->total_replication_slots == 0)
{
appendPQExpBufferStr(&details,
_("node has no physical replication slots"));
@@ -1927,42 +1822,50 @@ do_node_check_missing_slots(PGconn *conn, OutputMode mode, t_node_info *node_inf
initPQExpBuffer(&details);
get_downstream_nodes_with_missing_slot(conn,
config_file_options.node_id,
&missing_slots);
if (missing_slots.node_count == 0)
if (PQserverVersion(conn) < 90400)
{
appendPQExpBufferStr(&details,
_("node has no missing physical replication slots"));
_("replication slots not available for this PostgreSQL version"));
}
else
{
NodeInfoListCell *missing_slot_cell = NULL;
bool first_element = true;
get_downstream_nodes_with_missing_slot(conn,
config_file_options.node_id,
&missing_slots);
status = CHECK_STATUS_CRITICAL;
appendPQExpBuffer(&details,
_("%i physical replication slots are missing"),
missing_slots.node_count);
if (missing_slots.node_count)
if (missing_slots.node_count == 0)
{
appendPQExpBufferStr(&details, ": ");
appendPQExpBufferStr(&details,
_("node has no missing physical replication slots"));
}
else
{
NodeInfoListCell *missing_slot_cell = NULL;
bool first_element = true;
for (missing_slot_cell = missing_slots.head; missing_slot_cell; missing_slot_cell = missing_slot_cell->next)
status = CHECK_STATUS_CRITICAL;
appendPQExpBuffer(&details,
_("%i physical replication slots are missing"),
missing_slots.node_count);
if (missing_slots.node_count)
{
if (first_element == true)
{
first_element = false;
}
else
{
appendPQExpBufferStr(&details, ", ");
}
appendPQExpBufferStr(&details, ": ");
appendPQExpBufferStr(&details, missing_slot_cell->node_info->slot_name);
for (missing_slot_cell = missing_slots.head; missing_slot_cell; missing_slot_cell = missing_slot_cell->next)
{
if (first_element == true)
{
first_element = false;
}
else
{
appendPQExpBufferStr(&details, ", ");
}
appendPQExpBufferStr(&details, missing_slot_cell->node_info->slot_name);
}
}
}
}
@@ -2045,7 +1948,7 @@ do_node_check_data_directory(PGconn *conn, OutputMode mode, t_node_info *node_in
* Check actual data directory matches that in repmgr.conf; note this requires
* a superuser connection
*/
if (connection_has_pg_monitor_role(conn, "pg_read_all_settings") == true)
if (connection_has_pg_settings(conn) == true)
{
/* we expect to have a database connection */
if (get_pg_setting(conn, "data_directory", actual_data_directory) == false)
@@ -2194,72 +2097,6 @@ CheckStatus do_node_check_replication_config_owner(PGconn *conn, OutputMode mode
}
/*
* This is not included in the general list output
*/
static CheckStatus
do_node_check_db_connection(PGconn *conn, OutputMode mode)
{
CheckStatus status = CHECK_STATUS_OK;
PQExpBufferData details;
if (mode == OM_CSV)
{
log_error(_("--csv output not provided with --db-connection option"));
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
/* This check is for configuration diagnostics only */
if (mode == OM_NAGIOS)
{
log_error(_("--nagios output not provided with --db-connection option"));
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
initPQExpBuffer(&details);
if (PQstatus(conn) != CONNECTION_OK)
{
t_conninfo_param_list conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
int c;
status = CHECK_STATUS_CRITICAL;
initialize_conninfo_params(&conninfo, false);
conn_to_param_list(conn, &conninfo);
appendPQExpBufferStr(&details,
"connection parameters used:");
for (c = 0; c < conninfo.size && conninfo.keywords[c] != NULL; c++)
{
if (conninfo.values[c] != NULL && conninfo.values[c][0] != '\0')
{
appendPQExpBuffer(&details,
" %s=%s",
conninfo.keywords[c], conninfo.values[c]);
}
}
}
if (mode == OM_OPTFORMAT)
{
printf("--db-connection=%s\n",
output_check_status(status));
}
else if (mode == OM_TEXT)
{
printf("%s (%s)\n",
output_check_status(status),
details.data);
}
termPQExpBuffer(&details);
return status;
}
void
do_node_service(void)
{
@@ -2494,8 +2331,6 @@ do_node_rejoin(void)
DBState db_state;
PGPing status;
bool is_shutdown = true;
int server_version_num = UNKNOWN_SERVER_VERSION_NUM;
bool hide_standby_signal = false;
PQExpBufferData command;
PQExpBufferData command_output;
@@ -2526,11 +2361,7 @@ do_node_rejoin(void)
break;
}
if (get_db_state(config_file_options.data_directory, &db_state) == false)
{
log_error(_("unable to determine database state from pg_control"));
exit(ERR_BAD_CONFIG);
}
db_state = get_db_state(config_file_options.data_directory);
if (is_shutdown == false)
{
@@ -2540,21 +2371,6 @@ do_node_rejoin(void)
exit(ERR_REJOIN_FAIL);
}
/*
* Server version number required to determine whether pg_rewind will run
* crash recovery (Pg 13 and later).
*/
server_version_num = get_pg_version(config_file_options.data_directory, NULL);
if (server_version_num == UNKNOWN_SERVER_VERSION_NUM)
{
/* This is very unlikely to happen */
log_error(_("unable to determine database version"));
exit(ERR_BAD_CONFIG);
}
log_verbose(LOG_DEBUG, "server version number is: %i", server_version_num);
/* check if cleanly shut down */
if (db_state != DB_SHUTDOWNED && db_state != DB_SHUTDOWNED_IN_RECOVERY)
{
@@ -2562,41 +2378,15 @@ do_node_rejoin(void)
{
log_error(_("database is still shutting down"));
}
else if (server_version_num >= 130000 && runtime_options.force_rewind_used == true)
{
log_warning(_("database is not shut down cleanly"));
log_detail(_("--force-rewind provided, pg_rewind will automatically perform recovery"));
/*
* If pg_rewind is executed, the first change it will make
* is to start the server in single user mode, which will fail
* in the presence of "standby.signal", so we'll "hide" it
* (actually delete and recreate).
*/
hide_standby_signal = true;
}
else
{
/*
* If the database was not shut down cleanly, it *might* rejoin correctly
* after starting up and recovering, but better to ensure the database
* can recover before trying anything else.
*/
log_error(_("database is not shut down cleanly"));
if (server_version_num >= 130000)
if (runtime_options.force_rewind_used == true)
{
log_hint(_("provide --force-rewind to run recovery"));
log_detail(_("pg_rewind will not be able to run"));
}
else
{
if (runtime_options.force_rewind_used == true)
{
log_detail(_("pg_rewind will not be able to run"));
}
log_hint(_("database should be restarted then shut down cleanly after crash recovery completes"));
}
log_hint(_("database should be restarted then shut down cleanly after crash recovery completes"));
exit(ERR_REJOIN_FAIL);
}
}
@@ -2604,6 +2394,10 @@ do_node_rejoin(void)
/* check provided upstream connection */
upstream_conn = establish_db_connection_by_params(&source_conninfo, true);
/* sanity checks for 9.3 */
if (PQserverVersion(upstream_conn) < 90400)
check_93_config();
if (get_primary_node_record(upstream_conn, &primary_node_record) == false)
{
log_error(_("unable to retrieve primary node record"));
@@ -2664,7 +2458,7 @@ do_node_rejoin(void)
log_hint(_("check the local node is registered with the current primary \"%s\" (ID: %i)"),
primary_node_record.node_name,
primary_node_record.node_id);
PQfinish(upstream_conn);
PQfinish(primary_conn);
exit(ERR_BAD_CONFIG);
}
@@ -2688,7 +2482,7 @@ do_node_rejoin(void)
* sanity-check that it will actually be possible to stream from the new upstream
*/
{
bool can_rejoin;
bool can_follow;
TimeLineID tli = get_min_recovery_end_timeline(config_file_options.data_directory);
XLogRecPtr min_recovery_location = get_min_recovery_location(config_file_options.data_directory);
@@ -2702,13 +2496,13 @@ do_node_rejoin(void)
if (tli == 0)
tli = get_timeline(config_file_options.data_directory);
can_rejoin = check_node_can_attach(tli,
can_follow = check_node_can_attach(tli,
min_recovery_location,
primary_conn,
&primary_node_record,
true);
if (can_rejoin == false)
if (can_follow == false)
{
PQfinish(primary_conn);
exit(ERR_REJOIN_FAIL);
@@ -2776,9 +2570,9 @@ do_node_rejoin(void)
}
else
{
make_pg_path(&command, "pg_rewind");
appendPQExpBufferStr(&command,
" -D ");
appendPQExpBuffer(&command,
"%s -D ",
make_pg_path("pg_rewind"));
}
appendShellString(&command,
@@ -2800,30 +2594,6 @@ do_node_rejoin(void)
log_detail(_("pg_rewind command is \"%s\""),
command.data);
/*
* In Pg13 and later, pg_rewind will attempt to start up a server which
* was not cleanly shut down in single user mode. This will fail if
* "standby.signal" is present. We'll remove it and restore it after
* pg_rewind runs.
*/
if (hide_standby_signal == true)
{
char standby_signal_file_path[MAXPGPATH] = "";
log_notice(_("temporarily removing \"standby.signal\""));
log_detail(_("this is required so pg_rewind can fix the unclean shutdown"));
make_standby_signal_path(standby_signal_file_path);
if (unlink(standby_signal_file_path) < 0 && errno != ENOENT)
{
log_error(_("unable to remove \"standby.signal\" file in data directory \"%s\""),
standby_signal_file_path);
log_detail("%s", strerror(errno));
exit(ERR_REJOIN_FAIL);
}
}
initPQExpBuffer(&command_output);
ret = local_command(command.data,
@@ -2831,19 +2601,9 @@ do_node_rejoin(void)
termPQExpBuffer(&command);
if (hide_standby_signal == true)
{
/*
* Restore standby.signal if we previously removed it, regardless
* of whether the pg_rewind operation failed.
*/
log_notice(_("recreating \"standby.signal\""));
write_standby_signal();
}
if (ret == false)
{
log_error(_("pg_rewind execution failed"));
log_error(_("unable to execute pg_rewind"));
log_detail("%s", command_output.data);
termPQExpBuffer(&command_output);
@@ -2911,7 +2671,7 @@ do_node_rejoin(void)
struct stat statbuf;
PQExpBufferData slotdir_ent_path;
if (strcmp(slotdir_ent->d_name, ".") == 0 || strcmp(slotdir_ent->d_name, "..") == 0)
if(strcmp(slotdir_ent->d_name, ".") == 0 || strcmp(slotdir_ent->d_name, "..") == 0)
continue;
initPQExpBuffer(&slotdir_ent_path);
@@ -3017,7 +2777,7 @@ do_node_rejoin(void)
config_file_options.node_rejoin_timeout);
}
else {
log_detail(_("no active record for local node \"%s\" found in node \"%s\"'s \"pg_stat_replication\" table"),
log_detail(_("no record for local node \"%s\" found in node \"%s\"'s \"pg_stat_replication\" table"),
config_file_options.node_name,
primary_node_record.node_name);
}
@@ -3029,7 +2789,7 @@ do_node_rejoin(void)
else
{
/* -W/--no-wait provided - check once */
NodeAttached node_attached = is_downstream_node_attached(primary_conn, config_file_options.node_name, NULL);
NodeAttached node_attached = is_downstream_node_attached(primary_conn, config_file_options.node_name);
if (node_attached == NODE_ATTACHED)
success = true;
}
@@ -3638,8 +3398,8 @@ do_node_help(void)
printf(_(" --list-actions show what command would be performed for each action\n"));
printf(_(" --checkpoint issue a CHECKPOINT before stopping or restarting the node\n"));
printf(_(" -S, --superuser=USERNAME superuser to use, if repmgr user is not superuser\n"));
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
}

View File

@@ -454,9 +454,9 @@ do_primary_unregister(void)
/*
* This appears to be the cluster primary - cowardly refuse to
* delete the record, unless --force is supplied.
* delete the record
*/
if (primary_node_info.node_id == target_node_info_ptr->node_id && !runtime_options.force)
if (primary_node_info.node_id == target_node_info_ptr->node_id)
{
log_error(_("node \"%s\" (ID: %i) is the current primary node, unable to unregister"),
target_node_info_ptr->node_name,
@@ -575,5 +575,4 @@ do_primary_help(void)
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
}

View File

@@ -543,6 +543,4 @@ void do_service_help(void)
puts("");
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
}

File diff suppressed because it is too large Load Diff

View File

@@ -560,7 +560,7 @@ void do_witness_help(void)
printf(_("WITNESS UNREGISTER\n"));
puts("");
printf(_(" \"witness unregister\" unregisters a witness node.\n"));
printf(_(" \"witness register\" unregisters a witness node.\n"));
puts("");
printf(_(" --dry-run check prerequisites but don't make any changes\n"));
printf(_(" -F, --force unregister when witness node not running\n"));
@@ -569,5 +569,5 @@ void do_witness_help(void)
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
return;
}

View File

@@ -48,7 +48,6 @@ typedef struct
bool no_wait;
bool compact;
bool detail;
bool dump_config;
/* logging options */
char log_level[MAXLEN]; /* overrides setting in repmgr.conf */
@@ -84,12 +83,11 @@ typedef struct
bool fast_checkpoint;
bool rsync_only;
bool no_upstream_connection;
char recovery_min_apply_delay[MAXLEN]; /* overrides setting in repmgr.conf */
char recovery_min_apply_delay[MAXLEN];
char replication_user[MAXLEN];
char upstream_conninfo[MAXLEN];
bool without_barman;
bool replication_conf_only;
bool verify_backup;
/* "standby clone"/"standby follow" options */
int upstream_node_id;
@@ -122,7 +120,6 @@ typedef struct
bool replication_connection;
bool data_directory_config;
bool replication_config_owner;
bool db_connection;
/* "node rejoin" options */
char config_files[MAXLEN];
@@ -143,7 +140,7 @@ typedef struct
/* following options for internal use */
char config_archive_dir[MAXPGPATH];
OutputMode output_mode; /* set through provision of --csv, --nagios or --optformat */
OutputMode output_mode;
bool disable_wal_receiver;
bool enable_wal_receiver;
} t_runtime_options;
@@ -152,7 +149,7 @@ typedef struct
/* configuration metadata */ \
false, false, false, false, false, \
/* general configuration options */ \
"", false, false, "", -1, false, false, false, false, \
"", false, false, "", -1, false, false, false, \
/* logging options */ \
"", false, false, false, false, \
/* output options */ \
@@ -165,7 +162,7 @@ typedef struct
UNKNOWN_NODE_ID, "", "", UNKNOWN_NODE_ID, \
/* "standby clone" options */ \
false, CONFIG_FILE_SAMEPATH, false, false, false, "", "", "", \
false, false, false, \
false, false, \
/* "standby clone"/"standby follow" options */ \
NO_UPSTREAM_NODE, \
/* "standby register" options */ \
@@ -175,7 +172,7 @@ typedef struct
/* "node status" options */ \
false, \
/* "node check" options */ \
false, false, false, false, false, false, false, false, false, false, false, false, \
false, false, false, false, false, false, false, false, false, false, false, \
/* "node rejoin" options */ \
"", \
/* "node service" options */ \
@@ -224,13 +221,6 @@ typedef enum
JOIN_FAIL_NO_REPLICATION
} standy_join_status;
typedef enum
{
REMOTE_ERROR_UNKNOWN = -1,
REMOTE_ERROR_NONE,
REMOTE_ERROR_DB_CONNECTION,
REMOTE_ERROR_CONNINFO_PARSE
} t_remote_error_type;
typedef struct ColHeader
{
@@ -242,17 +232,21 @@ typedef struct ColHeader
/* globally available configuration structures */
/* global configuration structures */
extern t_runtime_options runtime_options;
extern t_conninfo_param_list source_conninfo;
extern t_node_info target_node_info;
extern t_configuration_options config_file_options;
t_conninfo_param_list source_conninfo;
/* global variables */
extern bool config_file_required;
extern char pg_bindir[MAXLEN];
/* global functions */
extern t_node_info target_node_info;
extern int check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string);
extern void check_93_config(void);
extern bool create_repmgr_extension(PGconn *conn);
extern int test_ssh_connection(char *host, char *remote_user);
@@ -263,7 +257,7 @@ extern int copy_remote_files(char *host, char *remote_user, char *remote_path,
extern void print_error_list(ItemList *error_list, int log_level);
extern void make_pg_path(PQExpBufferData *buf, const char *file);
extern char *make_pg_path(const char *file);
extern void get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGconn **privileged_conn);
@@ -282,8 +276,6 @@ extern void get_node_config_directory(char *config_dir_buf);
extern void get_node_data_directory(char *data_dir_buf);
extern void init_node_record(t_node_info *node_record);
extern bool can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *reason);
extern void make_standby_signal_path(char *buf);
extern bool write_standby_signal(void);
extern bool create_replication_slot(PGconn *conn, char *slot_name, t_node_info *upstream_node_record, PQExpBufferData *error_msg);
extern bool drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name);

View File

@@ -75,13 +75,15 @@
* ============================ */
t_runtime_options runtime_options = T_RUNTIME_OPTIONS_INITIALIZER;
t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
/* conninfo params for the node we're operating on */
t_conninfo_param_list source_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
bool config_file_required = true;
char pg_bindir[MAXPGPATH] = "";
char pg_bindir[MAXLEN] = "";
char path_buf[MAXLEN] = "";
/*
* if --node-id/--node-name provided, place that node's record here
@@ -277,11 +279,6 @@ main(int argc, char **argv)
runtime_options.detail = true;
break;
/* --dump-config */
case OPT_DUMP_CONFIG:
runtime_options.dump_config = true;
break;
/*----------------------------
* database connection options
*----------------------------
@@ -438,15 +435,6 @@ main(int argc, char **argv)
runtime_options.replication_conf_only = true;
break;
/* --recovery-min-apply-delay */
case OPT_RECOVERY_MIN_APPLY_DELAY:
strncpy(runtime_options.recovery_min_apply_delay, optarg, sizeof(runtime_options.recovery_min_apply_delay));
break;
/* --verify-backup */
case OPT_VERIFY_BACKUP:
runtime_options.verify_backup = true;
break;
/*---------------------------
* "standby register" options
@@ -553,10 +541,6 @@ main(int argc, char **argv)
runtime_options.replication_config_owner = true;
break;
case OPT_DB_CONNECTION:
runtime_options.db_connection = true;
break;
/*--------------------
* "node rejoin" options
*--------------------
@@ -717,12 +701,9 @@ main(int argc, char **argv)
if (strcmp(argv[optind - 1], "-?") == 0)
{
help_option = true;
break;
}
else
{
option_error_found = true;
}
break;
/* otherwise fall through to default */
default: /* invalid option */
option_error_found = true;
break;
@@ -1092,43 +1073,11 @@ main(int argc, char **argv)
load_config(runtime_options.config_file,
runtime_options.verbose,
runtime_options.terse,
&config_file_options,
argv[0]);
/*
* Handle options which must be executed without a repmgr command
*/
if (runtime_options.dump_config == true)
{
if (repmgr_command != NULL)
{
fprintf(stderr,
_("--dump-config cannot be used in combination with a repmgr command"));
exit(ERR_BAD_CONFIG);
}
dump_config();
exit(SUCCESS);
}
check_cli_parameters(action);
/*
* Command-line parameter --recovery-min-apply-delay overrides the equivalent
* setting in the config file. Note we'll need to parse it here to handle
* any formatting errors.
*/
if (*runtime_options.recovery_min_apply_delay != '\0')
{
parse_time_unit_parameter("--recovery-min-apply-delay",
runtime_options.recovery_min_apply_delay,
config_file_options.recovery_min_apply_delay,
&cli_errors);
config_file_options.recovery_min_apply_delay_provided = true;
}
/*
* Sanity checks for command line parameters completed by now; any further
* errors will be runtime ones
@@ -1189,7 +1138,7 @@ main(int argc, char **argv)
}
/*
* Check for configuration file items which can be overridden by runtime
* Check for configuration file items which can be overriden by runtime
* options
* =====================================================================
*/
@@ -1247,7 +1196,7 @@ main(int argc, char **argv)
/*
* If --dry-run specified, ensure log_level is at least LOG_INFO, regardless
* of what's in the configuration file or -L/--log-level parameter, otherwise
* of what's in the configuration file or -L/--log-level paremeter, otherwise
* some or output might not be displayed.
*/
if (runtime_options.dry_run == true)
@@ -1265,6 +1214,8 @@ main(int argc, char **argv)
logger_set_level(LOG_ERROR);
}
/*
* Node configuration information is not needed for all actions, with
* STANDBY CLONE being the main exception.
@@ -2339,7 +2290,7 @@ format_node_status(t_node_info *node_info, PQExpBufferData *node_status, PQExpBu
node_info->node_id);
}
/* mismatch between reported upstream and upstream in local node's metadata */
else if (node_info->upstream_node_id != remote_node_rec.upstream_node_id)
else if(node_info->upstream_node_id != remote_node_rec.upstream_node_id)
{
appendPQExpBufferStr(upstream, "! ");
@@ -2400,7 +2351,6 @@ format_node_status(t_node_info *node_info, PQExpBufferData *node_status, PQExpBu
* connected to the upstream
*/
NodeAttached attached_to_upstream = NODE_ATTACHED_UNKNOWN;
char *replication_state = NULL;
t_node_info upstream_node_rec = T_NODE_INFO_INITIALIZER;
RecordStatus upstream_node_rec_found = get_node_record(node_info->conn,
node_info->upstream_node_id,
@@ -2428,7 +2378,7 @@ format_node_status(t_node_info *node_info, PQExpBufferData *node_status, PQExpBu
}
else
{
attached_to_upstream = is_downstream_node_attached(upstream_conn, node_info->node_name, &replication_state);
attached_to_upstream = is_downstream_node_attached(upstream_conn, node_info->node_name);
}
PQfinish(upstream_conn);
@@ -2444,18 +2394,6 @@ format_node_status(t_node_info *node_info, PQExpBufferData *node_status, PQExpBu
upstream_node_rec.node_name,
upstream_node_rec.node_id);
}
if (attached_to_upstream == NODE_NOT_ATTACHED)
{
appendPQExpBufferStr(upstream, "? ");
item_list_append_format(warnings,
"node \"%s\" (ID: %i) attached to its upstream node \"%s\" (ID: %i) in state \"%s\"",
node_info->node_name,
node_info->node_id,
upstream_node_rec.node_name,
upstream_node_rec.node_id,
replication_state);
}
else if (attached_to_upstream == NODE_DETACHED)
{
appendPQExpBufferStr(upstream, "! ");
@@ -2724,8 +2662,6 @@ do_help(void)
printf(_(" -v, --verbose display additional log output (useful for debugging)\n"));
puts("");
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
}
@@ -2967,6 +2903,30 @@ check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *
}
/*
* check_93_config()
*
* Disable options not compatible with PostgreSQL 9.3
*/
void
check_93_config(void)
{
if (config_file_options.recovery_min_apply_delay_provided == true)
{
config_file_options.recovery_min_apply_delay_provided = false;
log_warning(_("configuration file option \"recovery_min_apply_delay\" not compatible with PostgreSQL 9.3, ignoring"));
}
if (config_file_options.use_replication_slots == true)
{
config_file_options.use_replication_slots = false;
log_warning(_("configuration file option \"use_replication_slots\" not compatible with PostgreSQL 9.3, ignoring"));
log_hint(_("replication slots are available from PostgreSQL 9.4"));
}
}
int
test_ssh_connection(char *host, char *remote_user)
{
@@ -3079,6 +3039,7 @@ get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGconn **privil
}
standy_clone_mode
get_standby_clone_mode(void)
{
@@ -3093,11 +3054,12 @@ get_standby_clone_mode(void)
}
void
make_pg_path(PQExpBufferData *buf, const char *file)
char *
make_pg_path(const char *file)
{
appendPQExpBuffer(buf, "%s%s",
pg_bindir, file);
maxlen_snprintf(path_buf, "%s%s", pg_bindir, file);
return path_buf;
}
@@ -3158,12 +3120,15 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
appendPQExpBufferStr(&rsync_flags,
" --exclude=recovery.conf --exclude=recovery.done");
/*
* 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, " --exclude=%s.tmp",
PG_AUTOCONF_FILENAME);
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, " --exclude=%s.tmp",
PG_AUTOCONF_FILENAME);
}
/* Temporary files which we don't want, if they exist */
appendPQExpBuffer(&rsync_flags, " --exclude=%s*",
@@ -3174,16 +3139,16 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
if (server_version_num >= 100000)
{
appendPQExpBufferStr(&rsync_flags,
" --exclude=pg_wal/* --exclude=log/*");
" --exclude=pg_wal/*");
}
else
{
appendPQExpBufferStr(&rsync_flags,
" --exclude=pg_xlog/* --exclude=pg_log/*");
" --exclude=pg_xlog/*");
}
appendPQExpBufferStr(&rsync_flags,
" --exclude=pg_stat_tmp/*");
" --exclude=pg_log/* --exclude=pg_stat_tmp/*");
maxlen_snprintf(script, "rsync %s %s:%s/* %s",
rsync_flags.data, host_string, remote_path, local_path);
@@ -3310,10 +3275,9 @@ get_server_action(t_server_action action, char *script, char *data_dir)
{
initPQExpBuffer(&command);
make_pg_path(&command, "pg_ctl");
appendPQExpBuffer(&command,
" %s -w -D ",
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
@@ -3341,10 +3305,9 @@ get_server_action(t_server_action action, char *script, char *data_dir)
else
{
initPQExpBuffer(&command);
make_pg_path(&command, "pg_ctl");
appendPQExpBuffer(&command,
" %s -D ",
"%s %s -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
@@ -3377,11 +3340,9 @@ get_server_action(t_server_action action, char *script, char *data_dir)
else
{
initPQExpBuffer(&command);
make_pg_path(&command, "pg_ctl");
appendPQExpBuffer(&command,
" %s -w -D ",
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
@@ -3407,11 +3368,9 @@ get_server_action(t_server_action action, char *script, char *data_dir)
else
{
initPQExpBuffer(&command);
make_pg_path(&command, "pg_ctl");
appendPQExpBuffer(&command,
" %s -w -D ",
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
@@ -3438,11 +3397,9 @@ get_server_action(t_server_action action, char *script, char *data_dir)
else
{
initPQExpBuffer(&command);
make_pg_path(&command, "pg_ctl");
appendPQExpBuffer(&command,
" %s -w -D ",
"%s %s -w -D ",
make_pg_path("pg_ctl"),
config_file_options.pg_ctl_options);
appendShellString(&command,
@@ -3616,6 +3573,27 @@ can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *rea
{
bool can_use = true;
/* wal_log_hints not available in 9.3, so just determine if data checksums enabled */
if (PQserverVersion(conn) < 90400)
{
int data_checksum_version = get_data_checksum_version(data_directory);
if (data_checksum_version < 0)
{
appendPQExpBuffer(reason,
_("unable to determine data checksum version"));
can_use = false;
}
else if (data_checksum_version == 0)
{
appendPQExpBuffer(reason,
_("this cluster was initialised without data checksums"));
can_use = false;
}
return can_use;
}
/* "full_page_writes" must be on in any case */
if (guc_set(conn, "full_page_writes", "=", "off"))
{
@@ -3635,7 +3613,7 @@ can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *rea
{
int data_checksum_version = get_data_checksum_version(data_directory);
if (data_checksum_version == UNKNOWN_DATA_CHECKSUM_VERSION)
if (data_checksum_version < 0)
{
if (can_use == false)
appendPQExpBuffer(reason, "; ");
@@ -3660,64 +3638,8 @@ can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *rea
}
void
make_standby_signal_path(char *buf)
{
snprintf(buf, MAXPGPATH,
"%s/%s",
config_file_options.data_directory,
STANDBY_SIGNAL_FILE);
}
/*
* create standby.signal (PostgreSQL 12 and later)
*/
bool
write_standby_signal(void)
{
char standby_signal_file_path[MAXPGPATH] = "";
FILE *file;
mode_t um;
make_standby_signal_path(standby_signal_file_path);
/* Set umask to 0600 */
um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO));
file = fopen(standby_signal_file_path, "w");
umask(um);
if (file == NULL)
{
log_error(_("unable to create %s file at \"%s\""),
STANDBY_SIGNAL_FILE,
standby_signal_file_path);
log_detail("%s", strerror(errno));
return false;
}
if (fputs("# created by repmgr\n", file) == EOF)
{
log_error(_("unable to write to %s file at \"%s\""),
STANDBY_SIGNAL_FILE,
standby_signal_file_path);
fclose(file);
return false;
}
fclose(file);
return true;
}
/*
* NOTE:
* - the provided connection should be for the normal repmgr user
* - if upstream_node_record is not NULL, its "repluser" entry, if
* set, will be used as the fallback replication user
*/
// provided connection should be for the normal repmgr user
// upstream_node_record may be NULL or initialised to default values
bool
create_replication_slot(PGconn *conn, char *slot_name, t_node_info *upstream_node_record, PQExpBufferData *error_msg)
{
@@ -4064,10 +3986,8 @@ check_standby_join(PGconn *upstream_conn, t_node_info *upstream_node_record, t_n
for (; i < config_file_options.node_rejoin_timeout; i++)
{
char *node_state = NULL;
NodeAttached node_attached = is_downstream_node_attached(upstream_conn,
standby_node_record->node_name,
&node_state);
standby_node_record->node_name);
if (node_attached == NODE_ATTACHED)
{
log_verbose(LOG_INFO, _("node \"%s\" (ID: %i) has attached to its upstream node"),
@@ -4084,19 +4004,9 @@ check_standby_join(PGconn *upstream_conn, t_node_info *upstream_node_record, t_n
i + 1,
config_file_options.node_rejoin_timeout);
if (node_attached == NODE_NOT_ATTACHED)
{
log_detail(_("node \"%s\" (ID: %i) is currrently attached to its upstream node in state \"%s\""),
upstream_node_record->node_name,
standby_node_record->node_id,
node_state);
}
else
{
log_detail(_("checking for record in node \"%s\"'s \"pg_stat_replication\" table where \"application_name\" is \"%s\""),
upstream_node_record->node_name,
standby_node_record->node_name);
}
log_detail(_("checking for record in node \"%s\"'s \"pg_stat_replication\" table where \"application_name\" is \"%s\""),
upstream_node_record->node_name,
standby_node_record->node_name);
}
else
{
@@ -4116,7 +4026,7 @@ check_standby_join(PGconn *upstream_conn, t_node_info *upstream_node_record, t_n
/*
* Here we'll perform some timeline sanity checks to ensure the follow target
* can actually be followed or rejoined.
* can actually be followed.
*
* See also comment for check_node_can_follow() in repmgrd-physical.c .
*/
@@ -4156,31 +4066,10 @@ check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *fo
local_system_identifier = get_system_identifier(config_file_options.data_directory);
/*
* Check for things that should never happen, but expect the unexpected anyway.
* Check for thing that should never happen, but expect the unexpected anyway.
*/
if (local_system_identifier == UNKNOWN_SYSTEM_IDENTIFIER)
if (follow_target_identification.system_identifier != local_system_identifier)
{
/*
* We don't return immediately here so subsequent checks can be
* made, but indicate the node will not be able to rejoin.
*/
success = false;
if (runtime_options.dry_run == true)
{
log_warning(_("unable to retrieve system identifier from pg_control"));
}
else
{
log_error(_("unable to retrieve system identifier from pg_control, aborting"));
}
}
else if (follow_target_identification.system_identifier != local_system_identifier)
{
/*
* It's never going to be possible to rejoin a node from another cluster,
* so no need to bother with further checks.
*/
log_error(_("this node is not part of the %s target node's replication cluster"), action);
log_detail(_("this node's system identifier is %lu, %s target node's system identifier is %lu"),
local_system_identifier,
@@ -4189,7 +4078,8 @@ check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *fo
PQfinish(follow_target_repl_conn);
return false;
}
else if (runtime_options.dry_run == true)
if (runtime_options.dry_run == true)
{
log_info(_("local and %s target system identifiers match"), action);
log_detail(_("system identifier is %lu"), local_system_identifier);
@@ -4202,64 +4092,20 @@ check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *fo
action,
follow_target_identification.timeline);
/*
* The upstream's timeline is lower than ours - we cannot follow, and rejoin
* requires PostgreSQL 9.6 and later.
*/
/* upstream's timeline is lower than ours - impossible case */
if (follow_target_identification.timeline < local_tli)
{
/*
* "repmgr standby follow" is impossible in this case
*/
if (is_rejoin == false)
{
log_error(_("this node's timeline is ahead of the %s target node's timeline"), action);
log_detail(_("this node's timeline is %i, %s target node's timeline is %i"),
local_tli,
action,
follow_target_identification.timeline);
if (PQserverVersion(follow_target_conn) >= 90600)
{
log_hint(_("use \"repmgr node rejoin --force-rewind\" to reattach this node"));
}
PQfinish(follow_target_repl_conn);
return false;
}
/*
* pg_rewind can only rejoin to a lower timeline from PostgreSQL 9.6
*/
if (PQserverVersion(follow_target_conn) < 90600)
{
log_error(_("this node's timeline is ahead of the %s target node's timeline"), action);
log_detail(_("this node's timeline is %i, %s target node's timeline is %i"),
local_tli,
action,
follow_target_identification.timeline);
if (runtime_options.force_rewind_used == true)
{
log_hint(_("pg_rewind can only be used to rejoin to a node with a lower timeline from PostgreSQL 9.6"));
}
PQfinish(follow_target_repl_conn);
return false;
}
if (runtime_options.force_rewind_used == false)
{
log_notice(_("pg_rewind execution required for this node to attach to rejoin target node %i"),
follow_target_node_record->node_id);
log_hint(_("provide --force-rewind"));
PQfinish(follow_target_repl_conn);
return false;
}
log_error(_("this node's timeline is ahead of the %s target node's timeline"), action);
log_detail(_("this node's timeline is %i, %s target node's timeline is %i"),
local_tli,
action,
follow_target_identification.timeline);
PQfinish(follow_target_repl_conn);
return false;
}
/* timelines are the same - check relative positions */
else if (follow_target_identification.timeline == local_tli)
if (follow_target_identification.timeline == local_tli)
{
XLogRecPtr follow_target_xlogpos = get_node_current_lsn(follow_target_conn);
@@ -4280,26 +4126,12 @@ check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *fo
}
else
{
/*
* Unable to follow or join to a node we're ahead of, if we're on the
* same timeline. Also, pg_rewind does not detect this situation,
* as there is no definitive fork point.
*
* Note that Pg will still happily attach to the upstream in state "streaming"
* for a while but then detach with an endless stream of
* "record with incorrect prev-link" errors.
*/
log_error(_("this node ahead of the %s target on the same timeline (%i)"), action, local_tli);
log_error(_("this node is ahead of the %s target"), action);
log_detail(_("local node lsn is %X/%X, %s target lsn is %X/%X"),
format_lsn(local_xlogpos),
action,
format_lsn(follow_target_xlogpos));
if (is_rejoin == true)
{
log_hint(_("the --force-rewind option is ineffective in this case"));
}
success = false;
}
}

View File

@@ -83,32 +83,27 @@
#define OPT_DOWNSTREAM 1030
#define OPT_UPSTREAM 1031
#define OPT_SLOTS 1032
#define OPT_HAS_PASSFILE 1033
#define OPT_WAIT_START 1034
#define OPT_REPL_CONN 1035
#define OPT_REMOTE_NODE_ID 1036
#define OPT_REPLICATION_CONF_ONLY 1037
#define OPT_NO_WAIT 1038
#define OPT_MISSING_SLOTS 1039
#define OPT_REPMGRD_NO_PAUSE 1040
#define OPT_VERSION_NUMBER 1041
#define OPT_DATA_DIRECTORY_CONFIG 1042
#define OPT_COMPACT 1043
#define OPT_DETAIL 1044
#define OPT_REPMGRD_FORCE_UNPAUSE 1045
#define OPT_REPLICATION_CONFIG_OWNER 1046
#define OPT_DB_CONNECTION 1047
#define OPT_VERIFY_BACKUP 1048
#define OPT_RECOVERY_MIN_APPLY_DELAY 1049
/* These options are for internal use only */
#define OPT_CONFIG_ARCHIVE_DIR 2001
#define OPT_DISABLE_WAL_RECEIVER 2002
#define OPT_ENABLE_WAL_RECEIVER 2003
#define OPT_DUMP_CONFIG 2004
#define OPT_CONFIG_ARCHIVE_DIR 1033
#define OPT_HAS_PASSFILE 1034
#define OPT_WAIT_START 1035
#define OPT_REPL_CONN 1036
#define OPT_REMOTE_NODE_ID 1037
#define OPT_REPLICATION_CONF_ONLY 1038
#define OPT_NO_WAIT 1039
#define OPT_MISSING_SLOTS 1040
#define OPT_REPMGRD_NO_PAUSE 1041
#define OPT_VERSION_NUMBER 1042
#define OPT_DATA_DIRECTORY_CONFIG 1043
#define OPT_COMPACT 1044
#define OPT_DISABLE_WAL_RECEIVER 1045
#define OPT_ENABLE_WAL_RECEIVER 1046
#define OPT_DETAIL 1047
#define OPT_REPMGRD_FORCE_UNPAUSE 1048
#define OPT_REPLICATION_CONFIG_OWNER 1049
/* deprecated since 4.0 */
#define OPT_CHECK_UPSTREAM_CONFIG 999
#define OPT_NODE 998
static struct option long_options[] =
@@ -127,7 +122,6 @@ static struct option long_options[] =
{"no-wait", no_argument, NULL, 'W'},
{"compact", no_argument, NULL, OPT_COMPACT},
{"detail", no_argument, NULL, OPT_DETAIL},
{"dump-config", no_argument, NULL, OPT_DUMP_CONFIG},
/* connection options */
{"dbname", required_argument, NULL, 'd'},
@@ -164,8 +158,6 @@ static struct option long_options[] =
{"upstream-node-id", required_argument, NULL, OPT_UPSTREAM_NODE_ID},
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
{"replication-conf-only", no_argument, NULL, OPT_REPLICATION_CONF_ONLY},
{"verify-backup", no_argument, NULL, OPT_VERIFY_BACKUP },
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY },
/* deprecate this once Pg11 and earlier are unsupported */
{"recovery-conf-only", no_argument, NULL, OPT_REPLICATION_CONF_ONLY},
@@ -197,7 +189,6 @@ static struct option long_options[] =
{"replication-connection", no_argument, NULL, OPT_REPL_CONN},
{"data-directory-config", no_argument, NULL, OPT_DATA_DIRECTORY_CONFIG},
{"replication-config-owner", no_argument, NULL, OPT_REPLICATION_CONFIG_OWNER},
{"db-connection", no_argument, NULL, OPT_DB_CONNECTION},
/* "node rejoin" options */
{"config-files", required_argument, NULL, OPT_CONFIG_FILES},
@@ -225,6 +216,9 @@ static struct option long_options[] =
{"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG},
/* previously used by "standby switchover" */
{"remote-config-file", required_argument, NULL, 'C'},
/* replaced by --node-id */
{"node", required_argument, NULL, OPT_NODE},
{NULL, 0, NULL, 0}
};

View File

@@ -33,14 +33,22 @@
#include "storage/shmem.h"
#include "storage/spin.h"
#include "utils/builtins.h"
#if (PG_VERSION_NUM >= 90400)
#include "utils/pg_lsn.h"
#endif
#include "utils/timestamp.h"
#include "lib/stringinfo.h"
#include "access/xact.h"
#include "utils/snapmgr.h"
#if (PG_VERSION_NUM >= 90400)
#include "pgstat.h"
#else
#define PGSTAT_STAT_PERMANENT_DIRECTORY "pg_stat"
#endif
#include "voting.h"
@@ -149,6 +157,8 @@ PG_FUNCTION_INFO_V1(get_wal_receiver_pid);
void
_PG_init(void)
{
elog(DEBUG1, "repmgr init");
if (!process_shared_preload_libraries_in_progress)
return;
@@ -165,6 +175,7 @@ _PG_init(void)
*/
prev_shmem_startup_hook = shmem_startup_hook;
shmem_startup_hook = repmgr_shmem_startup;
}

View File

@@ -181,8 +181,6 @@
#pg_ctl_options='' # Options to append to "pg_ctl"
#pg_basebackup_options='' # Options to append to "pg_basebackup"
# (Note: when cloning from Barman, repmgr will honour any
# --waldir/--xlogdir setting present in "pg_basebackup_options"
#rsync_options='' # Options to append to "rsync"
ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
@@ -211,9 +209,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# managing WAL archives (see: https://www.pgbarman.org )
#recovery_min_apply_delay= # If provided, "recovery_min_apply_delay" will be set to
# this value (PostgreSQL 9.4 and later). Value can be
# an integer representing milliseconds, or a string
# representing a period of time (e.g. '5 min').
# this value (PostgreSQL 9.4 and later).
#------------------------------------------------------------------------------
@@ -240,8 +236,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# for the new primary to become available
#standby_follow_timeout=15 # The max length of time (in seconds) to wait
# for the standby to connect to the primary
#standby_follow_restart=false # Restart the standby instead of sending a SIGHUP
# (only for PostgreSQL 13 and later)
#------------------------------------------------------------------------------
# "standby switchover" settings
@@ -302,7 +297,6 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
#connection_check_type=ping # How to check availability of the upstream node; valid options:
# 'ping': use PQping() to check if the node is accepting connections
# 'connection': execute a throwaway query on the current connection
# 'query': execute an SQL statement on the node via the existing connection
#reconnect_attempts=6 # Number of attempts which will be made to reconnect to an unreachable
# primary (or other upstream node)
#reconnect_interval=10 # Interval between attempts to reconnect to an unreachable
@@ -345,7 +339,6 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# WAL receivers
#primary_visibility_consensus=false # If "true", only continue with failover if no standbys have seen
# the primary node recently. *Must* be the same on all nodes.
#always_promote=false # Always promote a node, even if repmgr metadata is outdated
#failover_validation_command='' # Script to execute for an external mechanism to validate the failover
# decision made by repmgrd. One or both of the following parameter placeholders
# should be provided, which will be replaced by repmgrd with the appropriate

View File

@@ -1,6 +1,6 @@
# repmgr extension
comment = 'Replication manager for PostgreSQL'
default_version = '5.2'
default_version = '5.1'
module_pathname = '$libdir/repmgr'
relocatable = false
schema = repmgr

View File

@@ -74,15 +74,16 @@
#include "log.h"
#include "sysutils.h"
#define MIN_SUPPORTED_VERSION "9.4"
#define MIN_SUPPORTED_VERSION_NUM 90400
#define MIN_SUPPORTED_VERSION "9.3"
#define MIN_SUPPORTED_VERSION_NUM 90300
#define REPLICATION_TYPE_PHYSICAL 1
#define UNKNOWN_SERVER_VERSION_NUM -1
#define UNKNOWN_REPMGR_VERSION_NUM -1
#define UNKNOWN_TIMELINE_ID -1
#define UNKNOWN_SYSTEM_IDENTIFIER 0
#define UNKNOWN_DATA_CHECKSUM_VERSION -1
#define UNKNOWN_PID -1
#define UNKNOWN_REPLICATION_LAG -1
#define UNKNOWN_VALUE -1
@@ -96,58 +97,40 @@
#define ARCHIVE_STATUS_DIR_ERROR -1
#define NO_DEGRADED_MONITORING_ELAPSED -1
#define WALRECEIVER_DISABLE_TIMEOUT_VALUE 86400000 /* milliseconds */
/*
* Default command line option parameter values
* various default values - ensure repmgr.conf.sample is update
* if any of these are changed
*/
#define DEFAULT_WAIT_START 30 /* seconds */
/*
* Default configuration file parameter values - ensure repmgr.conf.sample
* is update if any of these are changed
*/
#define DEFAULT_USE_REPLICATION_SLOTS false
#define DEFAULT_USE_PRIMARY_CONNINFO_PASSWORD false
#define DEFAULT_PROMOTE_CHECK_TIMEOUT 60 /* seconds */
#define DEFAULT_PROMOTE_CHECK_INTERVAL 1 /* seconds */
#define DEFAULT_LOCATION "default"
#define DEFAULT_PRIORITY 100
#define DEFAULT_RECONNECTION_ATTEMPTS 6 /* seconds */
#define DEFAULT_RECONNECTION_INTERVAL 10 /* seconds */
#define DEFAULT_MONITORING_INTERVAL 2 /* seconds */
#define DEFAULT_ASYNC_QUERY_TIMEOUT 60 /* seconds */
#define DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT 60 /* seconds */
#define DEFAULT_PRIMARY_FOLLOW_TIMEOUT 60 /* seconds */
#define DEFAULT_STANDBY_FOLLOW_TIMEOUT 30 /* seconds */
#define DEFAULT_STANDBY_FOLLOW_RESTART false
#define DEFAULT_SHUTDOWN_CHECK_TIMEOUT 60 /* seconds */
#define DEFAULT_STANDBY_RECONNECT_TIMEOUT 60 /* seconds */
#define DEFAULT_NODE_REJOIN_TIMEOUT 60 /* seconds */
#define DEFAULT_ARCHIVE_READY_WARNING 16 /* WAL files */
#define DEFAULT_ARCHIVE_READY_CRITICAL 128 /* WAL files */
#define DEFAULT_REPLICATION_TYPE REPLICATION_TYPE_PHYSICAL
#define DEFAULT_REPLICATION_LAG_WARNING 300 /* seconds */
#define DEFAULT_REPLICATION_LAG_CRITICAL 600 /* seconds */
#define DEFAULT_WITNESS_SYNC_INTERVAL 15 /* seconds */
#define DEFAULT_WAIT_START 30 /* seconds */
#define DEFAULT_PROMOTE_CHECK_TIMEOUT 60 /* seconds */
#define DEFAULT_PROMOTE_CHECK_INTERVAL 1 /* seconds */
#define DEFAULT_SHUTDOWN_CHECK_TIMEOUT 60 /* seconds */
#define DEFAULT_STANDBY_RECONNECT_TIMEOUT 60 /* seconds */
#define DEFAULT_NODE_REJOIN_TIMEOUT 60 /* seconds */
#define DEFAULT_WAL_RECEIVE_CHECK_TIMEOUT 30 /* seconds */
#define DEFAULT_LOCATION "default"
#define DEFAULT_PRIORITY 100
#define DEFAULT_MONITORING_INTERVAL 2 /* seconds */
#define DEFAULT_RECONNECTION_ATTEMPTS 6 /* seconds */
#define DEFAULT_RECONNECTION_INTERVAL 10 /* seconds */
#define DEFAULT_MONITORING_HISTORY false
#define DEFAULT_DEGRADED_MONITORING_TIMEOUT -1 /* seconds */
#define DEFAULT_ASYNC_QUERY_TIMEOUT 60 /* seconds */
#define DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT 60 /* seconds */
#define DEFAULT_REPMGRD_STANDBY_STARTUP_TIMEOUT -1 /*seconds */
#define DEFAULT_STANDBY_DISCONNECT_ON_FAILOVER false
#define DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT 30 /* seconds */
#define DEFAULT_CONNECTION_CHECK_TYPE CHECK_PING
#define DEFAULT_PRIMARY_VISIBILITY_CONSENSUS false
#define DEFAULT_ALWAYS_PROMOTE false
#define DEFAULT_ELECTION_RERUN_INTERVAL 15 /* seconds */
#define DEFAULT_CHILD_NODES_CHECK_INTERVAL 5 /* seconds */
#define DEFAULT_CHILD_NODES_DISCONNECT_MIN_COUNT -1
#define DEFAULT_CHILD_NODES_CONNECTED_MIN_COUNT -1
#define DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS false
#define DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT 30 /* seconds */
#define DEFAULT_SSH_OPTIONS "-q -o ConnectTimeout=10"
#define DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS false
#define WALRECEIVER_DISABLE_TIMEOUT_VALUE 86400000 /* milliseconds */
#ifndef RECOVERY_COMMAND_FILE
#define RECOVERY_COMMAND_FILE "recovery.conf"
@@ -162,6 +145,6 @@
#define TABLESPACE_MAP "tablespace_map"
#endif
#define REPMGR_URL "https://repmgr.org/"
#endif /* _REPMGR_H_ */

View File

@@ -1,5 +1,5 @@
#define REPMGR_VERSION_DATE ""
#define REPMGR_VERSION "5.2.1"
#define REPMGR_VERSION_NUM 50201
#define REPMGR_RELEASE_DATE "2020-12-07"
#define REPMGR_VERSION "5.1.0"
#define REPMGR_VERSION_NUM 50100
#define REPMGR_RELEASE_DATE "2020-04-13"
#define PG_ACTUAL_VERSION_NUM

File diff suppressed because it is too large Load Diff

100
repmgrd.c
View File

@@ -39,9 +39,13 @@ static bool daemonize = true;
static bool show_pid_file = false;
static bool no_pid_file = false;
t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
t_node_info local_node_info = T_NODE_INFO_INITIALIZER;
PGconn *local_conn = NULL;
/* Collate command line errors here for friendlier reporting */
static ItemList cli_errors = {NULL, NULL};
@@ -252,7 +256,7 @@ main(int argc, char **argv)
* locations). If no conifguration file is available, or it can't be parsed
* parse_config() will abort anyway, with an appropriate message.
*/
load_config(config_file, verbose, false, argv[0]);
load_config(config_file, verbose, false, &config_file_options, argv[0]);
/* Determine pid file location, unless --no-pid-file supplied */
@@ -808,92 +812,42 @@ show_help(void)
bool
check_upstream_connection(PGconn **conn, const char *conninfo, PGconn **paired_conn)
check_upstream_connection(PGconn **conn, const char *conninfo)
{
/* Check the connection status twice in case it changes after reset */
bool twice = false;
log_debug("connection check type is \"%s\"",
print_connection_check_type(config_file_options.connection_check_type));
/*
* For the check types which do not involve using the existing database
* connection, we'll perform the actual check, then as an additional
* safeguard verify that the connection is still valid (as it might have
* gone away during a brief outage between checks).
*/
if (config_file_options.connection_check_type != CHECK_QUERY)
if (config_file_options.connection_check_type == CHECK_PING)
return is_server_available(conninfo);
if (config_file_options.connection_check_type == CHECK_CONNECTION)
{
bool success = true;
PGconn *test_conn = PQconnectdb(conninfo);
if (config_file_options.connection_check_type == CHECK_PING)
log_debug("check_upstream_connection(): attempting to connect to \"%s\"", conninfo);
if (PQstatus(test_conn) != CONNECTION_OK)
{
success = is_server_available(conninfo);
log_warning(_("unable to connect to \"%s\""), conninfo);
log_detail("\n%s", PQerrorMessage(test_conn));
success = false;
}
else if (config_file_options.connection_check_type == CHECK_CONNECTION)
{
/*
* This connection is thrown away, and we never execute a query on it.
*/
PGconn *test_conn = PQconnectdb(conninfo);
PQfinish(test_conn);
log_debug("check_upstream_connection(): attempting to connect to \"%s\"", conninfo);
if (PQstatus(test_conn) != CONNECTION_OK)
{
log_warning(_("unable to connect to \"%s\""), conninfo);
log_detail("\n%s", PQerrorMessage(test_conn));
success = false;
}
PQfinish(test_conn);
}
if (success == false)
return false;
if (PQstatus(*conn) == CONNECTION_OK)
return true;
/*
* Checks have succeeded, but the open connection to the primary has gone away,
* possibly due to a brief outage between monitoring intervals - attempt to
* reset it.
*/
log_notice(_("upstream is available but upstream connection has gone away, resetting"));
PQfinish(*conn);
*conn = establish_db_connection_quiet(conninfo);
if (PQstatus(*conn) == CONNECTION_OK)
{
if (paired_conn != NULL)
{
log_debug("resetting paired connection");
*paired_conn = *conn;
}
return true;
}
return false;
return success;
}
for (;;)
{
if (PQstatus(*conn) != CONNECTION_OK)
{
log_debug("check_upstream_connection(): upstream connection has gone away, resetting");
log_debug("check_upstream_connection(): connection not OK");
if (twice)
return false;
/* reconnect */
PQfinish(*conn);
*conn = establish_db_connection_quiet(conninfo);
if (paired_conn != NULL)
{
log_debug("resetting paired connection");
*paired_conn = *conn;
}
*conn = PQconnectdb(conninfo);
twice = true;
}
else
@@ -905,7 +859,7 @@ check_upstream_connection(PGconn **conn, const char *conninfo, PGconn **paired_c
goto failed;
/* execute a simple query to verify connection availability */
if (PQsendQuery(*conn, config_file_options.connection_check_query) == 0)
if (PQsendQuery(*conn, "SELECT 1") == 0)
{
log_warning(_("unable to send query to upstream"));
log_detail("%s", PQerrorMessage(*conn));
@@ -923,16 +877,8 @@ check_upstream_connection(PGconn **conn, const char *conninfo, PGconn **paired_c
return false;
/* reconnect */
log_debug("check_upstream_connection(): upstream connection not available, resetting");
PQfinish(*conn);
*conn = establish_db_connection_quiet(conninfo);
if (paired_conn != NULL)
{
log_debug("resetting paired connection");
*paired_conn = *conn;
}
*conn = PQconnectdb(conninfo);
twice = true;
}
}

View File

@@ -17,12 +17,13 @@ extern volatile sig_atomic_t got_SIGHUP;
extern MonitoringState monitoring_state;
extern instr_time degraded_monitoring_start;
extern t_configuration_options config_file_options;
extern t_node_info local_node_info;
extern PGconn *local_conn;
extern bool startup_event_logged;
extern char pid_file[MAXPGPATH];
bool check_upstream_connection(PGconn **conn, const char *conninfo, PGconn **paired_conn);
bool check_upstream_connection(PGconn **conn, const char *conninfo);
void try_reconnect(PGconn **conn, t_node_info *node_info);
int calculate_elapsed(instr_time start_time);

View File

@@ -386,9 +386,12 @@ output_check_status(CheckStatus status)
}
return "UNKNOWN";
}
/*
* Escape a string for use as a parameter in recovery.conf
* Caller must free returned value
@@ -430,6 +433,7 @@ escape_string(PGconn *conn, const char *string)
/*
* simple function to escape double quotes only
*/
void
escape_double_quotes(char *string, PQExpBufferData *out)
{
@@ -560,10 +564,3 @@ parse_follow_command(char *parsed_command, char *template, int node_id)
return;
}
const char *
format_bool(bool value)
{
return value == true ? "true" : "false";
}

View File

@@ -166,6 +166,4 @@ extern char *trim(char *s);
extern void
parse_follow_command(char *parsed_command, char *template, int node_id);
extern const char *format_bool(bool value);
#endif /* _STRUTIL_H_ */

View File

@@ -57,56 +57,27 @@ _local_command(const char *command, PQExpBufferData *outputbuf, bool simple, int
char output[MAXLEN];
int retval = 0;
bool success;
char tmpfile_path[MAXPGPATH];
const char *tmpdir = getenv("TMPDIR");
int fd;
PQExpBufferData command_final;
if (!tmpdir)
tmpdir = "/tmp";
maxpath_snprintf(tmpfile_path, "%s/repmgr_command.XXXXXX",
tmpdir);
fd = mkstemp(tmpfile_path);
if (fd < 1)
{
log_error(_("unable to open temporary file"));
return false;
}
initPQExpBuffer(&command_final);
appendPQExpBufferStr(&command_final, command);
appendPQExpBuffer(&command_final, " 2>%s", tmpfile_path);
log_verbose(LOG_DEBUG, "executing:\n %s", command_final.data);
log_verbose(LOG_DEBUG, "executing:\n %s", command);
if (outputbuf == NULL)
{
retval = system(command_final.data);
termPQExpBuffer(&command_final);
retval = system(command);
if (return_value != NULL)
*return_value = WEXITSTATUS(retval);
close(fd);
return (retval == 0) ? true : false;
}
fp = popen(command_final.data, "r");
fp = popen(command, "r");
if (fp == NULL)
{
log_error(_("unable to execute local command:\n%s"), command_final.data);
termPQExpBuffer(&command_final);
close(fd);
log_error(_("unable to execute local command:\n%s"), command);
return false;
}
termPQExpBuffer(&command_final);
while (fgets(output, MAXLEN, fp) != NULL)
{
@@ -120,32 +91,11 @@ _local_command(const char *command, PQExpBufferData *outputbuf, bool simple, int
retval = pclose(fp);
/* 141 = SIGPIPE */
/* */
success = (WEXITSTATUS(retval) == 0 || WEXITSTATUS(retval) == 141) ? true : false;
log_verbose(LOG_DEBUG, "result of command was %i (%i)", WEXITSTATUS(retval), retval);
/*
* Append any captured STDERR output
*/
fp = fopen(tmpfile_path, "r");
/*
* Not critical if we can't open the file
*/
if (fp != NULL)
{
while (fgets(output, MAXLEN, fp) != NULL)
{
appendPQExpBufferStr(outputbuf, output);
}
fclose(fp);
}
unlink(tmpfile_path);
if (return_value != NULL)
*return_value = WEXITSTATUS(retval);
@@ -154,7 +104,6 @@ _local_command(const char *command, PQExpBufferData *outputbuf, bool simple, int
else
log_verbose(LOG_DEBUG, "local_command(): no output returned");
return success;
}
@@ -169,13 +118,28 @@ remote_command(const char *host, const char *user, const char *command, const ch
{
FILE *fp;
PQExpBufferData ssh_command;
PQExpBufferData ssh_host;
char output[MAXLEN] = "";
initPQExpBuffer(&ssh_host);
if (*user != '\0')
{
appendPQExpBuffer(&ssh_host, "%s@", user);
}
appendPQExpBufferStr(&ssh_host, host);
initPQExpBuffer(&ssh_command);
make_remote_command(host, user, command, ssh_options, &ssh_command);
appendPQExpBuffer(&ssh_command,
"ssh -o Batchmode=yes %s %s %s",
ssh_options,
ssh_host.data,
command);
termPQExpBuffer(&ssh_host);
log_debug("remote_command():\n %s", ssh_command.data);
@@ -223,32 +187,6 @@ remote_command(const char *host, const char *user, const char *command, const ch
}
void
make_remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *ssh_command)
{
PQExpBufferData ssh_host;
initPQExpBuffer(&ssh_host);
if (*user != '\0')
{
appendPQExpBuffer(&ssh_host, "%s@", user);
}
appendPQExpBufferStr(&ssh_host, host);
appendPQExpBuffer(ssh_command,
"ssh -o Batchmode=yes %s %s %s",
ssh_options,
ssh_host.data,
command);
termPQExpBuffer(&ssh_host);
}
pid_t
disable_wal_receiver(PGconn *conn)
{
@@ -289,19 +227,10 @@ disable_wal_receiver(PGconn *conn)
if (wal_retrieve_retry_interval < WALRECEIVER_DISABLE_TIMEOUT_VALUE)
{
bool success;
log_notice(_("setting \"wal_retrieve_retry_interval\" to %i milliseconds"),
new_wal_retrieve_retry_interval);
alter_system_int(conn, "wal_retrieve_retry_interval", new_wal_retrieve_retry_interval);
success = pg_reload_conf(conn);
if (success == false)
{
log_warning(_("unable to reload configuration"));
return UNKNOWN_PID;
}
pg_reload_conf(conn);
}
/*
@@ -363,12 +292,6 @@ enable_wal_receiver(PGconn *conn, bool wait_startup)
/* make timeout configurable */
int i, timeout = 30;
if (PQstatus(conn) != CONNECTION_OK)
{
log_error(_("database connection not available"));
return UNKNOWN_PID;
}
if (is_superuser_connection(conn, NULL) == false)
{
log_error(_("superuser connection required"));
@@ -409,13 +332,7 @@ enable_wal_receiver(PGconn *conn, bool wait_startup)
return UNKNOWN_PID;
}
success = pg_reload_conf(conn);
if (success == false)
{
log_warning(_("unable to reload configuration"));
return UNKNOWN_PID;
}
pg_reload_conf(conn);
}
else
{

View File

@@ -24,7 +24,6 @@ extern bool local_command_return_value(const char *command, PQExpBufferData *out
extern bool local_command_simple(const char *command, PQExpBufferData *outputbuf);
extern bool remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *outputbuf);
extern void make_remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *ssh_command);
extern pid_t disable_wal_receiver(PGconn *conn);
extern pid_t enable_wal_receiver(PGconn *conn, bool wait_startup);