Compare commits

...

106 Commits

Author SHA1 Message Date
Ian Barwick
39003be5a3 Change version number from 5.0 to 5.0.0
Previous initial "major" releases were two-element only (e.g. 4.4);
beginning from repmgr 5 we want to ensure all version numbers have
three elements, for general consistency, including the generation
of package names.
2019-10-15 11:01:03 +09:00
Ian Barwick
c27f134e50 doc: split notes about PostgreSQL 9.3 and 9.4 support into a new subsection 2019-10-15 10:46:14 +09:00
Ian Barwick
5a619244ee doc: add note about 4.x development policy 2019-10-15 10:42:52 +09:00
Ian Barwick
b5448def7e doc: add repmgr 5.0 release date 2019-10-15 10:30:34 +09:00
Ian Barwick
af1c889bc3 Bump version to 5.0 2019-10-14 15:10:40 +09:00
Ian Barwick
2304584679 Fix handling of upstream node change check
repmgrd has a check to see if the upstream node has unexpectedly
changed, e.g. if the repmgrd service is paused and the PostgreSQL
instance has been pointed to another node.

However this check was relying on the node record on the local node
being up-to-date, which may not be the case immediately after a
failover, when the node is still replaying records updated prior
to the node's own record being updated. In this case it will
mistakenly assume the node is following the original primary
and attempt to restart monitoring, which will fail as the original
primary is no longer available.

To prevent this, we check against the node's record on the upstream
node.

Addresses issue noted in GitHub #587 and #588.
2019-10-14 12:28:04 +09:00
Ian Barwick
4aaa24a5f8 Update configuration file conversion script
Ensure output is quoted.
2019-10-08 10:59:20 +09:00
Ian Barwick
ea29af2e68 Remove mistakenly added file 2019-10-08 10:33:27 +09:00
Ian Barwick
7053ed5b51 doc: update links to Barman documentation 2019-10-08 10:30:41 +09:00
Ian Barwick
a845d7126d doc: minor updates to "repmgr standby clone" reference
- remove references to repmgr 4.0.4 (present because feature
  was added in a minor release, but that's a long time ago)
- note configuration is appended to postgresql.auto.conf
2019-10-08 10:21:26 +09:00
Ian Barwick
1c317059cd doc: clarify use of --recovery-conf-only 2019-10-08 10:03:44 +09:00
Ian Barwick
dcf5bfb649 doc: fix minor formatting error 2019-10-08 09:48:04 +09:00
Ian Barwick
dbd3d34c89 doc; update repmgr.conf.sample
Note new PostgreSQL-style parsing and add link to documentation.
2019-10-07 18:28:16 +09:00
Ian Barwick
f3c3320a9c doc: update examples in quickstart guide 2019-10-07 18:27:38 +09:00
Ian Barwick
cfc5bde219 doc: update repmgr.conf samples in Barman section
From repmgr 5.x we really need to quote all the things.
2019-10-07 12:05:26 +09:00
Ian Barwick
ea57269569 doc: improve instructions for cloning from Barman 2019-10-07 11:56:40 +09:00
Ian Barwick
b885337abc Minor code formatting fix 2019-10-07 10:48:35 +09:00
Ian Barwick
e8b5b92893 doc: improve Barman standby clone example 2019-10-03 15:52:32 +09:00
Ian Barwick
02bdcf657f doc: add link to FAQ section about 3rd party package compatibilty 2019-10-03 15:23:57 +09:00
Ian Barwick
405f70f769 doc: clarify barman-cli package usage
As of Barman 2.8, the barman-cli package has been merged with the core
Barman code, so is only requires an explicit mention for Barman 2.0 ~ 2.7.
2019-10-03 14:35:45 +09:00
Ian Barwick
577ca35de5 doc: remove reference to Barman 1.x.
Barman 1.x is very outdated and should no longer be used anyway.
2019-10-03 14:25:17 +09:00
Martín Marqués
a557f2d69e Typo in the documentation of the repmgrd configuration
Signed-off-by: Martín Marqués <martin.marques@2ndquadrant.com>
2019-10-01 09:44:30 -03:00
Ian Barwick
1196821457 doc: update FAQ
Note pg_monitor default role as well, and link to relevant section
in the PostgreSQL documentation.
2019-10-01 10:29:44 +09:00
Ian Barwick
c1d464f3da doc: add FAQ entry about 3rd-party PostgreSQL packages 2019-10-01 10:07:25 +09:00
Ian Barwick
4646bbc289 doc: standardize doc formatting
Indent with two spaces instead of one.
2019-10-01 09:44:04 +09:00
Ian Barwick
bb9a0c2297 doc: update FAQ
Add note about repmgr 5 in the "What's the difference between the repmgr
versions?" item.
2019-10-01 09:41:49 +09:00
Ian Barwick
1ed8b1067a Prevent use of backend string functions
From PostgreSQL 12, port.h forcibly redefines printf() et al to use
the versions defined by PostgreSQL (pg_printf() et al). As this
causes linking issues in build environments which build pre-Pg12
versions against Pg12's libpq, ensure relevant macros defined
in port.h are undefined.
2019-09-26 12:47:51 +09:00
Ian Barwick
a502b2cf96 Move function parse_repmgr_version() to a more appropriate location 2019-09-24 13:14:03 +09:00
Ian Barwick
8e6d111f32 Refactor remote_command() function
Use dynamic rather than fixed buffer to generate the command string.
2019-09-24 13:14:03 +09:00
Ian Barwick
50d4cee877 doc: link to GitHub release page 2019-09-24 11:37:25 +09:00
Ian Barwick
db99e98236 doc: note flex required for source builds
From 5.0 onwards.
2019-09-24 11:34:24 +09:00
Ian Barwick
10f00b8822 repmgr: pass explicitly provided log level when executing repmgr remotely
This makes it possible to return log output when executing repmgr
remotely at a different level to the one defined in the remote
repmgr's repmgr.conf.

This is particularly useful when DEBUG output is required.
2019-09-17 15:38:43 +09:00
Ian Barwick
98e96f4375 doc: clarify configuration file changes 2019-09-17 13:01:03 +09:00
Ian Barwick
8489fd061f doc: update release notes 2019-09-17 11:19:05 +09:00
Ian Barwick
56aae22b6c doc: update release notes 2019-09-17 11:00:04 +09:00
Ian Barwick
bce039f336 doc: add clarifications to "Standby disconnection on failover" 2019-09-05 18:00:56 +09:00
Ian Barwick
19190153a0 doc: clarify prerequisites for executing the "repmgr service" commands
Note node accessibility requirements on each command's reference
page.
2019-08-30 12:07:37 +09:00
Ian Barwick
b1c6418e8d doc: expand "see also" links for "repmgr (service|daemon)" commands 2019-08-30 11:51:32 +09:00
Ian Barwick
12491c6aa1 doc: add note about starting/stopping repmgrd on individual nodes 2019-08-30 11:51:11 +09:00
Ian Barwick
d5b806eeff Fix pg_control handling for PostgreSQL 12
"FullTransactionId" is not present in earlier versions, so we'll
have to #ifdef that out for those.

We should probably add more elegant checks to ensure that repmgr
is being executed against the PostgreSQL version it was built against.
In a normal production environment that will be the case as the
matching package will have been installed, so version mismatches
don't usually occur.

Note that while currently most aspects of the repmgr client are
compatible across PostgreSQL versions, the repmgrd code is very
version specific, so version specificity is a given anyway.
2019-08-29 17:22:01 +09:00
Ian Barwick
37bfe6ee4f Have "make clean" remove "repmgr_version.h" 2019-08-29 17:17:33 +09:00
Ian Barwick
97c21ed907 doc: update package examples
Use recent version numbers where appropriate.
2019-08-29 15:57:49 +09:00
Ian Barwick
3ea47522cd doc: clarify "repmgr daemon (start|stop)" purpose 2019-08-28 16:28:29 +09:00
Ian Barwick
935be3d669 doc: update example PostgreSQL version references to Pg12 2019-08-28 15:52:14 +09:00
Ian Barwick
494444869d doc: updates for repmgr 5.x and PostgreSQL 12 2019-08-28 15:33:01 +09:00
Ian Barwick
677a94513e repmgr: note that --dry-run is not effective with "repmgr service status" 2019-08-28 15:14:35 +09:00
Ian Barwick
931da14df1 Rename some "repmgr daemon ..." commands to "repmgr service ..."
"repmgr daemon" can be interpreted to mean the commands affect the local
daemon process only. Rename the commands which affect the entire cluster
to "repmgr service ...".

The "repmgr daemon ..." form of the affected commands is retained for backwards
 compatibility.
2019-08-28 14:58:11 +09:00
Ian Barwick
3e812f6e91 repmgrd: always emit NOTICE when attempting to follow a new primary
Previously, if a standby's repmgrd was looping in degraded monitoring
mode looking for a new primary to follow, once a new primary was
detected the follow command would be executed without any prior
logging at non-DEBUG log levels.
2019-08-26 16:02:41 +09:00
Ian Barwick
ffc7b7817b doc: update HISTORY
Note PostgreSQL support.
2019-08-22 15:42:29 +09:00
Ian Barwick
fb6352735a The next major release will be 5.0.
4.5 was a placeholder release number in case a major release was required
prior to the release of Pg12.
2019-08-22 15:15:56 +09:00
Ian Barwick
f122c44a77 Fix debugging output 2019-08-21 15:52:08 +09:00
Ian Barwick
b4e6922a26 doc: add further note about configuration file changes 2019-08-20 16:36:58 +09:00
Ian Barwick
6e200d32a4 doc: add note about max_wal_senders in Pg12 and later 2019-08-20 11:17:36 +09:00
Ian Barwick
507b27c05d Mark set_repmgrd_pid() as "RETURNS NULL ON NULL INPUT"
When unsetting the PID, we'll want to set the pidfile to NULL rather
than an empty string.
2019-08-19 20:15:30 +09:00
Ian Barwick
28f4536372 repmgrd: fix pidfile handling at shutdown 2019-08-19 17:55:18 +09:00
Ian Barwick
2ec761a979 doc: update "repmgr standby clone" reference for PostgreSQL 12
Mainly replace "recovery.conf" with the more generic "replication
configuration".
2019-08-19 10:40:42 +09:00
Ian Barwick
b5225a2662 Support --create-recovery-conf in Pg12 2019-08-19 10:40:38 +09:00
Ian Barwick
d5ed38f573 Make "standby follow" work in Pg12 2019-08-19 10:40:35 +09:00
Ian Barwick
2a37e28304 write standby.signal 2019-08-19 10:40:31 +09:00
Ian Barwick
9eb6ce52b4 Write replication configuration for Pg12 and later 2019-08-19 10:40:27 +09:00
Ian Barwick
9e072c6773 Update code comment with list of options for "standby clone" 2019-08-15 18:11:47 +09:00
Ian Barwick
72daa38baa durable_rename() only available externally from Pg10 2019-08-14 20:28:05 +09:00
Ian Barwick
f5044465cb Add function to safely modify postgresql.auto.conf
This is required for PostgreSQL 12 and later.
2019-08-14 16:57:42 +09:00
Ian Barwick
4ebc43fd63 Clean up variable usage in do_node_status()
Variable with the same name existed both at function level and within
local code blocks.
2019-08-14 14:15:41 +09:00
Ian Barwick
a1775237d4 Update comment
Deprecated command line option --data-dir was removed in commit 5ca0b57,
but a comment still referred to it.
2019-08-14 14:12:09 +09:00
Ian Barwick
94ba635811 Define our own PG_AUTOCONF_FILENAME 2019-08-13 16:48:44 +09:00
Ian Barwick
c0f3990973 Use appendPQExpBufferStr where appropriate 2019-08-13 16:32:40 +09:00
Ian Barwick
d0f5ee1851 doc: mention not to use --siblings-follow in the repmgrd promote command
This is noted on the "repmgr standby promote" page but needs repeating
on the repmgrd configuration page.
2019-08-13 11:49:52 +09:00
Ian Barwick
75c0987e79 repmgrd: emit node name when reporting follow target attach error
This is consistent with other error messages.
2019-08-13 11:02:52 +09:00
Ian Barwick
68be86349b Add function to parse version string returned by "repmgr --version" 2019-08-08 13:47:19 +09:00
Ian Barwick
666c6f5140 "standby clone": improve error messages related to extension status
Previously repmgr would emit the "repmgr extension not found on source node"
which depending on context is somewhat misleading, as it may exist
but not be installed, or the user may be attempting to clone from the
wrong database.
2019-08-07 16:41:27 +09:00
Ian Barwick
3df65d0eb3 Simplify pg_has_role() call
Specifying CURRENT_USER is superfluous here.
2019-08-07 14:43:56 +09:00
Ian Barwick
38b373e6df "node check": check role membership when trying to read pg_settings
From PostgreSQL 10, a member of the default roles "pg_monitor" and/or
"pg_read_all_settings" can read pg_settings without requiring superuser
privileges.

Previously, a hint was being emitted about making the repmgr user a
member of one of those groups, but no check for membership was being
made, meaning the check could only be run by a superuser.
2019-08-07 14:26:48 +09:00
Ian Barwick
10870503d1 Add missing field in init_replication_info()
"upstream_node_id" was not being initialised.
2019-08-06 21:24:37 +09:00
Ian Barwick
5ca0b57d0c Remove command-line options deprecated since repmgr 3.3
The following options have long since been deprecated, and any attempt
to use them results only in a warning that they are no longer valid:

  --data-dir
  --no-conninfo-password
  --recovery-min-apply-delay
2019-08-05 16:26:12 +09:00
Ian Barwick
7d20aea606 Fix typo in comment 2019-08-01 15:20:44 +09:00
Ian Barwick
424d92e311 doc: fix typo 2019-08-01 14:14:37 +09:00
Ian Barwick
8d55cab25e Convert configuration file parsing to use flex
Previously, repmgr was using a very simple ad-hoc string-based parser,
which had various limitations and allowed configuration files to be
created in a way which could cause confusion and/or unexpected
behaviour.

For example, it accepted strings enclosed in single quotes, but treated
strings enclosed in double quotes literally. A node_name defined thusly:

    node_name="somenode"

would result in the literal value '"somenode"' being used, which could
lead to unobvious errors along the lines of:

    no record found for ""somenode""

The configuration file parser has been adapted from the one used by
PostgreSQL itself, so behaves more-or-less identically (though some
functions such as file inclusion are not supported in repmgr).

This makes configuration parsing more robust and consistent;
additionally, error reporting will be more precise.

Note this does mean that some repmgr.conf items previously accepted
as valid by repmgr will now be rejected; in particular this includes
strings containing spaces which are not enclosed in single quotes.
2019-08-01 10:17:20 +09:00
Ian Barwick
ab7e527af8 More sed tweakage 2019-07-26 21:25:16 +09:00
Ian Barwick
9274fdc6ba Use more portable sed invocation
Works on FreeBSD too.
2019-07-26 20:13:15 +09:00
Ian Barwick
018394faa2 Define PG_ACTUAL_VERSION_NUM
Due to [insert reason here], in the Debian package build process (and
only there), when building frontend code PG_VERSION_NUM appears to be
from the newest libpq-dev version installed, and does not necessarily
match the version of the server the code is being built against.

To work around this distribution-specific package build issue, we'll
define our own substitution variable which is taken from the value
provided in Makefile.global.
2019-07-26 18:12:07 +09:00
Ian Barwick
532a5207e2 More portable usage of sed in Makefile 2019-07-26 18:08:16 +09:00
Ian Barwick
5bf9605286 Revert "Convert configuration file parsing to use flex"
This reverts commit c6ca183247.

Backing out this patch for now as the Debian build system doesn't
seem to like it, even though it builds just fine on Debian itself.
2019-07-18 10:19:18 +09:00
Ian Barwick
215f4bb9d9 doc: add note about parallel restore from Barman 2019-07-18 09:23:09 +09:00
Ian Barwick
75a381ed27 doc: add reminder to update release date in version header 2019-07-09 11:37:50 +09:00
Ian Barwick
a26b7e29a0 Add release date placeholder 2019-07-09 11:33:09 +09:00
Ian Barwick
d09214b83d doc: define entity &releasedate;
This should be used wherever we need to show the latest release
date.

Don't use this in the release notes however, as it will be easy to
forget to update it when adding notes for a new release.
2019-07-09 11:32:43 +09:00
Ian Barwick
822abbbe5b doc: update compatibility matrix
Use &repmgrversion; entity to generate the current version number and
prevent document bitrot.

Also define a "release-current" ID attribute for ease of linking to
the current release notes.

Per notification from the mailing list.
2019-07-09 11:07:04 +09:00
Ian Barwick
d51704e272 doc: update release notes 2019-07-04 11:21:37 +09:00
Ian Barwick
c6ca183247 Convert configuration file parsing to use flex
Previously, repmgr was using a very simple ad-hoc string-based parser,
which had various limitations and allowed configuration files to be
created in a way which could cause confusion and/or unexpected
behaviour.

For example, it accepted strings enclosed in single quotes, but treated
strings enclosed in double quotes literally. A node_name defined thusly:

    node_name="somenode"

would result in the literal value '"somenode"' being used, which could
lead to unobvious errors along the lines of:

    no record found for ""somenode""

The configuration file parser has been adapted from the one used by
PostgreSQL itself, so behaves more-or-less identically (though some
functions such as file inclusion are not supported in repmgr).

This makes configuration parsing more robust and consistent;
additionally, error reporting will be more precise.

Note this does mean that some repmgr.conf items previously accepted
as valid by repmgr will now be rejected; in particular this includes
strings containing spaces which are not enclosed in single quotes.
2019-07-03 12:18:01 +09:00
Ian Barwick
b125628f7b doc: update release notes
Finalize release date.
2019-06-26 15:57:42 +09:00
Ian Barwick
cd550fcd5c doc: clean up release notes
Remove tabs.
2019-06-14 16:53:45 +09:00
Ian Barwick
b6dc8af6c7 doc: fix typo 2019-06-12 16:28:28 +09:00
Ian Barwick
09979eaa91 note that "standby follow" requires a primary to be available
While it's technically possible to have a standby follow another
standby while the primary is not available, repmgr will not be able
to update its metadata, which will cause Confusion and Chaos.

Update the documentation to make this clear, and provide a more helpful
error message if this situation occurs. The operation previously
failed anyway, but with an unhelpful message about not being able to
find a node record.
2019-06-11 15:14:17 +09:00
Ian Barwick
3469152314 doc: document optional configuration settings 2019-06-10 14:08:34 +09:00
Ian Barwick
3bf308509f doc: add missing space 2019-06-10 09:02:11 +09:00
Ian Barwick
01852f7e3a doc: improve repmgr.conf settings documentation 2019-06-07 12:48:36 +09:00
Ian Barwick
36a09a5c4b doc: improve configuration documentation 2019-06-07 12:16:04 +09:00
Ian Barwick
7180e2bed7 Canonicalize the data directory path when parsing the configuration file
This ensures the provided path matches the path PostgreSQL reports as its
data directory.
2019-06-07 09:48:01 +09:00
Ian Barwick
6aca764d5e Fix extension version number query 2019-06-06 12:46:12 +09:00
Ian Barwick
341421f8e0 standby follow: remove some ineffective code
For some reason we were taking the trouble to extract an appliction_name
from the local node's conninfo, but this was being subsequently overwritten
with the node name (which is what we want anyway).
2019-06-06 12:12:33 +09:00
Ian Barwick
f5d29f6591 doc: update release notes 2019-06-06 11:30:30 +09:00
Ian Barwick
c0ea5ffa04 Ensure parsed value of --upstream-conninfo is written to recovery.conf
Previously it was being parsed (a step which ensures any "application_name"
set by the caller is changed to the node name), but the original string
was being copied to "primary_conninfo" anyway.
2019-06-06 11:30:24 +09:00
Ian Barwick
aa44c8abf1 Add .sql extension files for 4.5 2019-06-04 15:58:22 +09:00
Ian Barwick
456a95f159 Bump master branch to 4.5dev
Note that the next release is intended to be 5.0 to coincide with the
release of PostgreSQL 12; 4.5 is currently a placeholder in case we
need to push out a feature release before then.
2019-06-04 14:26:48 +09:00
Ian Barwick
703a483e81 Remove redundant comment in .sql files 2019-06-04 13:46:10 +09:00
74 changed files with 4320 additions and 2156 deletions

3
.gitignore vendored
View File

@@ -53,3 +53,6 @@ repmgr
repmgrd
repmgr4
repmgrd4
# generated files
configfile-scan.c

15
HISTORY
View File

@@ -1,4 +1,13 @@
4.4 2019-??-??
5.0 2019-10-15
general: add PostgreSQL 12 support (Ian)
general: parse configuration file using flex (Ian)
repmgr: rename "repmgr daemon ..." commands to "repmgr service ..." (Ian)
repmgr: improve data directory check (Ian)
repmgr: improve extension check during "standby clone" (Ian)
repmgr: pass provided log level when executing repmgr remotely (Ian)
repmgrd: fix handling of upstream node change check (Ian)
4.4 2019-06-27
repmgr: improve "daemon status" output (Ian)
repmgr: add "--siblings-follow" option to "standby promote" (Ian)
repmgr: add "--repmgrd-force-unpause" option to "standby switchover" (Ian)
@@ -6,6 +15,8 @@
an existing directory is being overwritten (Ian)
repmgr: improve "--dry-run" behaviour for "standby promote" and
"standby switchover" (Ian)
repmgr: when running "standby clone" with the "--upstream-conninfo" option
ensure that "application_name" is set correctly in "primary_conninfo" (Ian)
repmgr: ensure "--dry-run" together with --force when running "standby clone"
in barman mode does not modify an existing data directory (Ian)
repmgr: improve "--dry-run" output when running "standby clone" in
@@ -21,6 +32,8 @@
repmgr: prevent a witness server being registered on the cluster primary (John)
repmgr: ensure BDR2-specific functionality cannot be used on
BDR3 and later (Ian)
repmgr: canonicalize the data directory path (Ian)
repmgr: note that "standby follow" requires a primary to be available (Ian)
repmgrd: monitor standbys attached to primary (Ian)
repmgrd: add "primary visibility consensus" functionality (Ian)
repmgrd: fix memory leak which occurs while the monitored PostgreSQL

View File

@@ -2,6 +2,7 @@
# Makefile.global.in
# @configure_input@
# Can only be built using pgxs
USE_PGXS=1
@@ -14,6 +15,9 @@ ifeq ($(vpath_build),yes)
VPATH := $(repmgr_abs_srcdir)/$(repmgr_subdir)
USE_VPATH :=$(VPATH)
endif
SED=@SED@
GIT_WORK_TREE=${repmgr_abs_srcdir}
GIT_DIR=${repmgr_abs_srcdir}/.git
export GIT_DIR
@@ -24,4 +28,13 @@ include $(PGXS)
-include ${repmgr_abs_srcdir}/Makefile.custom
REPMGR_VERSION=$(shell awk '/^\#define REPMGR_VERSION / { print $3; }' ${repmgr_abs_srcdir}/repmgr_version.h.in | cut -d '"' -f 2)
REPMGR_RELEASE_DATE=$(shell awk '/^\#define REPMGR_RELEASE_DATE / { print $3; }' ${repmgr_abs_srcdir}/repmgr_version.h.in | cut -d '"' -f 2)
FLEX = flex
##########################################################################
#
# Global targets and rules
%.c: %.l
$(FLEX) $(FLEXFLAGS) -o'$@' $<

View File

@@ -19,7 +19,9 @@ DATA = \
repmgr--4.2--4.3.sql \
repmgr--4.3.sql \
repmgr--4.3--4.4.sql \
repmgr--4.4.sql
repmgr--4.4.sql \
repmgr--4.4--5.0.sql \
repmgr--5.0.sql
REGRESS = repmgr_extension
@@ -51,13 +53,16 @@ $(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-bdr.o repmgr-action-cluster.o repmgr-action-node.o repmgr-action-daemon.o \
configfile.o log.o strutil.o controldata.o dirutil.o compat.o dbutils.o sysutils.o
REPMGRD_OBJS = repmgrd.o repmgrd-physical.o repmgrd-bdr.o configfile.o log.o dbutils.o strutil.o controldata.o compat.o sysutils.o
repmgr-action-bdr.o repmgr-action-cluster.o repmgr-action-node.o repmgr-action-service.o repmgr-action-daemon.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 repmgrd-bdr.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
sed '0,/REPMGR_VERSION_DATE/s,\(REPMGR_VERSION_DATE\).*,\1 "$(DATE)",' $< >$@
$(SED) -E 's/REPMGR_VERSION_DATE.*""/REPMGR_VERSION_DATE "$(DATE)"/' $< >$@; \
$(SED) -i -E 's/PG_ACTUAL_VERSION_NUM/PG_ACTUAL_VERSION_NUM $(VERSION_NUM)/' $@
configfile-scan.c: configfile-scan.l
$(REPMGR_CLIENT_OBJS): repmgr-client.h repmgr_version.h
@@ -98,6 +103,7 @@ maintainer-clean: additional-maintainer-clean
additional-clean:
rm -f *.o
rm -f repmgr_version.h
$(MAKE) -C doc clean
additional-maintainer-clean: clean

366
configfile-scan.l Normal file
View File

@@ -0,0 +1,366 @@
/*
* Scanner for the configuration file
*/
%{
#include <setjmp.h>
#include "repmgr.h"
#include "configfile.h"
/*
* flex emits a yy_fatal_error() function that it calls in response to
* critical errors like malloc failure, file I/O errors, and detection of
* internal inconsistency. That function prints a message and calls exit().
* Mutate it to instead call our handler, which jumps out of the parser.
*/
#undef fprintf
#define fprintf(file, fmt, msg) CONF_flex_fatal(msg)
enum
{
CONF_ID = 1,
CONF_STRING = 2,
CONF_INTEGER = 3,
CONF_REAL = 4,
CONF_EQUALS = 5,
CONF_UNQUOTED_STRING = 6,
CONF_QUALIFIED_ID = 7,
CONF_EOL = 99,
CONF_ERROR = 100
};
static unsigned int ConfigFileLineno;
static const char *CONF_flex_fatal_errmsg;
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(FILE *fp, const char *config_file, KeyValueList *contents, t_configuration_options *options, ItemList *error_list, ItemList *warning_list);
%}
%option 8bit
%option never-interactive
%option nodefault
%option noinput
%option nounput
%option noyywrap
%option warn
%option prefix="CONF_yy"
SIGN ("-"|"+")
DIGIT [0-9]
HEXDIGIT [0-9a-fA-F]
UNIT_LETTER [a-zA-Z]
INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}*
EXPONENT [Ee]{SIGN}?{DIGIT}+
REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}?
LETTER [A-Za-z_\200-\377]
LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
ID {LETTER}{LETTER_OR_DIGIT}*
QUALIFIED_ID {ID}"."{ID}
UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
STRING \'([^'\\\n]|\\.|\'\')*\'
%%
\n ConfigFileLineno++; return CONF_EOL;
[ \t\r]+ /* eat whitespace */
#.* /* eat comment (.* matches anything until newline) */
{ID} return CONF_ID;
{QUALIFIED_ID} return CONF_QUALIFIED_ID;
{STRING} return CONF_STRING;
{UNQUOTED_STRING} return CONF_UNQUOTED_STRING;
{INTEGER} return CONF_INTEGER;
{REAL} return CONF_REAL;
= return CONF_EQUALS;
. return CONF_ERROR;
%%
extern bool
ProcessRepmgrConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list)
{
return ProcessConfigFile(fp, config_file, NULL, options, error_list, warning_list);
}
extern bool
ProcessPostgresConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
{
return ProcessConfigFile(fp, config_file, contents, NULL, error_list, warning_list);
}
static bool
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;
sigjmp_buf flex_fatal_jmp;
int errorcount;
int token;
if (sigsetjmp(flex_fatal_jmp, 1) == 0)
{
CONF_flex_fatal_jmp = &flex_fatal_jmp;
}
else
{
/*
* Regain control after a fatal, internal flex error. It may have
* corrupted parser state. Consequently, abandon the file, but trust
* that the state remains sane enough for yy_delete_buffer().
*/
item_list_append_format(error_list,
"%s at file \"%s\" line %u",
CONF_flex_fatal_errmsg, config_file, ConfigFileLineno);
OK = false;
goto cleanup;
}
/*
* Parse
*/
ConfigFileLineno = 1;
errorcount = 0;
lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
yy_switch_to_buffer(lex_buffer);
/* This loop iterates once per logical line */
while ((token = yylex()))
{
char *opt_name = NULL;
char *opt_value = NULL;
if (token == CONF_EOL) /* empty or comment line */
continue;
/* first token on line is option name */
if (token != CONF_ID && token != CONF_QUALIFIED_ID)
goto parse_error;
opt_name = pstrdup(yytext);
/* next we have an optional equal sign; discard if present */
token = yylex();
if (token == CONF_EQUALS)
token = yylex();
/* now we must have the option value */
if (token != CONF_ID &&
token != CONF_STRING &&
token != CONF_INTEGER &&
token != CONF_REAL &&
token != CONF_UNQUOTED_STRING)
goto parse_error;
if (token == CONF_STRING) /* strip quotes and escapes */
opt_value = CONF_scanstr(yytext);
else
opt_value = pstrdup(yytext);
/* now we'd like an end of line, or possibly EOF */
token = yylex();
if (token != CONF_EOL)
{
if (token != 0)
goto parse_error;
/* treat EOF like \n for line numbering purposes, cf bug 4752 */
ConfigFileLineno++;
}
/* OK, process the option name and value */
if (contents != NULL)
{
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)
break;
continue;
parse_error:
/* release storage if we allocated any on this line */
if (opt_name)
pfree(opt_name);
if (opt_value)
pfree(opt_value);
/* report the error */
if (token == CONF_EOL || token == 0)
{
item_list_append_format(error_list,
_("syntax error in file \"%s\" line %u, near end of line"),
config_file, ConfigFileLineno - 1);
}
else
{
item_list_append_format(error_list,
_("syntax error in file \"%s\" line %u, near token \"%s\""),
config_file, ConfigFileLineno, yytext);
}
OK = false;
errorcount++;
/*
* To avoid producing too much noise when fed a totally bogus file,
* give up after 100 syntax errors per file (an arbitrary number).
* Also, if we're only logging the errors at DEBUG level anyway, might
* as well give up immediately. (This prevents postmaster children
* from bloating the logs with duplicate complaints.)
*/
if (errorcount >= 100)
{
fprintf(stderr,
_("too many syntax errors found, abandoning file \"%s\"\n"),
config_file);
break;
}
/* resync to next end-of-line or EOF */
while (token != CONF_EOL && token != 0)
token = yylex();
/* break out of loop on EOF */
if (token == 0)
break;
}
cleanup:
yy_delete_buffer(lex_buffer);
return OK;
}
/*
* scanstr
*
* Strip the quotes surrounding the given string, and collapse any embedded
* '' sequences and backslash escapes.
*
* the string returned is palloc'd and should eventually be pfree'd by the
* caller.
*/
static char *
CONF_scanstr(const char *s)
{
char *newStr;
int len,
i,
j;
Assert(s != NULL && s[0] == '\'');
len = strlen(s);
Assert(s != NULL);
Assert(len >= 2);
Assert(s[len - 1] == '\'');
/* Skip the leading quote; we'll handle the trailing quote below */
s++, len--;
/* Since len still includes trailing quote, this is enough space */
newStr = palloc(len);
for (i = 0, j = 0; i < len; i++)
{
if (s[i] == '\\')
{
i++;
switch (s[i])
{
case 'b':
newStr[j] = '\b';
break;
case 'f':
newStr[j] = '\f';
break;
case 'n':
newStr[j] = '\n';
break;
case 'r':
newStr[j] = '\r';
break;
case 't':
newStr[j] = '\t';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
int k;
long octVal = 0;
for (k = 0;
s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
k++)
octVal = (octVal << 3) + (s[i + k] - '0');
i += k - 1;
newStr[j] = ((char) octVal);
}
break;
default:
newStr[j] = s[i];
break;
} /* switch */
}
else if (s[i] == '\'' && s[i + 1] == '\'')
{
/* doubled quote becomes just one quote */
newStr[j] = s[++i];
}
else
newStr[j] = s[i];
j++;
}
/* We copied the ending quote to newStr, so replace with \0 */
Assert(j > 0 && j <= len);
newStr[--j] = '\0';
return newStr;
}
/*
* Flex fatal errors bring us here. Stash the error message and jump back to
* ParseConfigFp(). Assume all msg arguments point to string constants; this
* holds for flex 2.5.31 (earliest we support) and flex 2.5.35 (latest as of
* this writing). Otherwise, we would need to copy the message.
*
* We return "int" since this takes the place of calls to fprintf().
*/
static int
CONF_flex_fatal(const char *msg)
{
CONF_flex_fatal_errmsg = msg;
siglongjmp(*CONF_flex_fatal_jmp, 1);
return 0; /* keep compiler quiet */
}

View File

@@ -23,6 +23,12 @@
#include "configfile.h"
#include "log.h"
#include <utils/elog.h>
#if (PG_ACTUAL_VERSION_NUM >= 100000)
#include <storage/fd.h> /* for durable_rename() */
#endif
const static char *_progname = NULL;
char config_file_path[MAXPGPATH] = "";
static bool config_file_provided = false;
@@ -266,12 +272,6 @@ static void
_parse_config(t_configuration_options *options, ItemList *error_list, ItemList *warning_list)
{
FILE *fp;
char *s = NULL,
buf[MAXLINELENGTH] = "";
char name[MAXLEN] = "";
char value[MAXLEN] = "";
bool node_id_found = false;
/* Initialize configuration options with sensible defaults */
@@ -468,360 +468,12 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
exit(ERR_BAD_CONFIG);
}
/* Read file */
while ((s = fgets(buf, sizeof buf, fp)) != NULL)
{
bool known_parameter = true;
/* Parse name/value pair from line */
_parse_line(buf, name, value);
/* Skip blank lines */
if (!strlen(name))
continue;
/* Skip comments */
if (name[0] == '#')
continue;
/* Copy into correct entry in parameters struct */
if (strcmp(name, "node_id") == 0)
{
options->node_id = repmgr_atoi(value, name, error_list, MIN_NODE_ID);
node_id_found = true;
}
else if (strcmp(name, "node_name") == 0)
{
if (strlen(value) < sizeof(options->node_name))
strncpy(options->node_name, value, sizeof(options->node_name));
else
item_list_append_format(error_list,
_("value for \"node_name\" must contain fewer than %lu characters"),
sizeof(options->node_name));
}
else if (strcmp(name, "conninfo") == 0)
strncpy(options->conninfo, value, MAXLEN);
else if (strcmp(name, "data_directory") == 0)
strncpy(options->data_directory, value, MAXPGPATH);
else if (strcmp(name, "config_directory") == 0)
strncpy(options->config_directory, value, MAXPGPATH);
else if (strcmp(name, "replication_user") == 0)
{
if (strlen(value) < sizeof(options->replication_user))
strncpy(options->replication_user, value, sizeof(options->replication_user));
else
item_list_append_format(error_list,
_("value for \"replication_user\" must contain fewer than %lu characters"),
sizeof(options->replication_user));
}
else if (strcmp(name, "pg_bindir") == 0)
strncpy(options->pg_bindir, value, MAXPGPATH);
else if (strcmp(name, "repmgr_bindir") == 0)
strncpy(options->repmgr_bindir, value, MAXPGPATH);
else if (strcmp(name, "replication_type") == 0)
{
if (strcmp(value, "physical") == 0)
options->replication_type = REPLICATION_TYPE_PHYSICAL;
else if (strcmp(value, "bdr") == 0)
options->replication_type = REPLICATION_TYPE_BDR;
else
item_list_append(error_list, _("value for \"replication_type\" must be \"physical\" or \"bdr\""));
}
/* log settings */
else if (strcmp(name, "log_file") == 0)
strncpy(options->log_file, value, MAXLEN);
else if (strcmp(name, "log_level") == 0)
strncpy(options->log_level, value, MAXLEN);
else if (strcmp(name, "log_facility") == 0)
strncpy(options->log_facility, value, MAXLEN);
else if (strcmp(name, "log_status_interval") == 0)
options->log_status_interval = repmgr_atoi(value, name, error_list, 0);
/* standby clone settings */
else if (strcmp(name, "use_replication_slots") == 0)
options->use_replication_slots = parse_bool(value, name, error_list);
else if (strcmp(name, "pg_basebackup_options") == 0)
strncpy(options->pg_basebackup_options, value, MAXLEN);
else if (strcmp(name, "tablespace_mapping") == 0)
tablespace_list_append(options, value);
else if (strcmp(name, "restore_command") == 0)
strncpy(options->restore_command, value, MAXLEN);
else if (strcmp(name, "recovery_min_apply_delay") == 0)
{
parse_time_unit_parameter(name, value, options->recovery_min_apply_delay, error_list);
options->recovery_min_apply_delay_provided = true;
}
else if (strcmp(name, "archive_cleanup_command") == 0)
strncpy(options->archive_cleanup_command, value, MAXLEN);
else if (strcmp(name, "use_primary_conninfo_password") == 0)
options->use_primary_conninfo_password = parse_bool(value, name, error_list);
else if (strcmp(name, "passfile") == 0)
strncpy(options->passfile, value, sizeof(options->passfile));
/* standby promote settings */
else if (strcmp(name, "promote_check_timeout") == 0)
options->promote_check_timeout = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "promote_check_interval") == 0)
options->promote_check_interval = repmgr_atoi(value, name, error_list, 1);
/* standby follow settings */
else if (strcmp(name, "primary_follow_timeout") == 0)
options->primary_follow_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "standby_follow_timeout") == 0)
options->standby_follow_timeout = repmgr_atoi(value, name, error_list, 0);
/* standby switchover settings */
else if (strcmp(name, "shutdown_check_timeout") == 0)
options->shutdown_check_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "standby_reconnect_timeout") == 0)
options->standby_reconnect_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "wal_receive_check_timeout") == 0)
options->wal_receive_check_timeout = repmgr_atoi(value, name, error_list, 0);
/* node rejoin settings */
else if (strcmp(name, "node_rejoin_timeout") == 0)
options->node_rejoin_timeout = repmgr_atoi(value, name, error_list, 0);
/* node check settings */
else if (strcmp(name, "archive_ready_warning") == 0)
options->archive_ready_warning = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "archive_ready_critical") == 0)
options->archive_ready_critical = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "replication_lag_warning") == 0)
options->replication_lag_warning = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "replication_lag_critical") == 0)
options->replication_lag_critical = repmgr_atoi(value, name, error_list, 1);
/* repmgrd settings */
else if (strcmp(name, "failover") == 0)
{
if (strcmp(value, "manual") == 0)
{
options->failover = FAILOVER_MANUAL;
}
else if (strcmp(value, "automatic") == 0)
{
options->failover = FAILOVER_AUTOMATIC;
}
else
{
item_list_append(error_list,
_("value for \"failover\" must be \"automatic\" or \"manual\"\n"));
}
}
else if (strcmp(name, "priority") == 0)
options->priority = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "location") == 0)
strncpy(options->location, value, sizeof(options->location));
else if (strcmp(name, "promote_command") == 0)
strncpy(options->promote_command, value, sizeof(options->promote_command));
else if (strcmp(name, "follow_command") == 0)
strncpy(options->follow_command, value, sizeof(options->follow_command));
else if (strcmp(name, "reconnect_attempts") == 0)
options->reconnect_attempts = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "reconnect_interval") == 0)
options->reconnect_interval = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "monitor_interval_secs") == 0)
options->monitor_interval_secs = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "monitoring_history") == 0)
options->monitoring_history = parse_bool(value, name, error_list);
else if (strcmp(name, "degraded_monitoring_timeout") == 0)
options->degraded_monitoring_timeout = repmgr_atoi(value, name, error_list, -1);
else if (strcmp(name, "async_query_timeout") == 0)
options->async_query_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "primary_notification_timeout") == 0)
options->primary_notification_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "repmgrd_standby_startup_timeout") == 0)
options->repmgrd_standby_startup_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "repmgrd_pid_file") == 0)
strncpy(options->repmgrd_pid_file, value, MAXPGPATH);
else if (strcmp(name, "standby_disconnect_on_failover") == 0)
options->standby_disconnect_on_failover = parse_bool(value, name, error_list);
else if (strcmp(name, "sibling_nodes_disconnect_timeout") == 0)
options->sibling_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "connection_check_type") == 0)
{
if (strcasecmp(value, "ping") == 0)
{
options->connection_check_type = CHECK_PING;
}
else if (strcasecmp(value, "connection") == 0)
{
options->connection_check_type = CHECK_CONNECTION;
}
else if (strcasecmp(value, "query") == 0)
{
options->connection_check_type = CHECK_QUERY;
}
else
{
item_list_append(error_list,
_("value for \"connection_check_type\" must be \"ping\", \"connection\" or \"query\"\n"));
}
}
else if (strcmp(name, "primary_visibility_consensus") == 0)
options->primary_visibility_consensus = parse_bool(value, name, error_list);
else if (strcmp(name, "failover_validation_command") == 0)
strncpy(options->failover_validation_command, value, sizeof(options->failover_validation_command));
else if (strcmp(name, "election_rerun_interval") == 0)
options->election_rerun_interval = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "child_nodes_check_interval") == 0)
options->child_nodes_check_interval = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "child_nodes_disconnect_command") == 0)
snprintf(options->child_nodes_disconnect_command, sizeof(options->child_nodes_disconnect_command), "%s", value);
else if (strcmp(name, "child_nodes_disconnect_min_count") == 0)
options->child_nodes_disconnect_min_count = repmgr_atoi(value, name, error_list, -1);
else if (strcmp(name, "child_nodes_connected_min_count") == 0)
options->child_nodes_connected_min_count = repmgr_atoi(value, name, error_list, -1);
else if (strcmp(name, "child_nodes_connected_include_witness") == 0)
options->child_nodes_connected_include_witness = parse_bool(value, name, error_list);
else if (strcmp(name, "child_nodes_disconnect_timeout") == 0)
options->child_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0);
/* witness settings */
else if (strcmp(name, "witness_sync_interval") == 0)
options->witness_sync_interval = repmgr_atoi(value, name, error_list, 1);
/* BDR settings */
else if (strcmp(name, "bdr_local_monitoring_only") == 0)
options->bdr_local_monitoring_only = parse_bool(value, name, error_list);
else if (strcmp(name, "bdr_recovery_timeout") == 0)
options->bdr_recovery_timeout = repmgr_atoi(value, name, error_list, 0);
/* service settings */
else if (strcmp(name, "pg_ctl_options") == 0)
strncpy(options->pg_ctl_options, value, sizeof(options->pg_ctl_options));
else if (strcmp(name, "service_start_command") == 0)
strncpy(options->service_start_command, value, sizeof(options->service_start_command));
else if (strcmp(name, "service_stop_command") == 0)
strncpy(options->service_stop_command, value, sizeof(options->service_stop_command));
else if (strcmp(name, "service_restart_command") == 0)
strncpy(options->service_restart_command, value, sizeof(options->service_restart_command));
else if (strcmp(name, "service_reload_command") == 0)
strncpy(options->service_reload_command, value, sizeof(options->service_reload_command));
else if (strcmp(name, "service_promote_command") == 0)
strncpy(options->service_promote_command, value, sizeof(options->service_promote_command));
/* repmgrd service settings */
else if (strcmp(name, "repmgrd_service_start_command") == 0)
strncpy(options->repmgrd_service_start_command, value, sizeof(options->repmgrd_service_start_command));
else if (strcmp(name, "repmgrd_service_stop_command") == 0)
strncpy(options->repmgrd_service_stop_command, value, sizeof(options->repmgrd_service_stop_command));
/* event notification settings */
else if (strcmp(name, "event_notification_command") == 0)
strncpy(options->event_notification_command, value, sizeof(options->event_notification_command));
else if (strcmp(name, "event_notifications") == 0)
{
/* store unparsed value for comparison when reloading config */
strncpy(options->event_notifications_orig, value, sizeof(options->event_notifications_orig));
parse_event_notifications_list(options, value);
}
/* barman settings */
else if (strcmp(name, "barman_host") == 0)
strncpy(options->barman_host, value, sizeof(options->barman_host));
else if (strcmp(name, "barman_server") == 0)
strncpy(options->barman_server, value, sizeof(options->barman_server));
else if (strcmp(name, "barman_config") == 0)
strncpy(options->barman_config, value, sizeof(options->barman_config));
/* rsync/ssh settings */
else if (strcmp(name, "rsync_options") == 0)
strncpy(options->rsync_options, value, sizeof(options->rsync_options));
else if (strcmp(name, "ssh_options") == 0)
strncpy(options->ssh_options, value, sizeof(options->ssh_options));
/* undocumented settings for testing */
else if (strcmp(name, "promote_delay") == 0)
options->promote_delay = repmgr_atoi(value, name, error_list, 1);
/*
* Following parameters have been deprecated or renamed from 3.x -
* issue a warning
*/
else if (strcmp(name, "cluster") == 0)
{
item_list_append(warning_list,
_("parameter \"cluster\" is deprecated and will be ignored"));
known_parameter = false;
}
else if (strcmp(name, "node") == 0)
{
item_list_append(warning_list,
_("parameter \"node\" has been renamed to \"node_id\""));
known_parameter = false;
}
else if (strcmp(name, "upstream_node") == 0)
{
item_list_append(warning_list,
_("parameter \"upstream_node\" has been removed; use \"--upstream-node-id\" when cloning a standby"));
known_parameter = false;
}
else if (strcmp(name, "loglevel") == 0)
{
item_list_append(warning_list,
_("parameter \"loglevel\" has been renamed to \"log_level\""));
known_parameter = false;
}
else if (strcmp(name, "logfacility") == 0)
{
item_list_append(warning_list,
_("parameter \"logfacility\" has been renamed to \"log_facility\""));
known_parameter = false;
}
else if (strcmp(name, "logfile") == 0)
{
item_list_append(warning_list,
_("parameter \"logfile\" has been renamed to \"log_file\""));
known_parameter = false;
}
else if (strcmp(name, "master_reponse_timeout") == 0)
{
item_list_append(warning_list,
_("parameter \"master_reponse_timeout\" has been removed; use \"async_query_timeout\" instead"));
known_parameter = false;
}
else if (strcmp(name, "retry_promote_interval_secs") == 0)
{
item_list_append(warning_list,
_("parameter \"retry_promote_interval_secs\" has been removed; use \"primary_notification_timeout\" instead"));
known_parameter = false;
}
else
{
known_parameter = false;
log_warning(_("%s/%s: unknown name/value pair provided; ignoring"), name, value);
}
/*
* Raise an error if a known parameter is provided with an empty
* value. Currently there's no reason why empty parameters are needed;
* if we want to accept those, we'd need to add stricter default
* checking, as currently e.g. an empty `node_id` value will be converted
* to '0'.
*/
if (known_parameter == true && !strlen(value))
{
char error_message_buf[MAXLEN] = "";
maxlen_snprintf(error_message_buf,
_("\"%s\": no value provided"),
name);
item_list_append(error_list, error_message_buf);
}
}
(void) ProcessRepmgrConfigFile(fp, config_file_path, options, error_list, warning_list);
fclose(fp);
/* check required parameters */
if (node_id_found == false)
if (options->node_id == UNKNOWN_NODE_ID)
{
item_list_append(error_list, _("\"node_id\": required parameter was not found"));
}
@@ -914,6 +566,349 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
}
void
parse_configuration_item(t_configuration_options *options, ItemList *error_list, ItemList *warning_list, const char *name, const char *value)
{
bool known_parameter = true;
if (strcmp(name, "node_id") == 0)
{
options->node_id = repmgr_atoi(value, name, error_list, MIN_NODE_ID);
}
else if (strcmp(name, "node_name") == 0)
{
if (strlen(value) < sizeof(options->node_name))
strncpy(options->node_name, value, sizeof(options->node_name));
else
item_list_append_format(error_list,
_("value for \"node_name\" must contain fewer than %lu characters"),
sizeof(options->node_name));
}
else if (strcmp(name, "conninfo") == 0)
{
strncpy(options->conninfo, value, MAXLEN);
}
else if (strcmp(name, "data_directory") == 0)
{
strncpy(options->data_directory, value, MAXPGPATH);
canonicalize_path(options->data_directory);
}
else if (strcmp(name, "config_directory") == 0)
{
strncpy(options->config_directory, value, MAXPGPATH);
canonicalize_path(options->config_directory);
}
else if (strcmp(name, "replication_user") == 0)
{
if (strlen(value) < sizeof(options->replication_user))
strncpy(options->replication_user, value, sizeof(options->replication_user));
else
item_list_append_format(error_list,
_("value for \"replication_user\" must contain fewer than %lu characters"),
sizeof(options->replication_user));
}
else if (strcmp(name, "pg_bindir") == 0)
strncpy(options->pg_bindir, value, MAXPGPATH);
else if (strcmp(name, "repmgr_bindir") == 0)
strncpy(options->repmgr_bindir, value, MAXPGPATH);
else if (strcmp(name, "replication_type") == 0)
{
if (strcmp(value, "physical") == 0)
options->replication_type = REPLICATION_TYPE_PHYSICAL;
else if (strcmp(value, "bdr") == 0)
options->replication_type = REPLICATION_TYPE_BDR;
else
item_list_append(error_list, _("value for \"replication_type\" must be \"physical\" or \"bdr\""));
}
/* log settings */
else if (strcmp(name, "log_file") == 0)
strncpy(options->log_file, value, MAXLEN);
else if (strcmp(name, "log_level") == 0)
strncpy(options->log_level, value, MAXLEN);
else if (strcmp(name, "log_facility") == 0)
strncpy(options->log_facility, value, MAXLEN);
else if (strcmp(name, "log_status_interval") == 0)
options->log_status_interval = repmgr_atoi(value, name, error_list, 0);
/* standby clone settings */
else if (strcmp(name, "use_replication_slots") == 0)
options->use_replication_slots = parse_bool(value, name, error_list);
else if (strcmp(name, "pg_basebackup_options") == 0)
strncpy(options->pg_basebackup_options, value, MAXLEN);
else if (strcmp(name, "tablespace_mapping") == 0)
tablespace_list_append(options, value);
else if (strcmp(name, "restore_command") == 0)
strncpy(options->restore_command, value, MAXLEN);
else if (strcmp(name, "recovery_min_apply_delay") == 0)
{
parse_time_unit_parameter(name, value, options->recovery_min_apply_delay, error_list);
options->recovery_min_apply_delay_provided = true;
}
else if (strcmp(name, "archive_cleanup_command") == 0)
strncpy(options->archive_cleanup_command, value, MAXLEN);
else if (strcmp(name, "use_primary_conninfo_password") == 0)
options->use_primary_conninfo_password = parse_bool(value, name, error_list);
else if (strcmp(name, "passfile") == 0)
strncpy(options->passfile, value, sizeof(options->passfile));
/* standby promote settings */
else if (strcmp(name, "promote_check_timeout") == 0)
options->promote_check_timeout = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "promote_check_interval") == 0)
options->promote_check_interval = repmgr_atoi(value, name, error_list, 1);
/* standby follow settings */
else if (strcmp(name, "primary_follow_timeout") == 0)
options->primary_follow_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "standby_follow_timeout") == 0)
options->standby_follow_timeout = repmgr_atoi(value, name, error_list, 0);
/* standby switchover settings */
else if (strcmp(name, "shutdown_check_timeout") == 0)
options->shutdown_check_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "standby_reconnect_timeout") == 0)
options->standby_reconnect_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "wal_receive_check_timeout") == 0)
options->wal_receive_check_timeout = repmgr_atoi(value, name, error_list, 0);
/* node rejoin settings */
else if (strcmp(name, "node_rejoin_timeout") == 0)
options->node_rejoin_timeout = repmgr_atoi(value, name, error_list, 0);
/* node check settings */
else if (strcmp(name, "archive_ready_warning") == 0)
options->archive_ready_warning = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "archive_ready_critical") == 0)
options->archive_ready_critical = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "replication_lag_warning") == 0)
options->replication_lag_warning = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "replication_lag_critical") == 0)
options->replication_lag_critical = repmgr_atoi(value, name, error_list, 1);
/* repmgrd settings */
else if (strcmp(name, "failover") == 0)
{
if (strcmp(value, "manual") == 0)
{
options->failover = FAILOVER_MANUAL;
}
else if (strcmp(value, "automatic") == 0)
{
options->failover = FAILOVER_AUTOMATIC;
}
else
{
item_list_append(error_list,
_("value for \"failover\" must be \"automatic\" or \"manual\"\n"));
}
}
else if (strcmp(name, "priority") == 0)
options->priority = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "location") == 0)
strncpy(options->location, value, sizeof(options->location));
else if (strcmp(name, "promote_command") == 0)
strncpy(options->promote_command, value, sizeof(options->promote_command));
else if (strcmp(name, "follow_command") == 0)
strncpy(options->follow_command, value, sizeof(options->follow_command));
else if (strcmp(name, "reconnect_attempts") == 0)
options->reconnect_attempts = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "reconnect_interval") == 0)
options->reconnect_interval = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "monitor_interval_secs") == 0)
options->monitor_interval_secs = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "monitoring_history") == 0)
options->monitoring_history = parse_bool(value, name, error_list);
else if (strcmp(name, "degraded_monitoring_timeout") == 0)
options->degraded_monitoring_timeout = repmgr_atoi(value, name, error_list, -1);
else if (strcmp(name, "async_query_timeout") == 0)
options->async_query_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "primary_notification_timeout") == 0)
options->primary_notification_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "repmgrd_standby_startup_timeout") == 0)
options->repmgrd_standby_startup_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "repmgrd_pid_file") == 0)
strncpy(options->repmgrd_pid_file, value, MAXPGPATH);
else if (strcmp(name, "standby_disconnect_on_failover") == 0)
options->standby_disconnect_on_failover = parse_bool(value, name, error_list);
else if (strcmp(name, "sibling_nodes_disconnect_timeout") == 0)
options->sibling_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "connection_check_type") == 0)
{
if (strcasecmp(value, "ping") == 0)
{
options->connection_check_type = CHECK_PING;
}
else if (strcasecmp(value, "connection") == 0)
{
options->connection_check_type = CHECK_CONNECTION;
}
else if (strcasecmp(value, "query") == 0)
{
options->connection_check_type = CHECK_QUERY;
}
else
{
item_list_append(error_list,
_("value for \"connection_check_type\" must be \"ping\", \"connection\" or \"query\"\n"));
}
}
else if (strcmp(name, "primary_visibility_consensus") == 0)
options->primary_visibility_consensus = parse_bool(value, name, error_list);
else if (strcmp(name, "failover_validation_command") == 0)
strncpy(options->failover_validation_command, value, sizeof(options->failover_validation_command));
else if (strcmp(name, "election_rerun_interval") == 0)
options->election_rerun_interval = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "child_nodes_check_interval") == 0)
options->child_nodes_check_interval = repmgr_atoi(value, name, error_list, 1);
else if (strcmp(name, "child_nodes_disconnect_command") == 0)
snprintf(options->child_nodes_disconnect_command, sizeof(options->child_nodes_disconnect_command), "%s", value);
else if (strcmp(name, "child_nodes_disconnect_min_count") == 0)
options->child_nodes_disconnect_min_count = repmgr_atoi(value, name, error_list, -1);
else if (strcmp(name, "child_nodes_connected_min_count") == 0)
options->child_nodes_connected_min_count = repmgr_atoi(value, name, error_list, -1);
else if (strcmp(name, "child_nodes_connected_include_witness") == 0)
options->child_nodes_connected_include_witness = parse_bool(value, name, error_list);
else if (strcmp(name, "child_nodes_disconnect_timeout") == 0)
options->child_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0);
/* witness settings */
else if (strcmp(name, "witness_sync_interval") == 0)
options->witness_sync_interval = repmgr_atoi(value, name, error_list, 1);
/* BDR settings */
else if (strcmp(name, "bdr_local_monitoring_only") == 0)
options->bdr_local_monitoring_only = parse_bool(value, name, error_list);
else if (strcmp(name, "bdr_recovery_timeout") == 0)
options->bdr_recovery_timeout = repmgr_atoi(value, name, error_list, 0);
/* service settings */
else if (strcmp(name, "pg_ctl_options") == 0)
strncpy(options->pg_ctl_options, value, sizeof(options->pg_ctl_options));
else if (strcmp(name, "service_start_command") == 0)
strncpy(options->service_start_command, value, sizeof(options->service_start_command));
else if (strcmp(name, "service_stop_command") == 0)
strncpy(options->service_stop_command, value, sizeof(options->service_stop_command));
else if (strcmp(name, "service_restart_command") == 0)
strncpy(options->service_restart_command, value, sizeof(options->service_restart_command));
else if (strcmp(name, "service_reload_command") == 0)
strncpy(options->service_reload_command, value, sizeof(options->service_reload_command));
else if (strcmp(name, "service_promote_command") == 0)
strncpy(options->service_promote_command, value, sizeof(options->service_promote_command));
/* repmgrd service settings */
else if (strcmp(name, "repmgrd_service_start_command") == 0)
strncpy(options->repmgrd_service_start_command, value, sizeof(options->repmgrd_service_start_command));
else if (strcmp(name, "repmgrd_service_stop_command") == 0)
strncpy(options->repmgrd_service_stop_command, value, sizeof(options->repmgrd_service_stop_command));
/* event notification settings */
else if (strcmp(name, "event_notification_command") == 0)
strncpy(options->event_notification_command, value, sizeof(options->event_notification_command));
else if (strcmp(name, "event_notifications") == 0)
{
/* store unparsed value for comparison when reloading config */
strncpy(options->event_notifications_orig, value, sizeof(options->event_notifications_orig));
parse_event_notifications_list(options, value);
}
/* barman settings */
else if (strcmp(name, "barman_host") == 0)
strncpy(options->barman_host, value, sizeof(options->barman_host));
else if (strcmp(name, "barman_server") == 0)
strncpy(options->barman_server, value, sizeof(options->barman_server));
else if (strcmp(name, "barman_config") == 0)
strncpy(options->barman_config, value, sizeof(options->barman_config));
/* rsync/ssh settings */
else if (strcmp(name, "rsync_options") == 0)
strncpy(options->rsync_options, value, sizeof(options->rsync_options));
else if (strcmp(name, "ssh_options") == 0)
strncpy(options->ssh_options, value, sizeof(options->ssh_options));
/* undocumented settings for testing */
else if (strcmp(name, "promote_delay") == 0)
options->promote_delay = repmgr_atoi(value, name, error_list, 1);
/*
* Following parameters have been deprecated or renamed from 3.x -
* issue a warning
*/
else if (strcmp(name, "cluster") == 0)
{
item_list_append(warning_list,
_("parameter \"cluster\" is deprecated and will be ignored"));
known_parameter = false;
}
else if (strcmp(name, "node") == 0)
{
item_list_append(warning_list,
_("parameter \"node\" has been renamed to \"node_id\""));
known_parameter = false;
}
else if (strcmp(name, "upstream_node") == 0)
{
item_list_append(warning_list,
_("parameter \"upstream_node\" has been removed; use \"--upstream-node-id\" when cloning a standby"));
known_parameter = false;
}
else if (strcmp(name, "loglevel") == 0)
{
item_list_append(warning_list,
_("parameter \"loglevel\" has been renamed to \"log_level\""));
known_parameter = false;
}
else if (strcmp(name, "logfacility") == 0)
{
item_list_append(warning_list,
_("parameter \"logfacility\" has been renamed to \"log_facility\""));
known_parameter = false;
}
else if (strcmp(name, "logfile") == 0)
{
item_list_append(warning_list,
_("parameter \"logfile\" has been renamed to \"log_file\""));
known_parameter = false;
}
else if (strcmp(name, "master_reponse_timeout") == 0)
{
item_list_append(warning_list,
_("parameter \"master_reponse_timeout\" has been removed; use \"async_query_timeout\" instead"));
known_parameter = false;
}
else if (strcmp(name, "retry_promote_interval_secs") == 0)
{
item_list_append(warning_list,
_("parameter \"retry_promote_interval_secs\" has been removed; use \"primary_notification_timeout\" instead"));
known_parameter = false;
}
else
{
known_parameter = false;
log_warning(_("%s/%s: unknown name/value pair provided; ignoring"), name, value);
}
/*
* Raise an error if a known parameter is provided with an empty
* value. Currently there's no reason why empty parameters are needed;
* if we want to accept those, we'd need to add stricter default
* checking, as currently e.g. an empty `node_id` value will be converted
* to '0'.
*/
if (known_parameter == true && !strlen(value))
{
char error_message_buf[MAXLEN] = "";
maxlen_snprintf(error_message_buf,
_("\"%s\": no value provided"),
name);
item_list_append(error_list, error_message_buf);
}
}
bool
@@ -1811,6 +1806,7 @@ parse_bool(const char *s, const char *config_item, ItemList *error_list)
return false;
}
/*
* Split argument into old_dir and new_dir and append to tablespace mapping
* list.
@@ -1878,6 +1874,114 @@ tablespace_list_append(t_configuration_options *options, const char *arg)
}
bool
modify_auto_conf(const char *data_dir, KeyValueList *items)
{
PQExpBufferData auto_conf;
PQExpBufferData auto_conf_tmp;
PQExpBufferData auto_conf_contents;
FILE *fp;
KeyValueList config = {NULL, NULL};
KeyValueListCell *cell = NULL;
bool success = true;
initPQExpBuffer(&auto_conf);
appendPQExpBuffer(&auto_conf, "%s/%s",
data_dir, PG_AUTOCONF_FILENAME);
fp = fopen(auto_conf.data, "r");
if (fp == NULL)
{
fprintf(stderr, "unable to open \"%s\": %s\n",
auto_conf.data,
strerror(errno));
termPQExpBuffer(&auto_conf);
return false;
}
// XXX check return value
(void) ProcessPostgresConfigFile(fp, auto_conf.data, &config, NULL, NULL);
fclose(fp);
/*
* Append requested items to items extracted from the existing file.
*/
for (cell = items->head; cell; cell = cell->next)
{
key_value_list_replace_or_set(&config,
cell->key,
cell->value);
}
initPQExpBuffer(&auto_conf_tmp);
appendPQExpBuffer(&auto_conf_tmp, "%s.tmp",
auto_conf.data);
initPQExpBuffer(&auto_conf_contents);
/*
* Keep this in sync with src/backend/utils/misc/guc.c:write_auto_conf_file()
*/
appendPQExpBufferStr(&auto_conf_contents,
"# Do not edit this file manually!\n"
"# It will be overwritten by the ALTER SYSTEM command.\n");
for (cell = config.head; cell; cell = cell->next)
{
appendPQExpBuffer(&auto_conf_contents,
"%s = '%s'\n",
cell->key, cell->value);
}
fp = fopen(auto_conf_tmp.data, "w");
if (fp == NULL)
{
fprintf(stderr, "unable to open \"%s\": %s\n",
auto_conf_tmp.data,
strerror(errno));
}
else
{
if (fwrite(auto_conf_contents.data, strlen(auto_conf_contents.data), 1, fp) != 1)
{
fclose(fp);
}
else
{
fclose(fp);
/*
* Note: durable_rename() is not exposed to frontend code before Pg 10.
* We only really need to be modifying postgresql.auto.conf from Pg 12,
* but provide backwards compatibitilty for Pg 9.6 and earlier for the
* (unlikely) event that a repmgr built against one of those versions
* is being used against Pg 12 and later.
*/
// XXX check return values
#if (PG_ACTUAL_VERSION_NUM >= 100000)
(void) durable_rename(auto_conf_tmp.data, auto_conf.data, LOG);
#else
if (rename(auto_conf_tmp.data, auto_conf.data) < 0)
{
success = false;
}
#endif
}
}
termPQExpBuffer(&auto_conf);
termPQExpBuffer(&auto_conf_tmp);
termPQExpBuffer(&auto_conf_contents);
key_value_list_free(&config);
return success;
}
/*
* parse_event_notifications_list()
@@ -2054,9 +2158,6 @@ free_parsed_argv(char ***argv_array)
}
bool
parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options, int server_version_num, ItemList *error_list)
{

View File

@@ -28,6 +28,12 @@
/* magic number for use in t_recovery_conf */
#define TARGET_TIMELINE_LATEST 0
/*
* 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"
extern bool config_file_found;
extern char config_file_path[MAXPGPATH];
@@ -317,6 +323,8 @@ const char *progname(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(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);
bool parse_bool(const char *s,
@@ -342,4 +350,10 @@ void exit_with_cli_errors(ItemList *error_list, const char *repmgr_command);
void print_item_list(ItemList *item_list);
const char *print_connection_check_type(ConnectionCheckType type);
extern bool modify_auto_conf(const char *data_dir, KeyValueList *items);
extern bool ProcessRepmgrConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, 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_ */

148
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 4.4.
# Generated by GNU Autoconf 2.69 for repmgr 5.0.0.
#
# Report bugs to <repmgr@googlegroups.com>.
#
@@ -582,13 +582,16 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='repmgr'
PACKAGE_TARNAME='repmgr'
PACKAGE_VERSION='4.4'
PACKAGE_STRING='repmgr 4.4'
PACKAGE_VERSION='5.0.0'
PACKAGE_STRING='repmgr 5.0.0'
PACKAGE_BUGREPORT='repmgr@googlegroups.com'
PACKAGE_URL='https://repmgr.org/'
ac_subst_vars='LTLIBOBJS
LIBOBJS
HAVE_SED
HAVE_GSED
HAVE_GNUSED
vpath_build
SED
PG_CONFIG
@@ -1178,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 4.4 to adapt to many kinds of systems.
\`configure' configures repmgr 5.0.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1239,7 +1242,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of repmgr 4.4:";;
short | recursive ) echo "Configuration of repmgr 5.0.0:";;
esac
cat <<\_ACEOF
@@ -1313,7 +1316,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
repmgr configure 4.4
repmgr configure 5.0.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1332,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 4.4, which was
It was created by repmgr $as_me 5.0.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -1847,6 +1850,133 @@ else
fi
# Extract the first word of "gnused", so it can be a program name with args.
set dummy gnused; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_HAVE_GNUSED+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$HAVE_GNUSED"; then
ac_cv_prog_HAVE_GNUSED="$HAVE_GNUSED" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_HAVE_GNUSED="yes"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_prog_HAVE_GNUSED" && ac_cv_prog_HAVE_GNUSED="no"
fi
fi
HAVE_GNUSED=$ac_cv_prog_HAVE_GNUSED
if test -n "$HAVE_GNUSED"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_GNUSED" >&5
$as_echo "$HAVE_GNUSED" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Extract the first word of "gsed", so it can be a program name with args.
set dummy gsed; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_HAVE_GSED+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$HAVE_GSED"; then
ac_cv_prog_HAVE_GSED="$HAVE_GSED" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_HAVE_GSED="yes"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_prog_HAVE_GSED" && ac_cv_prog_HAVE_GSED="no"
fi
fi
HAVE_GSED=$ac_cv_prog_HAVE_GSED
if test -n "$HAVE_GSED"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_GSED" >&5
$as_echo "$HAVE_GSED" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Extract the first word of "sed", so it can be a program name with args.
set dummy sed; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_HAVE_SED+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$HAVE_SED"; then
ac_cv_prog_HAVE_SED="$HAVE_SED" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_HAVE_SED="yes"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_prog_HAVE_SED" && ac_cv_prog_HAVE_SED="no"
fi
fi
HAVE_SED=$ac_cv_prog_HAVE_SED
if test -n "$HAVE_SED"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_SED" >&5
$as_echo "$HAVE_SED" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "$HAVE_GNUSED" = yes; then
SED=gnused
else
if test "$HAVE_GSED" = yes; then
SED=gsed
else
SED=sed
fi
fi
ac_config_files="$ac_config_files Makefile"
ac_config_files="$ac_config_files Makefile.global"
@@ -2357,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 4.4, which was
This file was extended by repmgr $as_me 5.0.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -2420,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 4.4
repmgr config.status 5.0.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([repmgr], [4.4], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
AC_INIT([repmgr], [5.0.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
AC_COPYRIGHT([Copyright (c) 2010-2019, 2ndQuadrant Ltd.])
@@ -57,6 +57,22 @@ else
fi
AC_SUBST(vpath_build)
AC_CHECK_PROG(HAVE_GNUSED,gnused,yes,no)
AC_CHECK_PROG(HAVE_GSED,gsed,yes,no)
AC_CHECK_PROG(HAVE_SED,sed,yes,no)
if test "$HAVE_GNUSED" = yes; then
SED=gnused
else
if test "$HAVE_GSED" = yes; then
SED=gsed
else
SED=sed
fi
fi
AC_SUBST(SED)
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([Makefile.global])
AC_OUTPUT

View File

@@ -73,7 +73,16 @@ while(<$fh>) {
if ($param eq 'data_directory') {
$data_directory_found = 1;
}
push @outp, $line;
# Don't quote numbers
if ($value =~ /^\d+$/) {
push @outp, sprintf(q|%s=%s|, $param, $value);
}
# Quote everything else
else {
$value =~ s/'/''/g;
push @outp, sprintf(q|%s='%s'|, $param, $value);
}
}
}
@@ -83,6 +92,6 @@ print join("\n", @outp);
print "\n";
if ($data_directory_found == 0) {
print "data_directory=\n";
print "data_directory=''\n";
}

View File

@@ -312,6 +312,7 @@ get_controlfile(const char *DataDir)
if (version_num >= 120000)
{
#if PG_ACTUAL_VERSION_NUM >= 120000
ControlFileData12 *ptr = (struct ControlFileData12 *)ControlFileDataPtr;
control_file_info->system_identifier = ptr->system_identifier;
control_file_info->state = ptr->state;
@@ -320,6 +321,10 @@ get_controlfile(const char *DataDir)
control_file_info->timeline = ptr->checkPointCopy.ThisTimeLineID;
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\n");
exit(ERR_BAD_CONFIG);
#endif
}
else if (version_num >= 110000)
{

View File

@@ -12,6 +12,7 @@
#include "postgres_fe.h"
#include "catalog/pg_control.h"
#define MAX_VERSION_STRING 24
/*
* A simplified representation of pg_control containing only those fields
@@ -55,7 +56,7 @@ typedef struct CheckPoint93
} CheckPoint93;
/* Same for 9.5, 9.6, 10, HEAD */
/* Same for 9.5, 9.6, 10, 11 */
typedef struct CheckPoint95
{
XLogRecPtr redo; /* next RecPtr available when we began to
@@ -83,6 +84,50 @@ typedef struct CheckPoint95
} CheckPoint95;
#if PG_ACTUAL_VERSION_NUM >= 120000
/*
* Following fields removed in PostgreSQL 12;
*
* uint32 nextXidEpoch;
* TransactionId nextXid;
*
* and replaced by:
*
* FullTransactionId nextFullXid;
*/
typedef struct CheckPoint12
{
XLogRecPtr redo; /* next RecPtr available when we began to
* create CheckPoint (i.e. REDO start point) */
TimeLineID ThisTimeLineID; /* current TLI */
TimeLineID PrevTimeLineID; /* previous TLI, if this record begins a new
* timeline (equals ThisTimeLineID otherwise) */
bool fullPageWrites; /* current full_page_writes */
FullTransactionId nextFullXid; /* next free full transaction ID */
Oid nextOid; /* next free OID */
MultiXactId nextMulti; /* next free MultiXactId */
MultiXactOffset nextMultiOffset; /* next free MultiXact offset */
TransactionId oldestXid; /* cluster-wide minimum datfrozenxid */
Oid oldestXidDB; /* database with minimum datfrozenxid */
MultiXactId oldestMulti; /* cluster-wide minimum datminmxid */
Oid oldestMultiDB; /* database with minimum datminmxid */
pg_time_t time; /* time stamp of checkpoint */
TransactionId oldestCommitTsXid; /* oldest Xid with valid commit
* timestamp */
TransactionId newestCommitTsXid; /* newest Xid with valid commit
* timestamp */
/*
* Oldest XID still running. This is only needed to initialize hot standby
* mode from an online checkpoint, so we only bother calculating this for
* online checkpoints and only when wal_level is replica. Otherwise it's
* set to InvalidTransactionId.
*/
TransactionId oldestActiveXid;
} CheckPoint12;
#endif
typedef struct ControlFileData93
{
uint64 system_identifier;
@@ -333,19 +378,11 @@ typedef struct ControlFileData11
} ControlFileData11;
#if PG_ACTUAL_VERSION_NUM >= 120000
/*
* Following field added in Pg12:
*
* int max_wal_senders;
*
* Following fields removed:
*
* uint32 nextXidEpoch;
* TransactionId nextXid;
*
* and replaced by:
*
* FullTransactionId nextFullXid;
*/
typedef struct ControlFileData12
@@ -359,7 +396,7 @@ typedef struct ControlFileData12
pg_time_t time; /* time stamp of last pg_control update */
XLogRecPtr checkPoint; /* last check point record ptr */
CheckPoint checkPointCopy; /* copy of last check point record */
CheckPoint12 checkPointCopy; /* copy of last check point record */
XLogRecPtr unloggedLSN; /* current fake LSN value, for unlogged rels */
@@ -398,6 +435,7 @@ typedef struct ControlFileData12
uint32 data_checksum_version;
} ControlFileData12;
#endif
extern int get_pg_version(const char *data_directory, char *version_string);
extern DBState get_db_state(const char *data_directory);

View File

@@ -336,12 +336,10 @@ establish_db_connection_by_params(t_conninfo_param_list *param_list,
bool
is_superuser_connection(PGconn *conn, t_connection_user *userinfo)
{
char *current_user = NULL;
const char *superuser_status = NULL;
bool is_superuser = false;
const char *current_user = PQuser(conn);
const char *superuser_status = PQparameterStatus(conn, "is_superuser");
current_user = PQuser(conn);
superuser_status = PQparameterStatus(conn, "is_superuser");
is_superuser = (strcmp(superuser_status, "on") == 0) ? true : false;
if (userinfo != NULL)
@@ -356,6 +354,51 @@ is_superuser_connection(PGconn *conn, t_connection_user *userinfo)
}
bool
connection_has_pg_settings(PGconn *conn)
{
bool has_pg_settings = false;
/* superusers can always read pg_settings */
if (is_superuser_connection(conn, NULL) == true)
{
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);
}
return has_pg_settings;
}
void
close_connection(PGconn **conn)
{
@@ -1865,8 +1908,20 @@ repmgrd_set_pid(PGconn *conn, pid_t repmgrd_pid, const char *pidfile)
initPQExpBuffer(&query);
appendPQExpBuffer(&query,
"SELECT repmgr.set_repmgrd_pid(%i, '%s')",
(int) repmgrd_pid, pidfile);
"SELECT repmgr.set_repmgrd_pid(%i, ",
(int) repmgrd_pid);
if (pidfile != NULL)
{
appendPQExpBuffer(&query,
" '%s')",
pidfile);
}
else
{
appendPQExpBufferStr(&query,
" NULL)");
}
res = PQexec(conn, query.data);
termPQExpBuffer(&query);
@@ -2092,9 +2147,9 @@ get_repmgr_extension_status(PGconn *conn, t_extension_versions *extversions)
appendPQExpBufferStr(&query,
" SELECT ae.name, e.extname, "
" ae.default_version, "
" (((ae.default_version::NUMERIC::INT) * 10000) + (ae.default_version::NUMERIC - ae.default_version::NUMERIC::INT) * 1000)::INT AS available, "
" (((FLOOR(ae.default_version::NUMERIC)::INT) * 10000) + (ae.default_version::NUMERIC - FLOOR(ae.default_version::NUMERIC)::INT) * 1000)::INT AS available, "
" ae.installed_version, "
" (((ae.installed_version::NUMERIC::INT) * 10000) + (ae.installed_version::NUMERIC - ae.installed_version::NUMERIC::INT) * 1000)::INT AS installed "
" (((FLOOR(ae.installed_version::NUMERIC)::INT) * 10000) + (ae.installed_version::NUMERIC - FLOOR(ae.installed_version::NUMERIC)::INT) * 1000)::INT AS installed "
" FROM pg_catalog.pg_available_extensions ae "
"LEFT JOIN pg_catalog.pg_extension e "
" ON e.extname=ae.name "
@@ -5073,6 +5128,7 @@ init_replication_info(ReplInfo *replication_info)
replication_info->receiving_streamed_wal = true;
replication_info->wal_replay_paused = false;
replication_info->upstream_last_seen = -1;
replication_info->upstream_node_id = UNKNOWN_NODE_ID;
}

View File

@@ -438,6 +438,7 @@ PGconn *get_primary_connection(PGconn *standby_conn, int *primary_id, char *p
PGconn *get_primary_connection_quiet(PGconn *standby_conn, int *primary_id, char *primary_conninfo_out);
bool is_superuser_connection(PGconn *conn, t_connection_user *userinfo);
bool connection_has_pg_settings(PGconn *conn);
void close_connection(PGconn **conn);
/* conninfo manipulation functions */

View File

@@ -31,6 +31,7 @@ ALLXML := $(wildcard $(srcdir)/*.xml) $(GENERATED_XML)
version.xml: $(repmgr_top_builddir)/repmgr_version.h
{ \
echo "<!ENTITY repmgrversion \"$(REPMGR_VERSION)\">"; \
echo "<!ENTITY releasedate \"$(REPMGR_RELEASE_DATE)\">"; \
} > $@
##

View File

@@ -3,437 +3,464 @@
<title>FAQ (Frequently Asked Questions)</title>
<indexterm>
<primary>FAQ (Frequently Asked Questions)</primary>
<primary>FAQ (Frequently Asked Questions)</primary>
</indexterm>
<sect1 id="faq-general" xreflabel="General">
<title>General</title>
<sect1 id="faq-general" xreflabel="General">
<title>General</title>
<sect2 id="faq-xrepmgr-version-diff" xreflabel="Version differences">
<title>What's the difference between the repmgr versions?</title>
<para>
&repmgr; 4 is a complete rewrite of the existing &repmgr; code base
and implements &repmgr; as a PostgreSQL extension. It
supports all PostgreSQL versions from 9.3 (although some &repmgr;
features are not available for PostgreSQL 9.3 and 9.4).
</para>
<para>
&repmgr; 3.x builds on the improved replication facilities added
in PostgreSQL 9.3, as well as improved automated failover support
via &repmgrd;, and is not compatible with PostgreSQL 9.2
and earlier. We recommend upgrading to &repmgr; 4, as the &repmgr; 3.x
series is no longer maintained.
</para>
<para>
&repmgr; 2.x supports PostgreSQL 9.0 ~ 9.3. While it is compatible
with PostgreSQL 9.3, we recommend using repmgr 4.x. &repmgr; 2.x is
no longer maintained.
</para>
<para>
See also <link linkend="install-compatibility-matrix">&repmgr; compatibility matrix</link>
and <link linkend="faq-upgrade-repmgr">Should I upgrade &repmgr;?</link>.
</para>
</sect2>
<sect2 id="faq-xrepmgr-version-diff" xreflabel="Version differences">
<title>What's the difference between the repmgr versions?</title>
<para>
&repmgr; 4 is a complete rewrite of the previous &repmgr; code base
and implements &repmgr; as a PostgreSQL extension. It
supports all PostgreSQL versions from 9.3 (although some &repmgr;
features are not available for PostgreSQL 9.3 and 9.4).
</para>
<note>
<para>
&repmgr; 5 is fundamentally the same code base as &repmgr; 4, but provides
support for the revised replication configuration mechanism in PostgreSQL 12.
</para>
</note>
<para>
&repmgr; 3.x builds on the improved replication facilities added
in PostgreSQL 9.3, as well as improved automated failover support
via &repmgrd;, and is not compatible with PostgreSQL 9.2
and earlier. We recommend upgrading to &repmgr; 4, as the &repmgr; 3.x
series is no longer maintained.
</para>
<para>
&repmgr; 2.x supports PostgreSQL 9.0 ~ 9.3. While it is compatible
with PostgreSQL 9.3, we recommend using repmgr 4.x. &repmgr; 2.x is
no longer maintained.
</para>
<para>
See also <link linkend="install-compatibility-matrix">&repmgr; compatibility matrix</link>
and <link linkend="faq-upgrade-repmgr">Should I upgrade &repmgr;?</link>.
</para>
</sect2>
<sect2 id="faq-replication-slots-advantage" xreflabel="Advantages of replication slots">
<title>What's the advantage of using replication slots?</title>
<para>
Replication slots, introduced in PostgreSQL 9.4, ensure that the
primary server will retain WAL files until they have been consumed
by all standby servers. This means standby servers should never
fail due to not being able to retrieve required WAL files from the
primary.
</para>
<para>
However this does mean that if a standby is no longer connected to the
primary, the presence of the replication slot will cause WAL files
to be retained indefinitely, and eventually lead to disk space
exhaustion.
</para>
<sect2 id="faq-replication-slots-advantage" xreflabel="Advantages of replication slots">
<title>What's the advantage of using replication slots?</title>
<para>
Replication slots, introduced in PostgreSQL 9.4, ensure that the
primary server will retain WAL files until they have been consumed
by all standby servers. This means standby servers should never
fail due to not being able to retrieve required WAL files from the
primary.
</para>
<para>
However this does mean that if a standby is no longer connected to the
primary, the presence of the replication slot will cause WAL files
to be retained indefinitely, and eventually lead to disk space
exhaustion.
</para>
<tip>
<para>
2ndQuadrant's recommended configuration is to configure
<ulink url="https://www.pgbarman.org/">Barman</ulink> as a fallback
source of WAL files, rather than maintain replication slots for
each standby. See also: <link linkend="cloning-from-barman-restore-command">Using Barman as a WAL file source</link>.
</para>
</tip>
</sect2>
<tip>
<para>
2ndQuadrant's recommended configuration is to configure
<ulink url="https://www.pgbarman.org/">Barman</ulink> as a fallback
source of WAL files, rather than maintain replication slots for
each standby. See also: <link linkend="cloning-from-barman-restore-command">Using Barman as a WAL file source</link>.
</para>
</tip>
</sect2>
<sect2 id="faq-replication-slots-number" xreflabel="Number of replication slots">
<title>How many replication slots should I define in <varname>max_replication_slots</varname>?</title>
<para>
Normally at least same number as the number of standbys which will connect
to the node. Note that changes to <varname>max_replication_slots</varname> require a server
restart to take effect, and as there is no particular penalty for unused
replication slots, setting a higher figure will make adding new nodes
easier.
</para>
</sect2>
<sect2 id="faq-replication-slots-number" xreflabel="Number of replication slots">
<title>How many replication slots should I define in <varname>max_replication_slots</varname>?</title>
<para>
Normally at least same number as the number of standbys which will connect
to the node. Note that changes to <varname>max_replication_slots</varname> require a server
restart to take effect, and as there is no particular penalty for unused
replication slots, setting a higher figure will make adding new nodes
easier.
</para>
</sect2>
<sect2 id="faq-hash-index" xreflabel="Hash indexes">
<title>Does &repmgr; support hash indexes?</title>
<para>
Before PostgreSQL 10, hash indexes were not WAL logged and are therefore not suitable
for use in streaming replication in PostgreSQL 9.6 and earlier. See the
<ulink url="https://www.postgresql.org/docs/9.6/sql-createindex.html#AEN80279">PostgreSQL documentation</ulink>
for details.
</para>
<para>
From PostgreSQL 10, this restriction has been lifted and hash indexes can be used
in a streaming replication cluster.
</para>
</sect2>
<sect2 id="faq-hash-index" xreflabel="Hash indexes">
<title>Does &repmgr; support hash indexes?</title>
<para>
Before PostgreSQL 10, hash indexes were not WAL logged and are therefore not suitable
for use in streaming replication in PostgreSQL 9.6 and earlier. See the
<ulink url="https://www.postgresql.org/docs/9.6/sql-createindex.html#AEN80279">PostgreSQL documentation</ulink>
for details.
</para>
<para>
From PostgreSQL 10, this restriction has been lifted and hash indexes can be used
in a streaming replication cluster.
</para>
</sect2>
<sect2 id="faq-upgrades" xreflabel="Upgrading PostgreSQL with repmgr">
<title>Can &repmgr; assist with upgrading a PostgreSQL cluster?</title>
<para>
For <emphasis>minor</emphasis> version upgrades, e.g. from 9.6.7 to 9.6.8, a common
approach is to upgrade a standby to the latest version, perform a
<link linkend="performing-switchover">switchover</link> promoting it to a primary,
then upgrade the former primary.
</para>
<para>
For <emphasis>major</emphasis> version upgrades (e.g. from PostgreSQL 9.6 to PostgreSQL 10),
the traditional approach is to "reseed" a cluster by upgrading a single
node with <ulink url="https://www.postgresql.org/docs/current/pgupgrade.html">pg_upgrade</ulink>
and recloning standbys from this.
</para>
<para>
To minimize downtime during major upgrades from PostgreSQL 9.4 and later,
<ulink url="https://www.2ndquadrant.com/en/resources/pglogical/">pglogical</ulink>
can be used to set up a parallel cluster using the newer PostgreSQL version,
which can be kept in sync with the existing production cluster until the
new cluster is ready to be put into production.
</para>
</sect2>
<sect2 id="faq-upgrades" xreflabel="Upgrading PostgreSQL with repmgr">
<title>Can &repmgr; assist with upgrading a PostgreSQL cluster?</title>
<para>
For <emphasis>minor</emphasis> version upgrades, e.g. from 9.6.7 to 9.6.8, a common
approach is to upgrade a standby to the latest version, perform a
<link linkend="performing-switchover">switchover</link> promoting it to a primary,
then upgrade the former primary.
</para>
<para>
For <emphasis>major</emphasis> version upgrades (e.g. from PostgreSQL 9.6 to PostgreSQL 10),
the traditional approach is to "reseed" a cluster by upgrading a single
node with <ulink url="https://www.postgresql.org/docs/current/pgupgrade.html">pg_upgrade</ulink>
and recloning standbys from this.
</para>
<para>
To minimize downtime during major upgrades from PostgreSQL 9.4 and later,
<ulink url="https://www.2ndquadrant.com/en/resources/pglogical/">pglogical</ulink>
can be used to set up a parallel cluster using the newer PostgreSQL version,
which can be kept in sync with the existing production cluster until the
new cluster is ready to be put into production.
</para>
</sect2>
<sect2 id="faq-libdir-repmgr-error">
<title>What does this error mean: <literal>ERROR: could not access file "$libdir/repmgr"</literal>?</title>
<para>
It means the &repmgr; extension code is not installed in the
PostgreSQL application directory. This typically happens when using PostgreSQL
packages provided by a third-party vendor, which often have different
filesystem layouts.
</para>
<para>
Either use PostgreSQL packages provided by the community or 2ndQuadrant; if this
is not possible, contact your vendor for assistance.
</para>
</sect2>
<sect2 id="faq-libdir-repmgr-error">
<title>What does this error mean: <literal>ERROR: could not access file "$libdir/repmgr"</literal>?</title>
<para>
It means the &repmgr; extension code is not installed in the
PostgreSQL application directory. This typically happens when using PostgreSQL
packages provided by a third-party vendor, which often have different
filesystem layouts.
</para>
<para>
Either use PostgreSQL packages provided by the community or 2ndQuadrant; if this
is not possible, contact your vendor for assistance.
</para>
</sect2>
<sect2 id="faq-old-packages">
<title>How can I obtain old versions of &repmgr; packages?</title>
<para>
See appendix <xref linkend="packages-old-versions"/> for details.
</para>
</sect2>
<sect2 id="faq-old-packages">
<title>How can I obtain old versions of &repmgr; packages?</title>
<para>
See appendix <xref linkend="packages-old-versions"/> for details.
</para>
</sect2>
<sect2 id="faq-repmgr-required-for-replication">
<title>Is &repmgr; required for streaming replication?</title>
<para>
No.
</para>
<para>
&repmgr; (together with &repmgrd;) assists with
<emphasis>managing</emphasis> replication. It does not actually perform replication, which
is part of the core PostgreSQL functionality.
</para>
</sect2>
<sect2 id="faq-repmgr-required-for-replication">
<title>Is &repmgr; required for streaming replication?</title>
<para>
No.
</para>
<para>
&repmgr; (together with &repmgrd;) assists with
<emphasis>managing</emphasis> replication. It does not actually perform replication, which
is part of the core PostgreSQL functionality.
</para>
</sect2>
<sect2 id="faq-what-if-repmgr-uninstalled">
<title>Will replication stop working if &repmgr; is uninstalled?</title>
<para>
No. See preceding question.
</para>
</sect2>
<sect2 id="faq-what-if-repmgr-uninstalled">
<title>Will replication stop working if &repmgr; is uninstalled?</title>
<para>
No. See preceding question.
</para>
</sect2>
<sect2 id="faq-version-mix">
<title>Does it matter if different &repmgr; versions are present in the replication cluster?</title>
<para>
Yes. If different &quot;major&quot; &repmgr; versions (e.g. 3.3.x and 4.1.x) are present,
&repmgr; (in particular &repmgrd;)
may not run, or run properly, or in the worst case (if different &repmgrd;
versions are running and there are differences in the failover implementation) break
your replication cluster.
</para>
<para>
If different &quot;minor&quot; &repmgr; versions (e.g. 4.1.1 and 4.1.6) are installed,
&repmgr; will function, but we strongly recommend always running the same version
to ensure there are no unexpected suprises, e.g. a newer version behaving slightly
differently to the older version.
</para>
<para>
See also <link linkend="faq-upgrade-repmgr">Should I upgrade &repmgr;?</link>.
</para>
</sect2>
<sect2 id="faq-version-mix">
<title>Does it matter if different &repmgr; versions are present in the replication cluster?</title>
<para>
Yes. If different &quot;major&quot; &repmgr; versions (e.g. 3.3.x and 4.1.x) are present,
&repmgr; (in particular &repmgrd;)
may not run, or run properly, or in the worst case (if different &repmgrd;
versions are running and there are differences in the failover implementation) break
your replication cluster.
</para>
<para>
If different &quot;minor&quot; &repmgr; versions (e.g. 4.1.1 and 4.1.6) are installed,
&repmgr; will function, but we strongly recommend always running the same version
to ensure there are no unexpected suprises, e.g. a newer version behaving slightly
differently to the older version.
</para>
<para>
See also <link linkend="faq-upgrade-repmgr">Should I upgrade &repmgr;?</link>.
</para>
</sect2>
<sect2 id="faq-upgrade-repmgr">
<title>Should I upgrade &repmgr;?</title>
<para>
Yes.
</para>
<para>
We don't release new versions for fun, you know. Upgrading may require a little effort,
but running an older &repmgr; version with bugs which have since been fixed may end up
costing you more effort. The same applies to PostgreSQL itself.
</para>
<sect2 id="faq-upgrade-repmgr">
<title>Should I upgrade &repmgr;?</title>
<para>
Yes.
</para>
<para>
We don't release new versions for fun, you know. Upgrading may require a little effort,
but running an older &repmgr; version with bugs which have since been fixed may end up
costing you more effort. The same applies to PostgreSQL itself.
</para>
</sect2>
</sect2>
<sect2 id="faq-repmgr-conf-data-directory">
<title>Why do I need to specify the data directory location in repmgr.conf?</title>
<para>
In some circumstances &repmgr; may need to access a PostgreSQL data
directory while the PostgreSQL server is not running, e.g. to confirm
it shut down cleanly during a <link linkend="performing-switchover">switchover</link>.
</para>
<para>
Additionally, this provides support when using &repmgr; on PostgreSQL 9.6 and
earlier, where the <literal>repmgr</literal> user is not a superuser; in that
case the <literal>repmgr</literal> user will not be able to access the
<literal>data_directory</literal> configuration setting, access to which is restricted
to superusers.
</para>
<para>
In PostgreSQL 10 and later, non-superusers can be added to the
<ulink url="https://www.postgresql.org/docs/current/default-roles.html">default role</ulink>
<option>pg_read_all_settings</option> (or the meta-role <option>pg_monitor</option>)
which will enable them to read this setting.
</para>
</sect2>
<sect2 id="faq-repmgr-conf-data-directory">
<title>Why do I need to specify the data directory location in repmgr.conf?</title>
<para>
In some circumstances &repmgr; may need to access a PostgreSQL data
directory while the PostgreSQL server is not running, e.g. to confirm
it shut down cleanly during a <link linkend="performing-switchover">switchover</link>.
</para>
<para>
Additionally, this provides support when using &repmgr; on PostgreSQL 9.6 and
earlier, where the <literal>repmgr</literal> user is not a superuser; in that
case the <literal>repmgr</literal> user will not be able to access the
<literal>data_directory</literal> configuration setting, access to which is restricted
to superusers. (In PostgreSQL 10 and later, non-superusers can be added to the
group <option>pg_read_all_settings</option> which will enable them to read this setting).
</para>
</sect2>
</sect1>
<sect2 id="faq-third-party-packages" xreflabel="Compatability with third party vendor packages">
<title>Are &repmgr; packages compatible with <literal>$third_party_vendor</literal>'s packages?</title>
<para>
&repmgr; packages provided by 2ndQuadrant are compatible with the community-provided PostgreSQL
packages and any software provided by 2ndQuadrant.
</para>
<para>
A number of other vendors provide their own versions of PostgreSQL packages, often with different
package naming schemes and/or file locations.
</para>
<para>
We cannot guarantee that &repmgr; packages will be compatible with these packages.
It may be possible to override package dependencies (e.g. <literal>rpm --nodeps</literal>
for CentOS-based systems or <literal>dpkg --force-depends</literal> for Debian-based systems).
</para>
</sect2>
</sect1>
<sect1 id="faq-repmgr" xreflabel="repmgr">
<title><command>repmgr</command></title>
<sect1 id="faq-repmgr" xreflabel="repmgr">
<title><command>repmgr</command></title>
<sect2 id="faq-register-existing-node" xreflabel="registering an existing node">
<title>Can I register an existing PostgreSQL server with repmgr?</title>
<para>
Yes, any existing PostgreSQL server which is part of the same replication
cluster can be registered with &repmgr;. There's no requirement for a
standby to have been cloned using &repmgr;.
</para>
</sect2>
<sect2 id="faq-register-existing-node" xreflabel="registering an existing node">
<title>Can I register an existing PostgreSQL server with repmgr?</title>
<para>
Yes, any existing PostgreSQL server which is part of the same replication
cluster can be registered with &repmgr;. There's no requirement for a
standby to have been cloned using &repmgr;.
</para>
</sect2>
<sect2 id="faq-repmgr-clone-other-source" >
<title>Can I use a standby not cloned by &repmgr; as a &repmgr; node?</title>
<sect2 id="faq-repmgr-clone-other-source" >
<title>Can I use a standby not cloned by &repmgr; as a &repmgr; node?</title>
<para>
For a standby which has been manually cloned or recovered from an external
backup manager such as Barman, the command
<command><link linkend="repmgr-standby-clone">repmgr standby clone --recovery-conf-only</link></command>
can be used to create the correct <filename>recovery.conf</filename> file for
use with &repmgr; (and will create a replication slot if required). Once this has been done,
<link linkend="repmgr-standby-register">register the node</link> as usual.
</para>
</sect2>
<para>
For a standby which has been manually cloned or recovered from an external
backup manager such as Barman, the command
<command><link linkend="repmgr-standby-clone">repmgr standby clone --recovery-conf-only</link></command>
can be used to create the correct <filename>recovery.conf</filename> file for
use with &repmgr; (and will create a replication slot if required). Once this has been done,
<link linkend="repmgr-standby-register">register the node</link> as usual.
</para>
</sect2>
<sect2 id="faq-repmgr-recovery-conf" >
<title>What does &repmgr; write in <filename>recovery.conf</filename>, and what options can be set there?</title>
<para>
See section <link linkend="repmgr-standby-clone-recovery-conf">Customising recovery.conf</link>.
</para>
</sect2>
<sect2 id="faq-repmgr-recovery-conf" >
<title>What does &repmgr; write in <filename>recovery.conf</filename>, and what options can be set there?</title>
<para>
See section <link linkend="repmgr-standby-clone-recovery-conf">Customising recovery.conf</link>.
</para>
</sect2>
<sect2 id="faq-repmgr-failed-primary-standby" xreflabel="Reintegrate a failed primary as a standby">
<title>How can a failed primary be re-added as a standby?</title>
<para>
This is a two-stage process. First, the failed primary's data directory
must be re-synced with the current primary; secondly the failed primary
needs to be re-registered as a standby.
</para>
<para>
It's possible to use <command>pg_rewind</command> to re-synchronise the existing data
directory, which will usually be much
faster than re-cloning the server. However <command>pg_rewind</command> can only
be used if PostgreSQL either has <varname>wal_log_hints</varname> enabled, or
data checksums were enabled when the cluster was initialized.
</para>
<para>
Note that <command>pg_rewind</command> is available as part of the core PostgreSQL
distribution from PostgreSQL 9.5, and as a third-party utility for PostgreSQL 9.3 and 9.4.
</para>
<para>
&repmgr; provides the command <command>repmgr node rejoin</command> which can
optionally execute <command>pg_rewind</command>; see the <xref linkend="repmgr-node-rejoin"/>
documentation for details, in particular the section <xref linkend="repmgr-node-rejoin-pg-rewind"/>.
</para>
<para>
If <command>pg_rewind</command> cannot be used, then the data directory will need
to be re-cloned from scratch.
</para>
<sect2 id="faq-repmgr-failed-primary-standby" xreflabel="Reintegrate a failed primary as a standby">
<title>How can a failed primary be re-added as a standby?</title>
<para>
This is a two-stage process. First, the failed primary's data directory
must be re-synced with the current primary; secondly the failed primary
needs to be re-registered as a standby.
</para>
<para>
It's possible to use <command>pg_rewind</command> to re-synchronise the existing data
directory, which will usually be much
faster than re-cloning the server. However <command>pg_rewind</command> can only
be used if PostgreSQL either has <varname>wal_log_hints</varname> enabled, or
data checksums were enabled when the cluster was initialized.
</para>
<para>
Note that <command>pg_rewind</command> is available as part of the core PostgreSQL
distribution from PostgreSQL 9.5, and as a third-party utility for PostgreSQL 9.3 and 9.4.
</para>
<para>
&repmgr; provides the command <command>repmgr node rejoin</command> which can
optionally execute <command>pg_rewind</command>; see the <xref linkend="repmgr-node-rejoin"/>
documentation for details, in particular the section <xref linkend="repmgr-node-rejoin-pg-rewind"/>.
</para>
<para>
If <command>pg_rewind</command> cannot be used, then the data directory will need
to be re-cloned from scratch.
</para>
</sect2>
</sect2>
<sect2 id="faq-repmgr-check-configuration" xreflabel="Check PostgreSQL configuration">
<title>Is there an easy way to check my primary server is correctly configured for use with &repmgr;?</title>
<para>
Execute <command><link linkend="repmgr-standby-clone">repmgr standby clone</link></command>
with the <literal>--dry-run</literal> option; this will report any configuration problems
which need to be rectified.
</para>
</sect2>
<sect2 id="faq-repmgr-check-configuration" xreflabel="Check PostgreSQL configuration">
<title>Is there an easy way to check my primary server is correctly configured for use with &repmgr;?</title>
<para>
Execute <command><link linkend="repmgr-standby-clone">repmgr standby clone</link></command>
with the <literal>--dry-run</literal> option; this will report any configuration problems
which need to be rectified.
</para>
</sect2>
<sect2 id="faq-repmgr-clone-skip-config-files" xreflabel="">
<title>When cloning a standby, how can I get &repmgr; to copy
<filename>postgresql.conf</filename> and <filename>pg_hba.conf</filename> from the PostgreSQL configuration
directory in <filename>/etc</filename>?</title>
<para>
Use the command line option <literal>--copy-external-config-files</literal>. For more details
see <xref linkend="repmgr-standby-clone-config-file-copying"/>.
</para>
</sect2>
<sect2 id="faq-repmgr-clone-skip-config-files" xreflabel="">
<title>When cloning a standby, how can I get &repmgr; to copy
<filename>postgresql.conf</filename> and <filename>pg_hba.conf</filename> from the PostgreSQL configuration
directory in <filename>/etc</filename>?</title>
<para>
Use the command line option <literal>--copy-external-config-files</literal>. For more details
see <xref linkend="repmgr-standby-clone-config-file-copying"/>.
</para>
</sect2>
<sect2 id="faq-repmgr-shared-preload-libaries-no-repmgrd" xreflabel="shared_preload_libraries without repmgrd">
<title>Do I need to include <literal>shared_preload_libraries = 'repmgr'</literal>
in <filename>postgresql.conf</filename> if I'm not using &repmgrd;?</title>
<para>
No, the <literal>repmgr</literal> shared library is only needed when running &repmgrd;.
If you later decide to run &repmgrd;, you just need to add
<literal>shared_preload_libraries = 'repmgr'</literal> and restart PostgreSQL.
</para>
</sect2>
<sect2 id="faq-repmgr-shared-preload-libaries-no-repmgrd" xreflabel="shared_preload_libraries without repmgrd">
<title>Do I need to include <literal>shared_preload_libraries = 'repmgr'</literal>
in <filename>postgresql.conf</filename> if I'm not using &repmgrd;?</title>
<para>
No, the <literal>repmgr</literal> shared library is only needed when running &repmgrd;.
If you later decide to run &repmgrd;, you just need to add
<literal>shared_preload_libraries = 'repmgr'</literal> and restart PostgreSQL.
</para>
</sect2>
<sect2 id="faq-repmgr-permissions" xreflabel="Replication permission problems">
<title>I've provided replication permission for the <literal>repmgr</literal> user in <filename>pg_hba.conf</filename>
but <command>repmgr</command>/&repmgrd; complains it can't connect to the server... Why?</title>
<para>
<command>repmgr</command> and &repmgrd; need to be able to connect to the repmgr database
with a normal connection to query metadata. The <literal>replication</literal> connection
permission is for PostgreSQL's streaming replication (and doesn't necessarily need to be the <literal>repmgr</literal> user).
</para>
</sect2>
<sect2 id="faq-repmgr-permissions" xreflabel="Replication permission problems">
<title>I've provided replication permission for the <literal>repmgr</literal> user in <filename>pg_hba.conf</filename>
but <command>repmgr</command>/&repmgrd; complains it can't connect to the server... Why?</title>
<para>
<command>repmgr</command> and &repmgrd; need to be able to connect to the repmgr database
with a normal connection to query metadata. The <literal>replication</literal> connection
permission is for PostgreSQL's streaming replication (and doesn't necessarily need to be the <literal>repmgr</literal> user).
</para>
</sect2>
<sect2 id="faq-repmgr-clone-provide-primary-conninfo" xreflabel="Providing primary connection parameters">
<title>When cloning a standby, why do I need to provide the connection parameters
for the primary server on the command line, not in the configuration file?</title>
<para>
Cloning a standby is a one-time action; the role of the server being cloned
from could change, so fixing it in the configuration file would create
confusion. If &repmgr; needs to establish a connection to the primary
server, it can retrieve this from the <literal>repmgr.nodes</literal> table on the local
node, and if necessary scan the replication cluster until it locates the active primary.
</para>
</sect2>
<sect2 id="faq-repmgr-clone-provide-primary-conninfo" xreflabel="Providing primary connection parameters">
<title>When cloning a standby, why do I need to provide the connection parameters
for the primary server on the command line, not in the configuration file?</title>
<para>
Cloning a standby is a one-time action; the role of the server being cloned
from could change, so fixing it in the configuration file would create
confusion. If &repmgr; needs to establish a connection to the primary
server, it can retrieve this from the <literal>repmgr.nodes</literal> table on the local
node, and if necessary scan the replication cluster until it locates the active primary.
</para>
</sect2>
<sect2 id="faq-repmgr-clone-waldir-xlogdir" xreflabel="Providing a custom WAL directory">
<title>When cloning a standby, how do I ensure the WAL files are placed in a custom directory?</title>
<para>
Provide the option <literal>--waldir</literal> (<literal>--xlogdir</literal> in PostgreSQL 9.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>
</sect2>
<sect2 id="faq-repmgr-clone-waldir-xlogdir" xreflabel="Providing a custom WAL directory">
<title>When cloning a standby, how do I ensure the WAL files are placed in a custom directory?</title>
<para>
Provide the option <literal>--waldir</literal> (<literal>--xlogdir</literal> in PostgreSQL 9.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>
</sect2>
<sect2 id="faq-repmgr-events-no-fkey" xreflabel="No foreign key on node_id in repmgr.events">
<title>Why is there no foreign key on the <literal>node_id</literal> column in the <literal>repmgr.events</literal>
table?</title>
<para>
Under some circumstances event notifications can be generated for servers
which have not yet been registered; it's also useful to retain a record
of events which includes servers removed from the replication cluster
which no longer have an entry in the <literal>repmgr.nodes</literal> table.
</para>
</sect2>
<sect2 id="faq-repmgr-events-no-fkey" xreflabel="No foreign key on node_id in repmgr.events">
<title>Why is there no foreign key on the <literal>node_id</literal> column in the <literal>repmgr.events</literal>
table?</title>
<para>
Under some circumstances event notifications can be generated for servers
which have not yet been registered; it's also useful to retain a record
of events which includes servers removed from the replication cluster
which no longer have an entry in the <literal>repmgr.nodes</literal> table.
</para>
</sect2>
<sect2 id="faq-repmgr-recovery-conf-quoted-values" xreflabel="Quoted values in recovery.conf">
<title>Why are some values in <filename>recovery.conf</filename> surrounded by pairs of single quotes?</title>
<para>
This is to ensure that user-supplied values which are written as parameter values in <filename>recovery.conf</filename>
are escaped correctly and do not cause errors when <filename>recovery.conf</filename> is parsed.
</para>
<para>
The escaping is performed by an internal PostgreSQL routine, which leaves strings consisting
of digits and alphabetical characters only as-is, but wraps everything else in pairs of single quotes,
even if the string does not contain any characters which need escaping.
</para>
</sect2>
<sect2 id="faq-repmgr-recovery-conf-quoted-values" xreflabel="Quoted values in recovery.conf">
<title>Why are some values in <filename>recovery.conf</filename> surrounded by pairs of single quotes?</title>
<para>
This is to ensure that user-supplied values which are written as parameter values in <filename>recovery.conf</filename>
are escaped correctly and do not cause errors when <filename>recovery.conf</filename> is parsed.
</para>
<para>
The escaping is performed by an internal PostgreSQL routine, which leaves strings consisting
of digits and alphabetical characters only as-is, but wraps everything else in pairs of single quotes,
even if the string does not contain any characters which need escaping.
</para>
</sect2>
</sect1>
</sect1>
<sect1 id="faq-repmgrd" xreflabel="repmgrd">
<title>&repmgrd;</title>
<sect1 id="faq-repmgrd" xreflabel="repmgrd">
<title>&repmgrd;</title>
<sect2 id="faq-repmgrd-prevent-promotion" xreflabel="Prevent standby from being promoted to primary">
<title>How can I prevent a node from ever being promoted to primary?</title>
<para>
In <filename>repmgr.conf</filename>, set its priority to a value of <literal>0</literal>; apply the changed setting with
<command><link linkend="repmgr-standby-register">repmgr standby register --force</link></command>.
</para>
<para>
Additionally, if <varname>failover</varname> is set to <literal>manual</literal>, the node will never
be considered as a promotion candidate.
</para>
</sect2>
<sect2 id="faq-repmgrd-prevent-promotion" xreflabel="Prevent standby from being promoted to primary">
<title>How can I prevent a node from ever being promoted to primary?</title>
<para>
In <filename>repmgr.conf</filename>, set its priority to a value of <literal>0</literal>; apply the changed setting with
<command><link linkend="repmgr-standby-register">repmgr standby register --force</link></command>.
</para>
<para>
Additionally, if <varname>failover</varname> is set to <literal>manual</literal>, the node will never
be considered as a promotion candidate.
</para>
</sect2>
<sect2 id="faq-repmgrd-delayed-standby" xreflabel="Delayed standby support">
<title>Does &repmgrd; support delayed standbys?</title>
<para>
&repmgrd; can monitor delayed standbys - those set up with
<varname>recovery_min_apply_delay</varname> set to a non-zero value
in <filename>recovery.conf</filename> - but as it's not currently possible
to directly examine the value applied to the standby, &repmgrd;
may not be able to properly evaluate the node as a promotion candidate.
</para>
<para>
We recommend that delayed standbys are explicitly excluded from promotion
by setting <varname>priority</varname> to <literal>0</literal> in
<filename>repmgr.conf</filename>.
</para>
<para>
Note that after registering a delayed standby, &repmgrd; will only start
once the metadata added in the primary node has been replicated.
</para>
</sect2>
<sect2 id="faq-repmgrd-delayed-standby" xreflabel="Delayed standby support">
<title>Does &repmgrd; support delayed standbys?</title>
<para>
&repmgrd; can monitor delayed standbys - those set up with
<varname>recovery_min_apply_delay</varname> set to a non-zero value
in <filename>recovery.conf</filename> - but as it's not currently possible
to directly examine the value applied to the standby, &repmgrd;
may not be able to properly evaluate the node as a promotion candidate.
</para>
<para>
We recommend that delayed standbys are explicitly excluded from promotion
by setting <varname>priority</varname> to <literal>0</literal> in
<filename>repmgr.conf</filename>.
</para>
<para>
Note that after registering a delayed standby, &repmgrd; will only start
once the metadata added in the primary node has been replicated.
</para>
</sect2>
<sect2 id="faq-repmgrd-logfile-rotate" xreflabel="repmgrd logfile rotation">
<title>How can I get &repmgrd; to rotate its logfile?</title>
<para>
Configure your system's <literal>logrotate</literal> service to do this; see <xref linkend="repmgrd-log-rotation"/>.
</para>
<sect2 id="faq-repmgrd-logfile-rotate" xreflabel="repmgrd logfile rotation">
<title>How can I get &repmgrd; to rotate its logfile?</title>
<para>
Configure your system's <literal>logrotate</literal> service to do this; see <xref linkend="repmgrd-log-rotation"/>.
</para>
</sect2>
</sect2>
<sect2 id="faq-repmgrd-recloned-no-start" xreflabel="repmgrd not restarting after node cloned">
<title>I've recloned a failed primary as a standby, but &repmgrd; refuses to start?</title>
<para>
Check you registered the standby after recloning. If unregistered, the standby
cannot be considered as a promotion candidate even if <varname>failover</varname> is set to
<literal>automatic</literal>, which is probably not what you want. &repmgrd; will start if
<varname>failover</varname> is set to <literal>manual</literal> so the node's replication status can still
be monitored, if desired.
</para>
</sect2>
<sect2 id="faq-repmgrd-recloned-no-start" xreflabel="repmgrd not restarting after node cloned">
<title>I've recloned a failed primary as a standby, but &repmgrd; refuses to start?</title>
<para>
Check you registered the standby after recloning. If unregistered, the standby
cannot be considered as a promotion candidate even if <varname>failover</varname> is set to
<literal>automatic</literal>, which is probably not what you want. &repmgrd; will start if
<varname>failover</varname> is set to <literal>manual</literal> so the node's replication status can still
be monitored, if desired.
</para>
</sect2>
<sect2 id="faq-repmgrd-pg-bindir" xreflabel="repmgrd does not apply pg_bindir to promote_command or follow_command">
<title>
&repmgrd; ignores pg_bindir when executing <varname>promote_command</varname> or <varname>follow_command</varname>
</title>
<para>
<varname>promote_command</varname> or <varname>follow_command</varname> can be user-defined scripts,
so &repmgr; will not apply <option>pg_bindir</option> even if excuting &repmgr;. Always provide the full
path; see <xref linkend="repmgrd-automatic-failover-configuration"/> for more details.
</para>
</sect2>
<sect2 id="faq-repmgrd-pg-bindir" xreflabel="repmgrd does not apply pg_bindir to promote_command or follow_command">
<title>
&repmgrd; ignores pg_bindir when executing <varname>promote_command</varname> or <varname>follow_command</varname>
</title>
<para>
<varname>promote_command</varname> or <varname>follow_command</varname> can be user-defined scripts,
so &repmgr; will not apply <option>pg_bindir</option> even if excuting &repmgr;. Always provide the full
path; see <xref linkend="repmgrd-automatic-failover-configuration"/> for more details.
</para>
</sect2>
<sect2 id="faq-repmgrd-startup-no-upstream" xreflabel="repmgrd does not start if upstream node is not running">
<title>
&repmgrd; aborts startup with the error "<literal>upstream node must be running before repmgrd can start</literal>"
</title>
<para>
&repmgrd; does this to avoid starting up on a replication cluster
which is not in a healthy state. If the upstream is unavailable, &repmgrd;
may initiate a failover immediately after starting up, which could have unintended side-effects,
particularly if &repmgrd; is not running on other nodes.
</para>
<para>
In particular, it's possible that the node's local copy of the <literal>repmgr.nodes</literal> copy
is out-of-date, which may lead to incorrect failover behaviour.
</para>
<para>
The onus is therefore on the adminstrator to manually set the cluster to a stable, healthy state before
starting &repmgrd;.
</para>
</sect2>
<sect2 id="faq-repmgrd-startup-no-upstream" xreflabel="repmgrd does not start if upstream node is not running">
<title>
&repmgrd; aborts startup with the error "<literal>upstream node must be running before repmgrd can start</literal>"
</title>
<para>
&repmgrd; does this to avoid starting up on a replication cluster
which is not in a healthy state. If the upstream is unavailable, &repmgrd;
may initiate a failover immediately after starting up, which could have unintended side-effects,
particularly if &repmgrd; is not running on other nodes.
</para>
<para>
In particular, it's possible that the node's local copy of the <literal>repmgr.nodes</literal> copy
is out-of-date, which may lead to incorrect failover behaviour.
</para>
<para>
The onus is therefore on the adminstrator to manually set the cluster to a stable, healthy state before
starting &repmgrd;.
</para>
</sect2>
</sect1>
</sect1>
</appendix>

View File

@@ -122,7 +122,7 @@
<row>
<entry>Package name example:</entry>
<entry><filename>repmgr10-4.0.4-1.rhel7.x86_64</filename></entry>
<entry><filename>repmgr11-4.4.0-1.rhel7.x86_64</filename></entry>
</row>
<row>
@@ -132,12 +132,12 @@
<row>
<entry>Installation command:</entry>
<entry><literal>yum install repmgr10</literal></entry>
<entry><literal>yum install repmgr11</literal></entry>
</row>
<row>
<entry>Binary location:</entry>
<entry><filename>/usr/pgsql-10/bin</filename></entry>
<entry><filename>/usr/pgsql-11/bin</filename></entry>
</row>
<row>
@@ -147,22 +147,22 @@
<row>
<entry>Configuration file location:</entry>
<entry><filename>/etc/repmgr/10/repmgr.conf</filename></entry>
<entry><filename>/etc/repmgr/11/repmgr.conf</filename></entry>
</row>
<row>
<entry>Data directory:</entry>
<entry><filename>/var/lib/pgsql/10/data</filename></entry>
<entry><filename>/var/lib/pgsql/11/data</filename></entry>
</row>
<row>
<entry>repmgrd service command:</entry>
<entry><command>systemctl [start|stop|restart|reload] repmgr10</command></entry>
<entry><command>systemctl [start|stop|restart|reload] repmgr11</command></entry>
</row>
<row>
<entry>repmgrd service file location:</entry>
<entry><filename>/usr/lib/systemd/system/repmgr10.service</filename></entry>
<entry><filename>/usr/lib/systemd/system/repmgr11.service</filename></entry>
</row>
<row>
@@ -323,7 +323,7 @@
<row>
<entry>Package name example:</entry>
<entry><filename>postgresql-10-repmgr</filename></entry>
<entry><filename>postgresql-11-repmgr</filename></entry>
</row>
<row>
@@ -333,12 +333,12 @@
<row>
<entry>Installation command:</entry>
<entry><literal>apt-get install postgresql-10-repmgr</literal></entry>
<entry><literal>apt-get install postgresql-11-repmgr</literal></entry>
</row>
<row>
<entry>Binary location:</entry>
<entry><filename>/usr/lib/postgresql/10/bin</filename></entry>
<entry><filename>/usr/lib/postgresql/11/bin</filename></entry>
</row>
<row>
@@ -353,12 +353,12 @@
<row>
<entry>Data directory:</entry>
<entry><filename>/var/lib/postgresql/10/main</filename></entry>
<entry><filename>/var/lib/postgresql/11/main</filename></entry>
</row>
<row>
<entry>PostgreSQL service command:</entry>
<entry><command>systemctl [start|stop|restart|reload] postgresql@10-main</command></entry>
<entry><command>systemctl [start|stop|restart|reload] postgresql@11-main</command></entry>
</row>
@@ -386,7 +386,7 @@
it's recommended to execute <command>pg_ctlcluster</command> (as <literal>root</literal>,
either directly or via <command>sudo</command>), e.g.:
<programlisting>
<command>pg_ctlcluster 10 main [start|stop|restart|reload]</command></programlisting>
<command>pg_ctlcluster 11 main [start|stop|restart|reload]</command></programlisting>
</para>
<para>
For pre-<application>systemd</application> systems, <command>pg_ctlcluster</command>

View File

@@ -15,33 +15,313 @@
See also: <xref linkend="upgrading-repmgr"/>
</para>
<sect1 id="release-4.4">
<title>Release 4.4</title>
<para><emphasis>?? June, 2019</emphasis></para>
<!-- remember to update the release date in ../repmgr_version.h.in -->
<sect2>
<title>repmgr client enhancements</title>
<sect1 id="release-5.0">
<title id="release-current">Release 5.0</title>
<para><emphasis>Tue 15 October, 2019</emphasis></para>
<para>
&repmgr; 5.0 is a major release.
</para>
<para>
For details on how to upgrade an existing &repmgr; installation, see
documentation section <link linkend="upgrading-major-version">Upgrading a major version release</link>.
</para>
<para>
If &repmgrd; is in use, a PostgreSQL restart <emphasis>is</emphasis> required;
in that case we suggest combining this &repmgr; upgrade with the next PostgreSQL
minor release, which will require a PostgreSQL restart in any case.
</para>
<sect2>
<title>Compatibility changes</title>
<sect3 id="repmgr-5-0-config-file-parsing">
<title>Configuration file parsing has been made stricter</title>
<para>
&repmgr; now parses <link linkend="configuration-file-format">configuration files</link>
in the same way that PostgreSQL itself does, which means some files used with
earlier &repmgr; versions may need slight modification before they can be used
with &repmgr; 5 and later.
</para>
<para>
The main change is that string parameters should always be enclosed in single quotes.
</para>
<para>
For example, in &repmgr; 4.4 and earlier, the following <filename>repmgr.conf</filename>
entry was valid:
<programlisting>
conninfo=host=node1 user=repmgr dbname=repmgr connect_timeout=2</programlisting>
This must now be changed to:
<programlisting>
conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlisting>
</para>
<para>
Note that simple string identifiers (e.g. <literal>node_name=node1</literal>)
may remain unquoted, though we recommend always enclosing
strings in single quotes.
</para>
<para>
Additionally, leading/trailing white space between single quotes will no longer
be trimmed; the entire string between single quotes will be
taken literally.
</para>
<para>
Strings enclosed in double quotes (e.g. <literal>node_name=&quot;node1&quot;</literal>)
will now be rejected; previously they were accepted, but the double quotes were
interpreted as part of the string, which was a frequent cause of confusion.
</para>
<para>
This syntax matches that used by PostgreSQL itself.
</para>
</sect3>
<sect3>
<title>Some &quot;repmgr daemon ...&quot; commands renamed</title>
<para>
Some &quot;<command>repmgr daemon ...</command>&quot; commands have been renamed to
&quot;<command>repmgr service ...</command>&quot; as they have a cluster-wide effect
and to avoid giving the impression they affect only the local &repmgr; daemon.
</para>
<para>
The following commands are affected:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara>
<command>repmgr daemon pause</command>
(now <link linkend="repmgr-service-pause"><command>repmgr service pause</command></link>)
</simpara>
</listitem>
<listitem>
<simpara>
<command>repmgr daemon unpause</command>
(now <link linkend="repmgr-service-unpause"><command>repmgr service unpause</command></link>)
</simpara>
</listitem>
<listitem>
<simpara>
<command>repmgr daemon status</command>
(now <link linkend="repmgr-service-status"><command>repmgr service status</command></link>)
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
The &quot;<command>repmgr daemon ...</command>&quot; form will still be accepted
for backwards compatibility.
</para>
</sect3>
<sect3>
<title>Some deprecated command line options removed</title>
<para>
The following command line options, which have been deprecated since &repmgr; 3.3
(and which no longer had any effect other than to generate a warning about their use)
have been removed:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara><option>--data-dir</option></simpara>
</listitem>
<listitem>
<simpara><option>--no-conninfo-password</option></simpara>
</listitem>
<listitem>
<simpara><option>--recovery-min-apply-delay</option></simpara>
</listitem>
</itemizedlist>
</para>
</sect3>
</sect2>
<sect2>
<title>General enhancements</title>
<para>
<itemizedlist>
<itemizedlist>
<listitem>
<para>
Support for PostgreSQL 12 added.
</para>
<para>
Beginning with PostgreSQL 12, replication configuration has been integrated
into the main PostgreSQL configuraton system and the conventional
<filename>recovery.conf</filename> file is no longer valid.
</para>
<para>
&repmgr; has been modified to be compatible with this change.
</para>
<para>
&repmgr; additionally takes advantage of the new <command>pg_promote()</command>
function, which enables a standby to be promoted to primary using an SQL
command.
</para>
</listitem>
<listitem>
<para>
The &repmgr; configuration file is now parsed using
<command>flex</command>, meaning it will be parsed in
the same way as PostgreSQL parses its own configuration
files.
</para>
<para>
This makes configuration file parsing more robust
and consistent.
</para>
<para>
See item <link linkend="repmgr-5-0-config-file-parsing">Configuration file parsing has been made stricter</link>
for details.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-clone"><command>repmgr standby clone</command></link>:
checks for availability of the &repmgr; extension on the upstream node have
been improved and error messages improved.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-clone"><command>repmgr standby clone</command></link>:
prevent a standby from being cloned from a witness server (PostgreSQL 9.6 and later only).
<para>
When executing &repmgr; remotely, if the &repmgr; log level was explicitly
provided (with <option>-L</option>/<option>--log-level</option>), that log level
will be passed to the remote &repmgr;.
</para>
<para>
This makes it possible to return log output when executing repmgr
remotely at a different level to the one defined in the remote
&repmgr;'s <filename>repmgr.conf</filename>.
</para>
<para>
This is particularly useful when <literal>DEBUG</literal> output is required.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<listitem>
<sect2>
<title>Bug fixes</title>
<para>
<itemizedlist>
<listitem>
<para>
<link linkend="repmgr-witness-register"><command>repmgr witness register</command></link>:
prevent a witness server from being registered on the replication cluster primary server
Check role membership when trying to read <literal>pg_settings</literal>.
</para>
<para>
Previously &repmgr; assumed only superusers could read <literal>pg_settings</literal>,
but from PostgreSQL 10, all members of the roles <literal>pg_read_all_settings</literal>
or <literal>pg_monitor</literal> are permitted to do this as well.
</para>
</listitem>
<listitem>
<para>
&repmgrd;: Fix handling of upstream node change check.
</para>
<para>
&repmgrd; has a check to see if the upstream node has unexpectedly
changed, e.g. if the repmgrd service is paused and the PostgreSQL
instance has been pointed to another node.
</para>
<para>
However this check was relying on the node record on the local node
being up-to-date, which may not be the case immediately after a
failover, when the node is still replaying records updated prior
to the node's own record being updated. In this case it will
mistakenly assume the node is following the original primary
and attempt to restart monitoring, which will fail as the original
primary is no longer available.
</para>
<para>
To prevent this, the node's record on the upstream node is checked
to see if the reported upstream <literal>node_id</literal> matches
the expected <literal>node_id</literal>. GitHub #587/#588.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
<sect1 id="release-4.4">
<title>Release 4.4</title>
<para><emphasis>Thu 27 June, 2019</emphasis></para>
<para>
&repmgr; 4.4 is a major release.
</para>
<para>
For details on how to upgrade an existing &repmgr; installation, see
documentation section <link linkend="upgrading-major-version">Upgrading a major version release</link>.
</para>
<para>
If &repmgrd; is in use, a PostgreSQL restart <emphasis>is</emphasis> required;
in that case we suggest combining this &repmgr; upgrade with the next PostgreSQL
minor release, which will require a PostgreSQL restart in any case.
</para>
<important>
<para>
On Debian-based systems, including Ubuntu, if using &repmgrd;
please ensure that in the file <filename>/etc/init.d/repmgrd</filename>, the parameter
<varname>REPMGRD_OPTS</varname> contains &quot;<literal>--daemonize=false</literal>&quot;, e.g.:
<programlisting>
# additional options
REPMGRD_OPTS="--daemonize=false"</programlisting>
</para>
<para>
For further details, see <link linkend="repmgrd-configuration-debian-ubuntu">repmgrd configuration on Debian/Ubuntu</link>.
</para>
</important>
<sect2>
<title>repmgr client enhancements</title>
<para>
<itemizedlist>
<listitem>
<para>
<link linkend="repmgr-standby-clone"><command>repmgr standby clone</command></link>:
prevent a standby from being cloned from a witness server (PostgreSQL 9.6 and later only).
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-witness-register"><command>repmgr witness register</command></link>:
prevent a witness server from being registered on the replication cluster primary server
(PostgreSQL 9.6 and later only).
</para>
</para>
<para>
Registering a witness on the primary node would defeat the purpose of having a witness server,
which is intended to remain running even if the cluster's primary goes down.
</para>
</listitem>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link>:
note that an active, reachable cluster primary is required for this command;
and provide a more helpful error message if no reachable primary could be found.
</para>
</listitem>
<listitem>
<para>
@@ -75,7 +355,6 @@
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>:
@@ -94,61 +373,61 @@
</note>
</listitem>
<listitem>
<listitem>
<para>
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
add <option>--repmgrd-force-unpause</option> to unpause all &repmgrd; instances after executing a switchover.
This will ensure that any &repmgrd; instances which were paused before the switchover will be
unpaused.
</para>
</listitem>
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
add <option>--repmgrd-force-unpause</option> to unpause all &repmgrd; instances after executing a switchover.
This will ensure that any &repmgrd; instances which were paused before the switchover will be
unpaused.
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-daemon-status"><command>repmgr daemon status</command></link>:
make output similar to that of
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>
for consistency and to make it easier to identify nodes not in the expected
state.
<link linkend="repmgr-service-status"><command>repmgr daemon status</command></link>:
make output similar to that of
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>
for consistency and to make it easier to identify nodes not in the expected
state.
</para>
</listitem>
<listitem>
<listitem>
<para>
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>:
display each node's timeline ID (PostgreSQL 9.6 and later only).
</para>
</listitem>
<listitem>
<para>
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>
and <link linkend="repmgr-daemon-status"><command>repmgr daemon status</command></link>:
show the upstream node name as reported by each individual node - this helps visualise
situations where the cluster is in an unexpected state, and provide a better idea of the
actual cluster state.
</para>
<para>
For example, if a cluster has divided somehow and a set of nodes are
following a new primary, when running either of these commands, &repmgr;
will now show the name of the primary those nodes are actually
following, rather than the now outdated node name recorded
on the other side of the &quot;split&quot;. A warning will also be issued
about the unexpected situation.
display each node's timeline ID (PostgreSQL 9.6 and later only).
</para>
</listitem>
<listitem>
<listitem>
<para>
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>
and <link linkend="repmgr-daemon-status"><command>repmgr daemon status</command></link>:
check if a node is attached to its advertised upstream node, and issue a
warning if the node is not attached.
</para>
</listitem>
and <link linkend="repmgr-service-status"><command>repmgr daemon status</command></link>:
show the upstream node name as reported by each individual node - this helps visualise
situations where the cluster is in an unexpected state, and provide a better idea of the
actual cluster state.
</para>
<para>
For example, if a cluster has divided somehow and a set of nodes are
following a new primary, when running either of these commands, &repmgr;
will now show the name of the primary those nodes are actually
following, rather than the now outdated node name recorded
on the other side of the &quot;split&quot;. A warning will also be issued
about the unexpected situation.
</para>
</listitem>
</itemizedlist>
</para>
<listitem>
<para>
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>
and <link linkend="repmgr-service-status"><command>repmgr daemon status</command></link>:
check if a node is attached to its advertised upstream node, and issue a
warning if the node is not attached.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
@@ -221,23 +500,41 @@
</para>
</listitem>
<listitem>
<para>
<listitem>
<para>
&repmgr;: when executing <link linkend="repmgr-standby-clone"><command>repmgr standby clone</command></link>
with the <option>--upstream-conninfo</option>, ensure that <varname>application_name</varname>
is set correctly in <varname>primary_conninfo</varname>.
</para>
</listitem>
<listitem>
<para>
&repmgr;: when executing <link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>,
don't abort if one or more nodes are not reachable <emphasis>and</emphasis>
they are marked as inactive.
</para>
</listitem>
they are marked as inactive.
</para>
</listitem>
<listitem>
<para>
<listitem>
<para>
&repmgr;: canonicalize the data directory path when parsing the configuration file, so
the provided path matches the path PostgreSQL reports as its data directory.
Otherwise, if e.g. the data directory is configured with a trailing slash,
<link linkend="repmgr-node-check"><command>repmgr node check --data-directory-config</command></link>
will return a spurious error.
</para>
</listitem>
<listitem>
<para>
&repmgrd;: fix memory leak which occurs while the monitored PostgreSQL node is <emphasis>not</emphasis>
running.
</para>
</listitem>
</itemizedlist>
</para>
</para>
</sect2>
@@ -271,7 +568,7 @@
&repmgr; 4.3 is a major release.
</para>
<para>
For details on how to upgrade an existing &repmgr; instrallation, see
For details on how to upgrade an existing &repmgr; installation, see
documentation section <link linkend="upgrading-major-version">Upgrading a major version release</link>.
</para>
<para>
@@ -281,19 +578,19 @@
</para>
<important>
<para>
On Debian-based systems, including Ubuntu, if using &repmgrd;
please ensure that in the file <filename>/etc/init.d/repmgrd</filename>, the parameter
<varname>REPMGRD_OPTS</varname> contains &quot;<literal>--daemonize=false</literal>&quot;, e.g.:
<programlisting>
<important>
<para>
On Debian-based systems, including Ubuntu, if using &repmgrd;
please ensure that in the file <filename>/etc/init.d/repmgrd</filename>, the parameter
<varname>REPMGRD_OPTS</varname> contains &quot;<literal>--daemonize=false</literal>&quot;, e.g.:
<programlisting>
# additional options
REPMGRD_OPTS="--daemonize=false"</programlisting>
</para>
<para>
For further details, see <link linkend="repmgrd-configuration-debian-ubuntu">repmgrd configuration on Debian/Ubuntu</link>.
</para>
</important>
</para>
<para>
For further details, see <link linkend="repmgrd-configuration-debian-ubuntu">repmgrd configuration on Debian/Ubuntu</link>.
</para>
</important>
<sect2>
<title>repmgr client enhancements</title>
@@ -340,7 +637,7 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<link linkend="repmgr-daemon-status"><command>repmgr daemon status</command></link>
<link linkend="repmgr-service-status"><command>repmgr daemon status</command></link>
additionally displays the node priority and the interval (in seconds) since the
&repmgrd; instance last verified its upstream node was available.
</para>
@@ -358,11 +655,11 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-cluster-show">repmgr cluster show</link></command>:
differentiate between unreachable nodes and nodes which are running but rejecting connections.
differentiate between unreachable nodes and nodes which are running but rejecting connections.
</para>
<para>
This makes it possible to see whether a node is unreachable at network level,
or if it is running but rejecting connections for some reason.
This makes it possible to see whether a node is unreachable at network level,
or if it is running but rejecting connections for some reason.
</para>
</listitem>
@@ -411,7 +708,7 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
&repmgrd; will no longer consider nodes where &repmgrd;
is not running as promotion candidates.
is not running as promotion candidates.
</para>
<para>
Previously, if &repmgrd; was not running on a node, but
@@ -449,15 +746,15 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
In a failover situation, &repmgrd; will not attempt to promote a
node if another primary has already appeared (e.g. by being promoted manually).
GitHub #420.
</para>
In a failover situation, &repmgrd; will not attempt to promote a
node if another primary has already appeared (e.g. by being promoted manually).
GitHub #420.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Bug fixes</title>
@@ -467,23 +764,23 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-cluster-show">repmgr cluster show</link></command>:
fix display of node IDs with multiple digits.
fix display of node IDs with multiple digits.
</para>
</listitem>
</listitem>
<listitem>
<para>
ensure <command><link linkend="repmgr-primary-unregister">repmgr primary unregister</link></command>
behaves correctly when executed on a witness server. GitHub #548.
behaves correctly when executed on a witness server. GitHub #548.
</para>
</listitem>
</listitem>
<listitem>
<para>
ensure <command><link linkend="repmgr-standby-register">repmgr standby register</link></command>
fails when <option>--upstream-node-id</option> is the same as the local node ID.
fails when <option>--upstream-node-id</option> is the same as the local node ID.
</para>
</listitem>
</listitem>
<listitem>
<para>
@@ -504,7 +801,7 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
&repmgr;: when executing <link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>,
verify the standby (promotion candidate) is currently attached to the primary (demotion candidate). GitHub #519.
verify the standby (promotion candidate) is currently attached to the primary (demotion candidate). GitHub #519.
</para>
</listitem>
@@ -512,7 +809,7 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<para>
&repmgr;: when executing <link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>,
avoid a potential race condition when comparing received WAL on the standby to the primary's shutdown location,
as the standby's walreceiver may not have yet flushed all received WAL to disk. GitHub #518.
as the standby's walreceiver may not have yet flushed all received WAL to disk. GitHub #518.
</para>
</listitem>
@@ -526,11 +823,11 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-node-check">repmgr node check</link></command>
will only consider physical replication slots, as the purpose
of slot checks is to warn about potential issues with
streaming replication standbys which are no longer attached.
</para>
</listitem>
will only consider physical replication slots, as the purpose
of slot checks is to warn about potential issues with
streaming replication standbys which are no longer attached.
</para>
</listitem>
<listitem>
<para>
@@ -561,19 +858,19 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<link linkend="upgrading-major-version">Upgrading a major version release</link>.
</para>
<important>
<para>
On Debian-based systems, including Ubuntu, if using &repmgrd;
please ensure that the in the file <filename>/etc/init.d/repmgrd</filename>, the parameter
<varname>REPMGRD_OPTS</varname> contains &quot;<literal>--daemonize=false</literal>&quot;, e.g.:
<programlisting>
<important>
<para>
On Debian-based systems, including Ubuntu, if using &repmgrd;
please ensure that the in the file <filename>/etc/init.d/repmgrd</filename>, the parameter
<varname>REPMGRD_OPTS</varname> contains &quot;<literal>--daemonize=false</literal>&quot;, e.g.:
<programlisting>
# additional options
REPMGRD_OPTS="--daemonize=false"</programlisting>
</para>
<para>
For further details, see <link linkend="repmgrd-configuration-debian-ubuntu">repmgrd daemon configuration on Debian/Ubuntu</link>.
</para>
</important>
</para>
<para>
For further details, see <link linkend="repmgrd-configuration-debian-ubuntu">repmgrd daemon configuration on Debian/Ubuntu</link>.
</para>
</important>
<sect2>
<title>Configuration file changes</title>
@@ -705,11 +1002,11 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
repmgr 4.1.1 contains a number of usability enhancements and bug fixes.
</para>
<para>
We recommend upgrading to this version as soon as possible.
This release can be installed as a simple package upgrade from repmgr 4.0 ~ 4.1.0;
We recommend upgrading to this version as soon as possible.
This release can be installed as a simple package upgrade from repmgr 4.0 ~ 4.1.0;
&repmgrd; (if running) should be restarted.
See <xref linkend="upgrading-repmgr"/> for more details.
</para>
</para>
<sect2>
<title>repmgr enhancements</title>
@@ -971,18 +1268,18 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-witness-unregister">repmgr witness unregister</link></command>
can be run on any node, by providing the ID of the witness node with <option>--node-id</option>.
(GitHub #472).
</para>
</listitem>
can be run on any node, by providing the ID of the witness node with <option>--node-id</option>.
(GitHub #472).
</para>
</listitem>
<listitem>
<para>
<command><link linkend="repmgr-standby-switchover">repmgr standby switchover</link></command>
will refuse to run if an exclusive backup is taking place on the current primary.
(GitHub #476).
</para>
</listitem>
(GitHub #476).
</para>
</listitem>
</itemizedlist>
</para>
@@ -1003,9 +1300,9 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
&repmgrd;: daemonize process by default.
In case, for whatever reason, the user does not wish to daemonize the
process, provide <option>--daemonize=false</option>.
(GitHub #458).
In case, for whatever reason, the user does not wish to daemonize the
process, provide <option>--daemonize=false</option>.
(GitHub #458).
</para>
</listitem>
@@ -1030,23 +1327,23 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-cluster-cleanup">repmgr cluster cleanup</link></command>:
add missing help options. (GitHub #461/#462).
</para>
</listitem>
add missing help options. (GitHub #461/#462).
</para>
</listitem>
<listitem>
<para>
Ensure witness node follows new primary after switchover. (GitHub #453).
</para>
</listitem>
Ensure witness node follows new primary after switchover. (GitHub #453).
</para>
</listitem>
<listitem>
<para>
<command><link linkend="repmgr-node-check">repmgr node check</link></command> and
<command><link linkend="repmgr-node-status">repmgr node status</link></command>:
fix witness node handling. (GitHub #451).
</para>
</listitem>
fix witness node handling. (GitHub #451).
</para>
</listitem>
<listitem>
<para>
@@ -1066,14 +1363,14 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<title>Release 4.0.6</title>
<para><emphasis>Thu June 14, 2018</emphasis></para>
<para>
&repmgr; 4.0.6 contains a number of bug fixes and usability enhancements.
&repmgr; 4.0.6 contains a number of bug fixes and usability enhancements.
</para>
<para>
We recommend upgrading to this version as soon as possible.
This release can be installed as a simple package upgrade from repmgr 4.0 ~ 4.0.5;
<para>
We recommend upgrading to this version as soon as possible.
This release can be installed as a simple package upgrade from repmgr 4.0 ~ 4.0.5;
&repmgrd; (if running) should be restarted. See <xref linkend="upgrading-repmgr"/>
for more details.
</para>
</para>
<sect2>
<title>Usability enhancements</title>
@@ -1091,33 +1388,33 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-standby-clone">repmgr standby clone</link></command>:
Improve handling of external configuration file copying, including consideration in
<option>--dry-run</option> check
(GitHub #443)
Improve handling of external configuration file copying, including consideration in
<option>--dry-run</option> check
(GitHub #443)
</para>
</listitem>
<listitem>
<para>
When using <option>--dry-run</option>, force log level to <literal>INFO</literal>
to ensure output will always be displayed
(GitHub #441)
to ensure output will always be displayed
(GitHub #441)
</para>
</listitem>
<listitem>
<para>
<command><link linkend="repmgr-standby-clone">repmgr standby clone</link></command>:
Improve documentation of <option>--recovery-conf-only</option> mode
(GitHub #438)
Improve documentation of <option>--recovery-conf-only</option> mode
(GitHub #438)
</para>
</listitem>
<listitem>
<para>
<command><link linkend="repmgr-standby-clone">repmgr standby clone</link></command>:
Don't require presence of <varname>user</varname> parameter in conninfo string
(GitHub #437)
Don't require presence of <varname>user</varname> parameter in conninfo string
(GitHub #437)
</para>
</listitem>
@@ -1143,23 +1440,23 @@ REPMGRD_OPTS="--daemonize=false"</programlisting>
<listitem>
<para>
<command><link linkend="repmgr-standby-follow">repmgr standby follow</link></command>:
check node has actually connected to new primary before reporting success
(GitHub #444)
check node has actually connected to new primary before reporting success
(GitHub #444)
</para>
</listitem>
<listitem>
<para>
<command><link linkend="repmgr-node-rejoin">repmgr node rejoin</link></command>:
Fix bug when parsing <option>--config-files</option> parameter
(GitHub #442)
Fix bug when parsing <option>--config-files</option> parameter
(GitHub #442)
</para>
</listitem>
<listitem>
<para>
&repmgrd;: ensure local node is counted as quorum member
(GitHub #439)
(GitHub #439)
</para>
</listitem>

View File

@@ -52,6 +52,24 @@
</itemizedlist>
</para>
<note>
<para>
Currently &repmgr;'s support for cloning from Barman is implemented by using
<productname>rsync</productname> to clone from the Barman server.
</para>
<para>
It is therefore not able to make use of Barman's parallel restore facility, which
is executed on the Barman server and clones to the target server.
</para>
<para>
Barman's parallel restore facility can be used by executing it manually on
the Barman server and integrating the resulting cloned standby using
<command><link linkend="repmgr-standby-clone">repmgr standby clone --recovery-conf-only</link></command>.
</para>
</note>
<sect2 id="cloning-from-barman-prerequisites">
<title>Prerequisites for cloning from Barman</title>
<para>
@@ -60,8 +78,7 @@
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<para>
the <varname>barman_server</varname> setting in <filename>repmgr.conf</filename> is the same as the
server configured in Barman;
the Barman catalogue must include at least one valid backup for this server;
</para>
</listitem>
<listitem>
@@ -72,19 +89,68 @@
</listitem>
<listitem>
<para>
the <varname>restore_command</varname> setting in <filename>repmgr.conf</filename> is configured to
use a copy of the <command>barman-wal-restore</command> script shipped with the
<literal>barman-cli</literal> package (see section <xref linkend="cloning-from-barman-restore-command"/>
below).
</para>
</listitem>
<listitem>
<para>
the Barman catalogue includes at least one valid backup for this server.
the <varname>barman_server</varname> setting in <filename>repmgr.conf</filename> is the same as the
server configured in Barman.
</para>
</listitem>
</itemizedlist>
</para>
<para>
For example, assuming Barman is located on the host &quot;<literal>barmansrv</literal>&quot;
under the &quot;<literal>barman</literal>&quot; user account,
<filename>repmgr.conf</filename> should contain the following entries:
<programlisting>
barman_host='barman@barmansrv'
barman_server='somedb'</programlisting>
</para>
<note>
<para>
To use a non-default Barman configuration file on the Barman server,
specify this in <filename>repmgr.conf</filename> with <filename>barman_config</filename>:
<programlisting>
barman_config=/path/to/barman.conf</programlisting>
</para>
</note>
<para>
We also recommend configuring the <varname>restore_command</varname> setting in <filename>repmgr.conf</filename>
to use the <command>barman-wal-restore</command> script
(see section <xref linkend="cloning-from-barman-restore-command"/> below).
</para>
<tip>
<simpara>
If you have a non-default SSH configuration on the Barman
server, e.g. using a port other than 22, then you can set those
parameters in a dedicated Host section in <filename>~/.ssh/config</filename>
corresponding to the value of <varname>barman_host</varname> in
<filename>repmgr.conf</filename>. See the <literal>Host</literal>
section in <command>man 5 ssh_config</command> for more details.
</simpara>
</tip>
<para>
It's now possible to clone a standby from Barman, e.g.:
<programlisting>
$ repmgr -f /etc/repmgr.conf -h node1 -U repmgr -d repmgr standby clone
NOTICE: destination directory "/var/lib/postgresql/data" provided
INFO: connecting to Barman server to verify backup for "test_cluster"
INFO: checking and correcting permissions on existing directory "/var/lib/postgresql/data"
INFO: creating directory "/var/lib/postgresql/data/repmgr"...
INFO: connecting to Barman server to fetch server parameters
INFO: connecting to source node
DETAIL: current installation size is 30 MB
NOTICE: retrieving backup from Barman...
(...)
NOTICE: standby clone (from Barman) complete
NOTICE: you can now start your PostgreSQL server
HINT: for example: pg_ctl -D /var/lib/postgresql/data start</programlisting>
</para>
<note>
<simpara>
Barman support is automatically enabled if <varname>barman_server</varname>
@@ -94,37 +160,7 @@
command line option.
</simpara>
</note>
<tip>
<simpara>
If you have a non-default SSH configuration on the Barman
server, e.g. using a port other than 22, then you can set those
parameters in a dedicated Host section in <filename>~/.ssh/config</filename>
corresponding to the value of<varname>barman_host</varname> in
<filename>repmgr.conf</filename>. See the <literal>Host</literal>
section in <command>man 5 ssh_config</command> for more details.
</simpara>
</tip>
<para>
It's now possible to clone a standby from Barman, e.g.:
<programlisting>
NOTICE: using configuration file "/etc/repmgr.conf"
NOTICE: destination directory "/var/lib/postgresql/data" provided
INFO: connecting to Barman server to verify backup for test_cluster
INFO: checking and correcting permissions on existing directory "/var/lib/postgresql/data"
INFO: creating directory "/var/lib/postgresql/data/repmgr"...
INFO: connecting to Barman server to fetch server parameters
INFO: connecting to upstream node
INFO: connected to source node, checking its state
INFO: successfully connected to source node
DETAIL: current installation size is 29 MB
NOTICE: retrieving backup from Barman...
receiving file list ...
(...)
NOTICE: standby clone (from Barman) complete
NOTICE: you can now start your PostgreSQL server
HINT: for example: pg_ctl -D /var/lib/postgresql/data start</programlisting>
</para>
</sect2>
<sect2 id="cloning-from-barman-restore-command" xreflabel="Using Barman as a WAL file source">
<title>Using Barman as a WAL file source</title>
@@ -142,35 +178,28 @@
</para>
<para>
<command>barman-wal-restore</command> is a Python script provided as part of the <literal>barman-cli</literal>
package (Barman 2.0 and later; for Barman 1.x the script is provided separately as
<command>barman-wal-restore.py</command>) which performs this function for Barman.
package (Barman 2.0 ~ 2.7) or as part of the core Barman distribution (Barman 2.8 and later).
</para>
<para>
To use <command>barman-wal-restore</command> with &repmgr;
and assuming Barman is located on the <literal>barmansrv</literal> host
To use <command>barman-wal-restore</command> with &repmgr;,
assuming Barman is located on the host &quot;<literal>barmansrv</literal>&quot;
under the &quot;<literal>barman</literal>&quot; user account,
and that <command>barman-wal-restore</command> is located as an executable at
<filename>/usr/bin/barman-wal-restore</filename>,
<filename>repmgr.conf</filename> should include the following lines:
<programlisting>
barman_host=barmansrv
barman_server=somedb
restore_command=/usr/bin/barman-wal-restore barmansrv somedb %f %p</programlisting>
barman_host='barman@barmansrv'
barman_server='somedb'
restore_command='/usr/bin/barman-wal-restore barmansrv somedb %f %p'</programlisting>
</para>
<note>
<simpara>
<command>barman-wal-restore</command> supports command line switches to
control parallelism (<literal>--parallel=N</literal>) and compression (
<literal>--bzip2</literal>, <literal>--gzip</literal>).
control parallelism (<literal>--parallel=N</literal>) and compression
(<literal>--bzip2</literal>, <literal>--gzip</literal>).
</simpara>
</note>
<note>
<para>
To use a non-default Barman configuration file on the Barman server,
specify this in <filename>repmgr.conf</filename> with <filename>barman_config</filename>:
<programlisting>
barman_config=/path/to/barman.conf</programlisting>
</para>
</note>
</sect2>
</sect1>

View File

@@ -0,0 +1,124 @@
<sect1 id="configuration-file-optional-settings" xreflabel="optional configuration file settings">
<title>Optional configuration file settings</title>
<indexterm>
<primary>repmgr.conf</primary>
<secondary>optional settings</secondary>
</indexterm>
<variablelist>
<varlistentry id="repmgr-conf-config-directory" xreflabel="config_directory">
<term><varname>config_directory</varname> (<type>string</type>)
<indexterm>
<primary><varname>config_directory</varname> configuration file parameter</primary>
</indexterm>
</term>
<listitem>
<para>
If PostgreSQL configuration files are located outside the data
directory, specify the directory where the main
<filename>postgresql.conf</filename> file is located.
</para>
<para>
This enables explicit provision of an external configuration file
directory, which if set will be passed to <command>pg_ctl</command> as the
<option>-D</option> parameter. Otherwise <command>pg_ctl</command> will
default to using the data directory, which will cause some operations
to fail if the configuration files are not present there.
</para>
<note>
<para>
This is implemented primarily for feature completeness and for
development/testing purposes. Users who have installed &repmgr; from
a package should <emphasis>not</emphasis> rely on to stop/start/restart PostgreSQL,
instead they should set the appropriate <option>service_..._command</option>
for their operating system. For more details see
<xref linkend="configuration-file-service-commands"/>.
</para>
</note>
</listitem>
</varlistentry>
<varlistentry id="repmgr-conf-replication-user" xreflabel="replication_user">
<term><varname>replication_user</varname> (<type>string</type>)
<indexterm>
<primary><varname>replication_user</varname> configuration file parameter</primary>
</indexterm>
</term>
<listitem>
<para>
PostgreSQL user to make replication connections with.
If not set defaults, to the user defined in <xref linkend="repmgr-conf-conninfo"/>.
</para>
</listitem>
</varlistentry>
<varlistentry id="repmgr-conf-replication-type" xreflabel="replication_type">
<term><varname>replication_type</varname> (<type>string</type>)
<indexterm>
<primary><varname>replication_type</varname> configuration file parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Must be one of <literal>physical</literal> (for standard streaming replication)
or <literal>bdr</literal>.
</para>
<note>
<para>
Replication type <literal>bdr</literal> can only be used with BDR 2.x
</para>
<para>
BDR 3.x users should use <literal>physical</literal>.
</para>
</note>
</listitem>
</varlistentry>
<varlistentry id="repmgr-conf-location" xreflabel="location">
<term><varname>location</varname> (<type>string</type>)
<indexterm>
<primary><varname>location</varname> configuration file parameter</primary>
</indexterm>
</term>
<listitem>
<para>
An arbitrary string defining the location of the node; this
is used during failover to check visibility of the
current primary node.
</para>
<para>
For more details see <xref linkend="repmgrd-network-split"/>.
</para>
</listitem>
</varlistentry>
<varlistentry id="repmgr-conf-use-replication-slots" xreflabel="use_replication_slots">
<term><varname>use_replication_slots</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>use_replication_slots</varname> configuration file parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Whether to use physical replication slots.
</para>
<note>
<para>
When using replication slots,
<varname>max_replication_slots</varname> should be configured for
at least the number of standbys which will connect
to the primary.
</para>
</note>
</listitem>
</varlistentry>
</variablelist>
</sect1>

View File

@@ -96,33 +96,6 @@
</variablelist>
</para>
<para>
For a full list of annotated configuration items, see the file
<ulink url="https://raw.githubusercontent.com/2ndQuadrant/repmgr/master/repmgr.conf.sample">repmgr.conf.sample</ulink>.
</para>
<para>
For &repmgrd;-specific settings, see <xref linkend="repmgrd-configuration"/>.
</para>
<note>
<para>
The following parameters in the configuration file can be overridden with
command line options:
<itemizedlist>
<listitem>
<simpara>
<literal>-L/--log-level</literal> overrides <literal>log_level</literal> in
<filename>repmgr.conf</filename>
</simpara>
</listitem>
<listitem>
<simpara>
<literal>-b/--pg_bindir</literal> overrides <literal>pg_bindir</literal> in
<filename>repmgr.conf</filename>
</simpara>
</listitem>
</itemizedlist>
</para>
</note>
</sect1>

View File

@@ -31,6 +31,8 @@
<secondary>format</secondary>
</indexterm>
<para>
<filename>repmgr.conf</filename> is a plain text file with one parameter/value
combination per line.
@@ -39,14 +41,10 @@
Whitespace is insignificant (except within a quoted parameter value) and blank lines are ignored.
Hash marks (<literal>#</literal>) designate the remainder of the line as a comment.
Parameter values that are not simple identifiers or numbers should be single-quoted.
Note that single quote cannot be embedded in a parameter value.
</para>
<important>
<para>
&repmgr; will interpret double-quotes as being part of a string value; only use single quotes
to quote parameter values.
</para>
</important>
<para>
To embed a single quote in a parameter value, write either two quotes (preferred) or backslash-quote.
</para>
<para>
Example of a valid <filename>repmgr.conf</filename> file:
@@ -56,12 +54,99 @@
node_id=1
node_name= node1
conninfo ='host=node1 dbname=repmgr user=repmgr connect_timeout=2'
data_directory = /var/lib/pgsql/11/data</programlisting>
data_directory = '/var/lib/pgsql/12/data'</programlisting>
</para>
<note>
<para>
Beginning with <link linkend="release-5.0">repmgr 5.0</link>, configuration
file parsing has been tightened up and now matches the way PostgreSQL
itself parses configuration files.
</para>
<para>
This means <filename>repmgr.conf</filename> files used with earlier &repmgr;
versions may need slight modification before they can be used with &repmgr; 5
and later.
</para>
<para>
The main change is that &repmgr; requires most string values to be
enclosed in single quotes. For example, this was previously valid:
<programlisting>
conninfo=host=node1 user=repmgr dbname=repmgr connect_timeout=2</programlisting>
but must now be changed to:
<programlisting>
conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlisting>
</para>
</note>
</sect2>
<sect2 id="configuration-file-items" xreflabel="configuration file items">
<title>Configuration file items</title>
<para>
The following sections document some sections of the configuration file:
<itemizedlist>
<listitem>
<simpara>
<xref linkend="configuration-file-settings"/>
</simpara>
</listitem>
<listitem>
<simpara>
<xref linkend="configuration-file-optional-settings"/>
</simpara>
</listitem>
<listitem>
<simpara>
<xref linkend="configuration-file-log-settings"/>
</simpara>
</listitem>
<listitem>
<simpara>
<xref linkend="configuration-file-service-commands"/>
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
For a full list of annotated configuration items, see the file
<ulink url="https://raw.githubusercontent.com/2ndQuadrant/repmgr/master/repmgr.conf.sample">repmgr.conf.sample</ulink>.
</para>
<para>
For &repmgrd;-specific settings, see <xref linkend="repmgrd-configuration"/>.
</para>
<note>
<para>
The following parameters in the configuration file can be overridden with
command line options:
<itemizedlist>
<listitem>
<simpara>
<literal>-L/--log-level</literal> overrides <literal>log_level</literal> in
<filename>repmgr.conf</filename>
</simpara>
</listitem>
<listitem>
<simpara>
<literal>-b/--pg_bindir</literal> overrides <literal>pg_bindir</literal> in
<filename>repmgr.conf</filename>
</simpara>
</listitem>
</itemizedlist>
</para>
</note>
</sect2>
<sect2 id="configuration-file-location" xreflabel="configuration file location">
<title>Configuration file location</title>

View File

@@ -148,9 +148,19 @@
instances in the replication cluster which may potentially become a primary server or
(in cascading replication) the upstream server of a standby.
</para>
<para>
<para>
PostgreSQL documentation: <ulink url="https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-MAX-WAL-SENDERS">max_wal_senders</ulink>.
</para>
<note>
<para>
From <productname>PostgreSQL 12</productname>, <option>max_wal_senders</option>
<emphasis>must</emphasis> be set to the same or a higher value as the primary node
(at the time the node was cloned), otherwise the standby will refuse
to start (unless <option>hot_standby</option> is set to <literal>off</literal>, which
will prevent the node from accepting queries).
</para>
</note>
</listitem>
</varlistentry>
@@ -305,6 +315,7 @@
&configuration-file;
&configuration-file-required-settings;
&configuration-file-optional-settings;
&configuration-file-log-settings;
&configuration-file-service-commands;

View File

@@ -18,6 +18,7 @@
<!ENTITY configuration SYSTEM "configuration.xml">
<!ENTITY configuration-file SYSTEM "configuration-file.xml">
<!ENTITY configuration-file-required-settings SYSTEM "configuration-file-required-settings.xml">
<!ENTITY configuration-file-optional-settings SYSTEM "configuration-file-optional-settings.xml">
<!ENTITY configuration-file-log-settings SYSTEM "configuration-file-log-settings.xml">
<!ENTITY configuration-file-service-commands SYSTEM "configuration-file-service-commands.xml">
<!ENTITY cloning-standbys SYSTEM "cloning-standbys.xml">
@@ -53,11 +54,11 @@
<!ENTITY repmgr-cluster-crosscheck SYSTEM "repmgr-cluster-crosscheck.xml">
<!ENTITY repmgr-cluster-event SYSTEM "repmgr-cluster-event.xml">
<!ENTITY repmgr-cluster-cleanup SYSTEM "repmgr-cluster-cleanup.xml">
<!ENTITY repmgr-daemon-status SYSTEM "repmgr-daemon-status.xml">
<!ENTITY repmgr-service-status SYSTEM "repmgr-service-status.xml">
<!ENTITY repmgr-service-pause SYSTEM "repmgr-service-pause.xml">
<!ENTITY repmgr-service-unpause SYSTEM "repmgr-service-unpause.xml">
<!ENTITY repmgr-daemon-start SYSTEM "repmgr-daemon-start.xml">
<!ENTITY repmgr-daemon-stop SYSTEM "repmgr-daemon-stop.xml">
<!ENTITY repmgr-daemon-pause SYSTEM "repmgr-daemon-pause.xml">
<!ENTITY repmgr-daemon-unpause SYSTEM "repmgr-daemon-unpause.xml">
<!ENTITY appendix-release-notes SYSTEM "appendix-release-notes.xml">
<!ENTITY appendix-faq SYSTEM "appendix-faq.xml">

View File

@@ -42,6 +42,10 @@
customers, as the PostgreSQL filesystem layout may be different to the community RPMs.
Please contact your support vendor for assistance.
</para>
<para>
See also <link linkend="appendix-faq">FAQ</link> entry
<xref linkend="faq-third-party-packages"/>.
</para>
</note>
<para>
@@ -81,9 +85,9 @@
(this enables the 2ndQuadrant repository as a source of &repmgr; packages).
</para>
<para>
For example, for PostgreSQL 10 on CentOS, execute:
For example, for PostgreSQL 11 on CentOS, execute:
<programlisting>
curl https://dl.2ndquadrant.com/default/release/get/10/rpm | sudo bash</programlisting>
curl https://dl.2ndquadrant.com/default/release/get/11/rpm | sudo bash</programlisting>
</para>
<para>
@@ -99,8 +103,8 @@ curl https://dl.2ndquadrant.com/default/release/get/9.6/rpm | sudo bash</program
sudo yum repolist</programlisting>
The output should contain two entries like this:
<programlisting>
2ndquadrant-dl-default-release-pg10/7/x86_64 2ndQuadrant packages (PG10) for 7 - x86_64 4
2ndquadrant-dl-default-release-pg10-debug/7/x86_64 2ndQuadrant packages (PG10) for 7 - x86_64 - Debug 3</programlisting>
2ndquadrant-dl-default-release-pg11/7/x86_64 2ndQuadrant packages (PG11) for 7 - x86_64 18
2ndquadrant-dl-default-release-pg11-debug/7/x86_64 2ndQuadrant packages (PG11) for 7 - x86_64 - Debug 8</programlisting>
</para>
</listitem>
@@ -108,7 +112,7 @@ sudo yum repolist</programlisting>
<para>
Install the &repmgr; version appropriate for your PostgreSQL version (e.g. <literal>repmgr10</literal>):
<programlisting>
sudo yum install repmgr10</programlisting>
sudo yum install repmgr11</programlisting>
</para>
<note>
<para>
@@ -153,19 +157,23 @@ yum search repmgr</programlisting>
To install a specific package version, execute <command>yum --showduplicates list</command>
for the package in question:
<programlisting>
[root@localhost ~]# yum --showduplicates list repmgr10
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: ftp.iij.ad.jp
* extras: ftp.iij.ad.jp
* updates: ftp.iij.ad.jp
Available Packages
repmgr10.x86_64 4.0.3-1.rhel7 pgdg10
repmgr10.x86_64 4.0.4-1.rhel7 pgdg10
repmgr10.x86_64 4.0.5-1.el7 2ndquadrant-repo-10</programlisting>
[root@localhost ~]# yum --showduplicates list repmgr11
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: ftp.tsukuba.wide.ad.jp
* epel: nrt.edge.kernel.org
* extras: ftp.tsukuba.wide.ad.jp
* updates: ftp.tsukuba.wide.ad.jp
Installed Packages
repmgr11.x86_64 4.4.0-1.rhel7 @pgdg11
Available Packages
repmgr11.x86_64 4.2-1.el7 2ndquadrant-dl-default-release-pg11
repmgr11.x86_64 4.2-2.el7 2ndquadrant-dl-default-release-pg11
repmgr11.x86_64 4.3-1.el7 2ndquadrant-dl-default-release-pg11
repmgr11.x86_64 4.4-1.el7 2ndquadrant-dl-default-release-pg11</programlisting>
then append the appropriate version number to the package name with a hyphen, e.g.:
<programlisting>
[root@localhost ~]# yum install repmgr10-4.0.3-1.rhel7</programlisting>
[root@localhost ~]# yum install repmgr11-4.3-1.el7</programlisting>
</para>
<para>
@@ -220,13 +228,13 @@ yum search repmgr</programlisting>
<itemizedlist>
<listitem>
<para>
<listitem>
<para>
Install the repository definition for your distribution and PostgreSQL version
(this enables the 2ndQuadrant repository as a source of &repmgr; packages) by executing:
(this enables the 2ndQuadrant repository as a source of &repmgr; packages) by executing:
<programlisting>
curl https://dl.2ndquadrant.com/default/release/get/deb | sudo bash</programlisting>
</para>
curl https://dl.2ndquadrant.com/default/release/get/deb | sudo bash</programlisting>
</para>
<note>
<para>
This will automatically install the following additional packages, if not already present:
@@ -242,20 +250,20 @@ curl https://dl.2ndquadrant.com/default/release/get/deb | sudo bash</programlist
</note>
</listitem>
<listitem>
<para>
Install the &repmgr; version appropriate for your PostgreSQL version (e.g. <literal>repmgr10</literal>):
<listitem>
<para>
Install the &repmgr; version appropriate for your PostgreSQL version (e.g. <literal>repmgr11</literal>):
<programlisting>
sudo apt-get install postgresql-10-repmgr</programlisting>
</para>
sudo apt-get install postgresql-11-repmgr</programlisting>
</para>
<note>
<para>
For packages for PostgreSQL 9.6 and earlier, the package name includes
a period between major and minor version numbers, e.g.
<literal>postgresql-9.6-repmgr</literal>.
For packages for PostgreSQL 9.6 and earlier, the package name includes
a period between major and minor version numbers, e.g.
<literal>postgresql-9.6-repmgr</literal>.
</para>
</note>
</listitem>
</listitem>
</itemizedlist>

View File

@@ -14,7 +14,7 @@
</para>
<para>
&repmgr; 4.x is compatible with all PostgreSQL versions from 9.3. 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>
@@ -39,13 +39,13 @@
<note>
<simpara>
The same &quot;major&quot; &repmgr; version (e.g. <literal>4.2.x</literal>) <emphasis>must</emphasis>
The same &quot;major&quot; &repmgr; version (e.g. <literal>&repmgrversion;.x</literal>) <emphasis>must</emphasis>
be installed on all node in the replication cluster. We strongly recommend keeping all
nodes on the same (preferably latest) &quot;minor&quot; &repmgr; version to minimize the risk
of incompatibilities.
</simpara>
<simpara>
If different &quot;major&quot; &repmgr; versions (e.g. 3.3.x and 4.1.x)
If different &quot;major&quot; &repmgr; versions (e.g. 4.1.x and &repmgrversion;.x)
are installed on different nodes, in the best case &repmgr; (in particular &repmgrd;)
will not run. In the worst case, you will end up with a broken cluster.
</simpara>
@@ -109,12 +109,25 @@
</thead>
<tbody>
<row>
<entry>
&repmgr; 5.x
</entry>
<entry>
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
</entry>
<entry>
9.3, 9.4, 9.5, 9.6, 10, 11, 12
</entry>
</row>
<row>
<entry>
&repmgr; 4.x
</entry>
<entry>
<link linkend="release-4.2">4.2</link> (2018-10-24)
<link linkend="release-4.4">4.4</link> (2019-06-27)
</entry>
<entry>
9.3, 9.4, 9.5, 9.6, 10, 11
@@ -154,11 +167,26 @@
The &repmgr; 2.x and 3.x series are no longer maintained or supported.
We strongly recommend upgrading to the latest &repmgr; version.
</para>
<para>
Following the release of &repmgr; 5.0, there will be no further releases of
the &repmgr; 4.x series. Note that &repmgr; 5.x is an incremental development
of the 4.x series and &repmgr; 4.x users should upgrade to this as soon as possible.
</para>
</important>
</sect2>
<sect2 id="install-postgresql-93-94">
<title>PostgreSQL 9.3 and 9.4 support</title>
<indexterm>
<primary>PostgreSQL 9.3</primary>
<secondary>repmgr support</secondary>
</indexterm>
<para>
Note that some &repmgr; functionality is not available in PostgreSQL 9.3 and 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">
@@ -177,5 +205,22 @@
</listitem>
</itemizedlist>
<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>)
and will no longer be updated with security or bugfixes.
</para>
<para>
PostgreSQL 9.4. is scheduled for its final release in February 2020
(see <ulink url="https://www.postgresql.org/support/versioning/">PostgreSQL Versioning Policy</ulink>).
</para>
<para>
We recommend that users of these versions migrate to a recent PostgreSQL version
as soon as possible.
</para>
</important>
</sect2>
</sect1>

View File

@@ -61,6 +61,9 @@ deb-src http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main</programlisti
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara><literal>flex</literal></simpara>
</listitem>
<listitem>
<simpara><literal>libedit-dev</literal></simpara>
</listitem>
@@ -115,6 +118,9 @@ deb-src http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main</programlisti
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<simpara><literal>flex</literal></simpara>
</listitem>
<listitem>
<simpara><literal>libselinux-devel</literal></simpara>
</listitem>
@@ -177,7 +183,8 @@ deb-src http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main</programlisti
</para>
<para>
There are also tags for each &repmgr; release, e.g. <literal>v4.2.0</literal>.
There are also tags for each <ulink url="https://github.com/2ndQuadrant/repmgr/releases">&repmgr; release</ulink>, e.g.
<literal><ulink url="https://github.com/2ndQuadrant/repmgr/releases/tag/v4.4.0">v4.4.0</ulink></literal>.
</para>
<para>

View File

@@ -254,7 +254,7 @@
</para>
<programlisting>
node_id=1
node_name=node1
node_name='node1'
conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/data'
</programlisting>
@@ -265,7 +265,6 @@
server. See sections <xref linkend="configuration"/> and <xref linkend="configuration-file"/>
for further details about <filename>repmgr.conf</filename>.
</para>
<note>
<para>
&repmgr; only uses <option>pg_bindir</option> when it executes
@@ -368,7 +367,7 @@
</para>
<programlisting>
node_id=2
node_name=node2
node_name='node2'
conninfo='host=node2 user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/data'</programlisting>
<para>
@@ -478,6 +477,8 @@
latest_end_lsn | 0/7000538
latest_end_time | 2017-08-28 15:20:56.418735+09
slot_name |
sender_host | node1
sender_port | 5432
conninfo | user=repmgr dbname=replication host=node1 application_name=node2
</programlisting>
Note that the <varname>conninfo</varname> value is that generated in <filename>recovery.conf</filename>
@@ -497,11 +498,12 @@
<para>
Check the node is registered by executing <command>repmgr cluster show</command> on the standby:
<programlisting>
$ repmgr -f /etc/repmgr.conf cluster show
ID | Name | Role | Status | Upstream | Location | Connection string
----+-------+---------+-----------+----------+----------+--------------------------------------
1 | node1 | primary | * running | | default | host=node1 dbname=repmgr user=repmgr
2 | node2 | standby | running | node1 | default | host=node2 dbname=repmgr user=repmgr</programlisting>
$ repmgr -f /etc/repmgr.conf cluster show
ID | Name | Role | Status | Upstream | Location | Priority | Timeline | Connection string
----+-------+---------+-----------+----------+----------+----------+----------+--------------------------------------
1 | node1 | primary | * running | | default | 100 | 1 | host=node1 dbname=repmgr user=repmgr
2 | node2 | standby | running | node1 | default | 100 | 1 | host=node2 dbname=repmgr user=repmgr</programlisting>
</para>
<para>
Both nodes are now registered with &repmgr; and the records have been copied to the standby server.

View File

@@ -233,7 +233,7 @@
<refsect1>
<title>See also</title>
<para>
<xref linkend="repmgr-node-status"/>, <xref linkend="repmgr-node-check"/>, <xref linkend="repmgr-daemon-status"/>
<xref linkend="repmgr-node-status"/>, <xref linkend="repmgr-node-check"/>, <xref linkend="repmgr-service-status"/>
</para>
</refsect1>

View File

@@ -14,13 +14,13 @@
<refnamediv>
<refname>repmgr daemon start</refname>
<refpurpose>Start the &repmgrd; daemon</refpurpose>
<refpurpose>Start the &repmgrd; daemon on the local node</refpurpose>
</refnamediv>
<refsect1>
<title>Description</title>
<para>
This command starts the &repmgrd; daemon on the
This command starts the &repmgrd; service on the
local node.
</para>
<para>
@@ -197,7 +197,11 @@
<refsect1>
<title>See also</title>
<para>
<xref linkend="repmgr-daemon-stop"/>, <xref linkend="repmgr-daemon-status"/>, <xref linkend="repmgrd-daemon"/>
<xref linkend="repmgr-daemon-stop"/>,
<xref linkend="repmgrd-daemon"/>,
<xref linkend="repmgr-service-status"/>,
<xref linkend="repmgr-service-pause"/>,
<xref linkend="repmgr-service-unpause"/>
</para>
</refsect1>

View File

@@ -14,7 +14,7 @@
<refnamediv>
<refname>repmgr daemon stop</refname>
<refpurpose>Stop the &repmgrd; daemon</refpurpose>
<refpurpose>Stop the &repmgrd; daemon on the local node</refpurpose>
</refnamediv>
<refsect1>
@@ -194,7 +194,11 @@
<refsect1>
<title>See also</title>
<para>
<xref linkend="repmgr-daemon-start"/>, <xref linkend="repmgr-daemon-status"/>, <xref linkend="repmgrd-daemon"/>
<xref linkend="repmgr-daemon-start"/>,
<xref linkend="repmgrd-daemon"/>,
<xref linkend="repmgr-service-status"/>,
<xref linkend="repmgr-service-pause"/>,
<xref linkend="repmgr-service-unpause"/>
</para>
</refsect1>

View File

@@ -356,7 +356,7 @@
<command>repmgr node rejoin</command> attempts to determine whether it will succeed by
comparing the timelines and relative WAL positions of the local node (rejoin candidate) and primary
(rejoin target). This is particularly important if planning to use <application>pg_rewind</application>,
which currently (as of PostgreSQL 11) may appear to succeed (or indicate there is no action
which currently (as of PostgreSQL 12) may appear to succeed (or indicate there is no action
needed) but potentially allow an impossible action, such as trying to rejoin a standby to a
primary which is behind the standby. &repmgr; will prevent this situation from occurring.
</para>

View File

@@ -114,38 +114,38 @@
<para>
See what action would be taken for a restart:
<programlisting>
[postgres@node1 ~]$ repmgr -f /etc/repmgr/11/repmgr.conf node service --action=restart --checkpoint --dry-run
[postgres@node1 ~]$ repmgr -f /etc/repmgr/12/repmgr.conf node service --action=restart --checkpoint --dry-run
INFO: a CHECKPOINT would be issued here
INFO: would execute server command "sudo service postgresql-11 restart"</programlisting>
INFO: would execute server command "sudo service postgresql-12 restart"</programlisting>
</para>
<para>
Restart the PostgreSQL instance:
<programlisting>
[postgres@node1 ~]$ repmgr -f /etc/repmgr/11/repmgr.conf node service --action=restart --checkpoint
[postgres@node1 ~]$ repmgr -f /etc/repmgr/12/repmgr.conf node service --action=restart --checkpoint
NOTICE: issuing CHECKPOINT
DETAIL: executing server command "sudo service postgresql-11 restart"
Redirecting to /bin/systemctl restart postgresql-11.service</programlisting>
DETAIL: executing server command "sudo service postgresql-12 restart"
Redirecting to /bin/systemctl restart postgresql-12.service</programlisting>
</para>
<para>
List all commands:
<programlisting>
[postgres@node1 ~]$ repmgr -f /etc/repmgr/11/repmgr.conf node service --list-actions
[postgres@node1 ~]$ repmgr -f /etc/repmgr/12/repmgr.conf node service --list-actions
Following commands would be executed for each action:
start: "sudo service postgresql-11 start"
stop: "sudo service postgresql-11 stop"
restart: "sudo service postgresql-11 restart"
reload: "sudo service postgresql-11 reload"
promote: "/usr/pgsql-11/bin/pg_ctl -w -D '/var/lib/pgsql/11/data' promote"</programlisting>
start: "sudo service postgresql-12 start"
stop: "sudo service postgresql-12 stop"
restart: "sudo service postgresql-12 restart"
reload: "sudo service postgresql-12 reload"
promote: "/usr/pgsql-12/bin/pg_ctl -w -D '/var/lib/pgsql/12/data' promote"</programlisting>
</para>
<para>
List a single command:
<programlisting>
[postgres@node1 ~]$ repmgr -f /etc/repmgr/11/repmgr.conf node service --list-actions --action=promote
/usr/pgsql-11/bin/pg_ctl -w -D '/var/lib/pgsql/11/data' promote </programlisting>
[postgres@node1 ~]$ repmgr -f /etc/repmgr/12/repmgr.conf node service --list-actions --action=promote
/usr/pgsql-12/bin/pg_ctl -w -D '/var/lib/pgsql/12/data' promote </programlisting>
</para>
</refsect1>
</refentry>

View File

@@ -1,6 +1,6 @@
<refentry id="repmgr-daemon-pause">
<refentry id="repmgr-service-pause">
<indexterm>
<primary>repmgr daemon pause</primary>
<primary>repmgr service pause</primary>
</indexterm>
<indexterm>
@@ -9,11 +9,11 @@
</indexterm>
<refmeta>
<refentrytitle>repmgr daemon pause</refentrytitle>
<refentrytitle>repmgr service pause</refentrytitle>
</refmeta>
<refnamediv>
<refname>repmgr daemon pause</refname>
<refname>repmgr service pause</refname>
<refpurpose>Instruct all &repmgrd; instances in the replication cluster to pause failover operations</refpurpose>
</refnamediv>
@@ -32,20 +32,29 @@
<note>
<para>
It's important to wait a few seconds after restarting PostgreSQL on any node before running
<command>repmgr daemon pause</command>, as the &repmgrd; instance
<command>repmgr service pause</command>, as the &repmgrd; instance
on the restarted node will take a second or two before it has updated its status.
</para>
</note>
<para>
<xref linkend="repmgr-daemon-unpause"/> will instruct all previously paused &repmgrd;
<xref linkend="repmgr-service-unpause"/> will instruct all previously paused &repmgrd;
instances to resume normal failover operation.
</para>
</refsect1>
<refsect1>
<title>Prerequisites</title>
<para>
PostgreSQL must be accessible on all nodes (using the <varname>conninfo</varname> string shown by
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>)
from the node where <command>repmgr service pause</command> is executed.
</para>
</refsect1>
<refsect1>
<title>Execution</title>
<para>
<command>repmgr daemon pause</command> can be executed on any active node in the
<command>repmgr service pause</command> can be executed on any active node in the
replication cluster. A valid <filename>repmgr.conf</filename> file is required.
It will have no effect on previously paused nodes.
</para>
@@ -55,7 +64,7 @@
<title>Example</title>
<para>
<programlisting>
$ repmgr -f /etc/repmgr.conf daemon pause
$ repmgr -f /etc/repmgr.conf service pause
NOTICE: node 1 (node1) paused
NOTICE: node 2 (node2) paused
NOTICE: node 3 (node3) paused</programlisting>
@@ -79,7 +88,7 @@ NOTICE: node 3 (node3) paused</programlisting>
<refsect1>
<title>Exit codes</title>
<para>
One of the following exit codes will be emitted by <command>repmgr daemon unpause</command>:
One of the following exit codes will be emitted by <command>repmgr service unpause</command>:
</para>
<variablelist>
@@ -107,7 +116,11 @@ NOTICE: node 3 (node3) paused</programlisting>
<refsect1>
<title>See also</title>
<para>
<xref linkend="repmgr-daemon-unpause"/>, <xref linkend="repmgr-daemon-status"/>
<xref linkend="repmgr-service-unpause"/>,
<xref linkend="repmgr-service-status"/>,
<xref linkend="repmgrd-pausing"/>,
<xref linkend="repmgr-daemon-start"/>,
<xref linkend="repmgr-daemon-stop"/>
</para>
</refsect1>
</refentry>

View File

@@ -1,19 +1,19 @@
<refentry id="repmgr-daemon-status">
<refentry id="repmgr-service-status">
<indexterm>
<primary>repmgr daemon status</primary>
<primary>repmgr service status</primary>
</indexterm>
<indexterm>
<primary>repmgrd</primary>
<secondary>displaying daemon status</secondary>
<secondary>displaying service status</secondary>
</indexterm>
<refmeta>
<refentrytitle>repmgr daemon status</refentrytitle>
<refentrytitle>repmgr service status</refentrytitle>
</refmeta>
<refnamediv>
<refname>repmgr daemon status</refname>
<refname>repmgr service status</refname>
<refpurpose>display information about the status of &repmgrd; on each node in the cluster</refpurpose>
</refnamediv>
@@ -22,21 +22,33 @@
<para>
This command provides an overview over all active nodes in the cluster and the state
of each node's &repmgrd; instance. It can be used to check
the result of <xref linkend="repmgr-daemon-pause"/> and <xref linkend="repmgr-daemon-unpause"/>
the result of <xref linkend="repmgr-service-pause"/> and <xref linkend="repmgr-service-unpause"/>
operations.
</para>
</refsect1>
<refsect1>
<title>Prerequisites</title>
<para>
PostgreSQL should be accessible on all nodes (using the <varname>conninfo</varname> string shown by
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>)
from the node where <command>repmgr service status</command> is executed.
</para>
</refsect1>
<refsect1>
<title>Execution</title>
<para>
<command>repmgr daemon status</command> can be executed on any active node in the
<command>repmgr service status</command> can be executed on any active node in the
replication cluster. A valid <filename>repmgr.conf</filename> file is required.
</para>
<para>
If PostgreSQL is not running on a node, &repmgr; will not be able to determine the
status of that node's &repmgrd; instance.
If a node is not accessible, or PostgreSQL itself is not running on the node,
&repmgr; will not be able to determine the status of that node's &repmgrd; instance,
and &quot;<literal>n/a</literal>&quot; will be displayed in the node's <literal>repmgrd</literal>
column.
</para>
<note>
<para>
After restarting PostgreSQL on any node, the &repmgrd; instance
@@ -51,7 +63,7 @@
<title>Examples</title>
<para>
&repmgrd; running normally on all nodes:
<programlisting>$ repmgr -f /etc/repmgr.conf daemon status
<programlisting>$ repmgr -f /etc/repmgr.conf service status
ID | Name | Role | Status | Upstream | repmgrd | PID | Paused? | Upstream last seen
----+-------+---------+-----------+----------+---------+-------+---------+--------------------
1 | node1 | primary | * running | | running | 96563 | no | n/a
@@ -60,8 +72,8 @@
</para>
<para>
&repmgrd; paused on all nodes (using <xref linkend="repmgr-daemon-pause"/>):
<programlisting>$ repmgr -f /etc/repmgr.conf daemon status
&repmgrd; paused on all nodes (using <xref linkend="repmgr-service-pause"/>):
<programlisting>$ repmgr -f /etc/repmgr.conf service status
ID | Name | Role | Status | Upstream | repmgrd | PID | Paused? | Upstream last seen
----+-------+---------+-----------+----------+---------+-------+---------+--------------------
1 | node1 | primary | * running | | running | 96563 | yes | n/a
@@ -71,7 +83,7 @@
<para>
&repmgrd; not running on one node:
<programlisting>$ repmgr -f /etc/repmgr.conf daemon status
<programlisting>$ repmgr -f /etc/repmgr.conf service status
ID | Name | Role | Status | Upstream | repmgrd | PID | Paused? | Upstream last seen
----+-------+---------+-----------+----------+-------------+-------+---------+--------------------
1 | node1 | primary | * running | | running | 96563 | yes | n/a
@@ -89,11 +101,11 @@
<term><option>--csv</option></term>
<listitem>
<para>
<command>repmgr daemon status</command> accepts an optional parameter <literal>--csv</literal>, which
<command>repmgr service status</command> accepts an optional parameter <literal>--csv</literal>, which
outputs the replication cluster's status in a simple CSV format, suitable for
parsing by scripts, e.g.:
<programlisting>
$ repmgr -f /etc/repmgr.conf daemon status --csv
$ repmgr -f /etc/repmgr.conf service status --csv
1,node1,primary,1,1,5722,1,100,-1,default
2,node2,standby,1,0,-1,1,100,1,default
3,node3,standby,1,1,5779,1,100,1,default</programlisting>
@@ -171,7 +183,7 @@
<listitem>
<para>
Display additional information (<literal>location</literal>, <literal>priority</literal>)
about the &repmgr; configuration.
about the &repmgr; configuration.
</para>
</listitem>
</varlistentry>
@@ -180,7 +192,7 @@
<term><option>--verbose</option></term>
<listitem>
<para>
Display the full text of any database connection error messages
Display the full text of any database connection error messages.
</para>
</listitem>
</varlistentry>
@@ -192,7 +204,12 @@
<refsect1>
<title>See also</title>
<para>
<xref linkend="repmgr-daemon-pause"/>, <xref linkend="repmgr-daemon-unpause"/>, <xref linkend="repmgr-cluster-show"/>
<xref linkend="repmgr-service-pause"/>,
<xref linkend="repmgr-service-unpause"/>,
<xref linkend="repmgr-cluster-show"/>,
<xref linkend="repmgrd-pausing"/>,
<xref linkend="repmgr-daemon-start"/>,
<xref linkend="repmgr-daemon-stop"/>
</para>
</refsect1>
</refentry>

View File

@@ -1,6 +1,6 @@
<refentry id="repmgr-daemon-unpause">
<refentry id="repmgr-service-unpause">
<indexterm>
<primary>repmgr daemon unpause</primary>
<primary>repmgr service unpause</primary>
</indexterm>
<indexterm>
@@ -8,13 +8,12 @@
<secondary>unpausing</secondary>
</indexterm>
<refmeta>
<refentrytitle>repmgr daemon unpause</refentrytitle>
<refentrytitle>repmgr service unpause</refentrytitle>
</refmeta>
<refnamediv>
<refname>repmgr daemon unpause</refname>
<refname>repmgr service unpause</refname>
<refpurpose>Instruct all &repmgrd; instances in the replication cluster to resume failover operations</refpurpose>
</refnamediv>
@@ -23,24 +22,33 @@
<para>
This command can be run on any active node in the replication cluster to instruct all
running &repmgrd; instances to &quot;unpause&quot;
(following a previous execution of <xref linkend="repmgr-daemon-pause"/>)
(following a previous execution of <xref linkend="repmgr-service-pause"/>)
and resume normal failover/monitoring operation.
</para>
<note>
<para>
It's important to wait a few seconds after restarting PostgreSQL on any node before running
<command>repmgr daemon pause</command>, as the &repmgrd; instance
<command>repmgr service pause</command>, as the &repmgrd; instance
on the restarted node will take a second or two before it has updated its status.
</para>
</note>
</refsect1>
<refsect1>
<title>Prerequisites</title>
<para>
PostgreSQL must be accessible on all nodes (using the <varname>conninfo</varname> string shown by
<link linkend="repmgr-cluster-show"><command>repmgr cluster show</command></link>)
from the node where <command>repmgr service pause</command> is executed.
</para>
</refsect1>
<refsect1>
<title>Execution</title>
<para>
<command>repmgr daemon unpause</command> can be executed on any active node in the
<command>repmgr service unpause</command> can be executed on any active node in the
replication cluster. A valid <filename>repmgr.conf</filename> file is required.
It will have no effect on nodes which are not already paused.
</para>
@@ -50,7 +58,7 @@
<title>Example</title>
<para>
<programlisting>
$ repmgr -f /etc/repmgr.conf daemon unpause
$ repmgr -f /etc/repmgr.conf service unpause
NOTICE: node 1 (node1) unpaused
NOTICE: node 2 (node2) unpaused
NOTICE: node 3 (node3) unpaused</programlisting>
@@ -74,7 +82,7 @@ NOTICE: node 3 (node3) unpaused</programlisting>
<refsect1>
<title>Exit codes</title>
<para>
One of the following exit codes will be emitted by <command>repmgr daemon unpause</command>:
One of the following exit codes will be emitted by <command>repmgr service unpause</command>:
</para>
<variablelist>
@@ -102,7 +110,11 @@ NOTICE: node 3 (node3) unpaused</programlisting>
<refsect1>
<title>See also</title>
<para>
<xref linkend="repmgr-daemon-pause"/>, <xref linkend="repmgr-daemon-status"/>
<xref linkend="repmgr-service-pause"/>,
<xref linkend="repmgr-service-status"/>,
<xref linkend="repmgrd-pausing"/>,
<xref linkend="repmgr-daemon-start"/>,
<xref linkend="repmgr-daemon-stop"/>
</para>
</refsect1>
</refentry>

View File

@@ -18,7 +18,7 @@
<para>
<command>repmgr standby clone</command> clones a PostgreSQL node from another
PostgreSQL node, typically the primary, but optionally from any other node in
the cluster or from Barman. It creates the <filename>recovery.conf</filename> file required
the cluster or from Barman. It creates the replication configuration required
to attach the cloned node to the primary node (or another standby, if cascading replication
is in use).
</para>
@@ -85,15 +85,20 @@
</refsect1>
<refsect1 id="repmgr-standby-clone-recovery-conf">
<title>Customising recovery.conf</title>
<title>Customising replication configuration</title>
<indexterm>
<primary>recovery.conf</primary>
<secondary>customising with &quot;repmgr standby clone&quot;</secondary>
</indexterm>
<indexterm>
<primary>replication configuration</primary>
<secondary>customising with &quot;repmgr standby clone&quot;</secondary>
</indexterm>
<para>
By default, &repmgr; will create a minimal <filename>recovery.conf</filename>
By default, &repmgr; will create a minimal replication configuration
containing following parameters:
</para>
@@ -119,7 +124,7 @@
<para>
The following additional parameters can be specified in <filename>repmgr.conf</filename>
for inclusion in <filename>recovery.conf</filename>:
for inclusion in the replication configuration:
</para>
<itemizedlist spacing="compact" mark="bullet">
@@ -193,37 +198,50 @@
<secondary>generating for a standby cloned by another method</secondary>
</indexterm>
<indexterm>
<primary>replication configuration</primary>
<secondary>generating for a standby cloned by another method</secondary>
</indexterm>
<para>
&repmgr; supports standbys cloned by another method (e.g. using <application>barman</application>'s
<command><ulink url="http://docs.pgbarman.org/release/2.5/#recover">barman recover</ulink></command> command).
<command><ulink url="https://docs.pgbarman.org/#recover">barman recover</ulink></command> command).
</para>
<para>
To integrate the standby as a &repmgr; node, once the standby has been cloned,
ensure the <filename>repmgr.conf</filename>
file is created for the node, and that it has been registered using
<command><link linkend="repmgr-standby-register">repmgr standby register</link></command>.
</para>
<tip>
<para>
To register a standby which is not running, execute
<link linkend="repmgr-standby-register">repmgr standby register --force</link>
and provide the connection details for the primary.
</para>
<para>
See <xref linkend="repmgr-standby-register-inactive-node"/> for more details.
</para>
</tip>
<para>
Then execute the command <command>repmgr standby clone --recovery-conf-only</command>.
This will create the <filename>recovery.conf</filename> file needed to attach
the node to its upstream, and will also create a replication slot on the
the node to its upstream (in PostgreSQL 12 and later: append replication configuration
to <filename>postgresql.auto.conf</filename>), and will also create a replication slot on the
upstream node if required.
</para>
<para>
Note that the upstream node must be running. An existing
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>
<para>
Execute <command>repmgr standby clone --recovery-conf-only --dry-run</command>
to check the prerequisites for creating the <filename>recovery.conf</filename> file,
and display the contents of the file without actually creating it.
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>
<note>
<para>
<option>--recovery-conf-only</option> was introduced in &repmgr; <link linkend="release-4.0.4">4.0.4</link>.
</para>
</note>
</refsect1>
<refsect1>
@@ -249,8 +267,8 @@
</para>
<para>
If <option>--recovery-conf-only</option> specified, the contents of
the generated <filename>recovery.conf</filename> file will be displayed
but the file itself not written.
the generated recovery configuration will be displayed
but not written.
</para>
</listitem>
</varlistentry>
@@ -297,8 +315,16 @@
<term><option> --recovery-conf-only</option></term>
<listitem>
<para>
Create <filename>recovery.conf</filename> file for a previously cloned instance. &repmgr; 4.0.4 and later.
Create recovery configuration for a previously cloned instance.
</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>
@@ -326,9 +352,13 @@
<term><option>--upstream-conninfo</option></term>
<listitem>
<para>
<literal>primary_conninfo</literal> value to write in recovery.conf
<literal>primary_conninfo</literal> value to include in the recovery configuration
when the intended upstream server does not yet exist.
</para>
<para>
Note that &repmgr; may modify the provided value, in particular to set the
correct <literal>application_name</literal>.
</para>
</listitem>
</varlistentry>

View File

@@ -20,49 +20,54 @@
(&quot;follow target&quot;). Typically this will be the primary, but this
command can also be used to attach the standby to another standby.
</para>
<para>
This command requires a valid
<filename>repmgr.conf</filename> file for the standby, either specified
explicitly with <literal>-f/--config-file</literal> or located in a
This command requires a valid <filename>repmgr.conf</filename> file for the standby,
either specified explicitly with <literal>-f/--config-file</literal> or located in a
default location; no additional arguments are required.
</para>
<para>
By default &repmgr; will attempt to attach the standby to the current primary.
If <option>--upstream-node-id</option> is provided, &repmgr; will attempt
to attach the standby to the specified node, which can be another standby.
</para>
<para>
This command will force a restart of the standby server, which must be
running.
<para>The standby node (&quot;follow candidate&quot;) <emphasis>must</emphasis>
be running. If the new upstream (&quot;follow target&quot;) is not the primary,
the cluster primary <emphasis>must</emphasis> be running and accessible from the
standby node.
</para>
<tip>
<tip>
<para>
To re-add an inactive node to the replication cluster, use
<xref linkend="repmgr-node-rejoin"/>.
To re-add an inactive node to the replication cluster, use
<xref linkend="repmgr-node-rejoin"/>.
</para>
</tip>
</tip>
<para>
<command>repmgr standby follow</command> will wait up to
<varname>standby_follow_timeout</varname> seconds (default: <literal>30</literal>)
to verify the standby has actually connected to the new upstream node.
</para>
<para>
By default &repmgr; will attempt to attach the standby to the current primary.
If <option>--upstream-node-id</option> is provided, &repmgr; will attempt
to attach the standby to the specified node, which can be another standby.
</para>
<note>
<para>
If <option>recovery_min_apply_delay</option> is set for the standby, it
will not attach to the new upstream node until it has replayed available
WAL.
</para>
<para>
Conversely, if the standby is attached to an upstream standby
which has <option>recovery_min_apply_delay</option> set, the upstream
standby's replay state may actually be behind that of its new downstream node.
</para>
</note>
<para>
This command will force a restart of PostgreSQL on the standby node.
</para>
<para>
<command>repmgr standby follow</command> will wait up to
<varname>standby_follow_timeout</varname> seconds (default: <literal>30</literal>)
to verify the standby has actually connected to the new upstream node.
</para>
<note>
<para>
If <option>recovery_min_apply_delay</option> is set for the standby, it
will not attach to the new upstream node until it has replayed available
WAL.
</para>
<para>
Conversely, if the standby is attached to an upstream standby
which has <option>recovery_min_apply_delay</option> set, the upstream
standby's replay state may actually be behind that of its new downstream node.
</para>
</note>
</refsect1>
@@ -124,7 +129,7 @@
<para>
Note that when using &repmgrd;, <option>--upstream-node-id</option>
should always be configured;
see <link linkend="repmgrd-automatic-failover-configuration">Automatic failover configuration</link>
see <link linkend="repmgrd-automatic-failover-configuration">Automatic failover configuration</link>
for details.
</para>
</listitem>
@@ -166,7 +171,7 @@
be possible to follow the new upstream node, and &repmgr; will emit an error
message like this:
<programlisting>
ERROR: this node cannot attach to follow target node 3
ERROR: this node cannot attach to follow target node &quot;node3&quot; (ID 3)
DETAIL: follow target server's timeline 2 forked off current database system timeline 1 before current recovery point 0/6108880</programlisting>
</para>
<para>

View File

@@ -23,7 +23,8 @@
<important>
<para>
If &repmgrd; is active, you must execute
<command><link linkend="repmgr-daemon-pause">repmgr daemon pause</link></command>
<command><link linkend="repmgr-service-pause">repmgr service pause</link></command>
(&repmgr; 4.2 - 4.4: <command><link linkend="repmgr-service-pause">repmgr service pause</link></command>)
to temporarily disable &repmgrd; while making any changes
to the replication cluster.
</para>

View File

@@ -75,10 +75,22 @@
<para>
Under some circumstances you may wish to register a standby which is not
yet running; this can be the case when using provisioning tools to create
a complex replication cluster. In this case, by using the <option>-F/--force</option>
option and providing the connection parameters to the primary server,
the standby can be registered.
a complex replication cluster, or if the node was not cloned by &repmgr;.
</para>
<para>
In this case, by using the <option>-F/--force</option>
option and providing the connection parameters to the primary server,
the standby can be registered even if it has not yet been started.
</para>
<tip>
<para>
Connection parameters can either be provided either as a <literal>conninfo</literal> string
(e.g. <option>-d 'host=node1 user=repmgr'</option> or as individual connection parameters
(<option>-h/--host</option>, <option>-d/--dbname</option>,
<option>-U/--user</option>, <option>-p/--port</option> etc.).
</para>
</tip>
<para>
Similarly, with cascading replication it may be necessary to register
a standby whose upstream node has not yet been registered - in this case,
@@ -96,9 +108,11 @@
<title>Registering a node not cloned by repmgr</title>
<para>
If you've cloned a standby using another method (e.g. <application>barman</application>'s
<command>barman recover</command> command), first execute
<command><ulink url="https://docs.pgbarman.org/#recover">barman recover</ulink></command>
command), register the node as detailed in section
<xref linkend="repmgr-standby-register-inactive-node"/> then execute
<link linkend="repmgr-standby-create-recovery-conf">repmgr standby clone --recovery-conf-only</link>
to add the <filename>recovery.conf</filename> file, then register the standby as usual.
to generate the appropriate replication configuration.
</para>
</refsect1>
@@ -119,7 +133,7 @@
<varlistentry>
<term><option>-F</option><option>--force</option></term>
<term><option>-F</option>/<option>--force</option></term>
<listitem>
<para>
Overwrite an existing node record

View File

@@ -26,7 +26,7 @@
<abstract>
<para>
This is the official documentation of &repmgr; &repmgrversion; for
use with PostgreSQL 9.3 - PostgreSQL 11.
use with PostgreSQL 9.3 - PostgreSQL 12.
</para>
<para>
&repmgr; is being continually developed and we strongly recommend using the
@@ -116,11 +116,11 @@
&repmgr-cluster-crosscheck;
&repmgr-cluster-event;
&repmgr-cluster-cleanup;
&repmgr-daemon-status;
&repmgr-service-status;
&repmgr-service-pause;
&repmgr-service-unpause;
&repmgr-daemon-start;
&repmgr-daemon-stop;
&repmgr-daemon-pause;
&repmgr-daemon-unpause;
</part>
&appendix-release-notes;

View File

@@ -55,7 +55,7 @@
</para>
</note>
<para>
For more complex replication scenarios,e.g. with multiple datacentres, it may
For more complex replication scenarios, e.g. with multiple datacentres, it may
be preferable to use location-based failover, which ensures that only nodes
in the same location as the primary will ever be promotion candidates;
see <xref linkend="repmgrd-network-split"/> for more details.
@@ -199,9 +199,10 @@
<para>
The time the primary was last seen by each node can be checked by executing
<link linkend="repmgr-daemon-status"><command>repmgr daemon status</command></link>,
<link linkend="repmgr-service-status"><command>repmgr service status</command></link>
(&repmgr; 4.2 - 4.4: <link linkend="repmgr-service-status"><command>repmgr daemon status</command></link>)
which includes this in its output, e.g.:
<programlisting>$ repmgr -f /etc/repmgr.conf daemon status
<programlisting>$ repmgr -f /etc/repmgr.conf service status
ID | Name | Role | Status | Upstream | repmgrd | PID | Paused? | Upstream last seen
----+-------+---------+-----------+----------+---------+-------+---------+--------------------
1 | node1 | primary | * running | | running | 96563 | no | n/a
@@ -267,11 +268,12 @@
<para>
If <option>standby_disconnect_on_failover</option> is set to <literal>true</literal> in
<filename>repmgr.conf</filename>, in a failover situation &repmgrd; will forcibly disconnect
the local node's WAL receiver before making a failover decision.
the local node's WAL receiver, and wait for the WAL receiver on all sibling nodes to be
disconnected, before making a failover decision.
</para>
<note>
<para>
<option>standby_disconnect_on_failover</option> is available from PostgreSQL 9.5 and later.
<option>standby_disconnect_on_failover</option> is available with PostgreSQL 9.5 and later.
Additionally this requires that the <literal>repmgr</literal> database user is a superuser.
</para>
</note>
@@ -290,6 +292,12 @@
plus however many seconds it takes to confirm the WAL receiver is disconnected before
&repmgrd; proceeds with the failover decision.
</para>
<para>
&repmgrd; will wait up to <option>sibling_nodes_disconnect_timeout</option> seconds (default:
<literal>30</literal>) to confirm that the WAL receiver on all sibling nodes hase been
disconnected before proceding with the failover operation. If the timeout is reached, the
failover operation will go ahead anyway.
</para>
<para>
Following the failover operation, no matter what the outcome, each node will reconnect its WAL receiver.
</para>

View File

@@ -8,7 +8,7 @@
</indexterm>
<para>
&repmgrd; is a daemon which runs on each PostgreSQL node,
&repmgrd; is a daemon process which runs on each PostgreSQL node,
monitoring the local node, and (unless it's the primary node) the upstream server
(the primary server or with cascading replication, another standby) which it's
connected to.
@@ -77,7 +77,7 @@
<listitem>
<simpara>
<literal>connection</literal> - determines server availability
by attempt ingto make a new connection to the upstream node
by attempting to make a new connection to the upstream node
</simpara>
</listitem>
<listitem>
@@ -222,6 +222,17 @@
Normally <option>promote_command</option> is set as &repmgr;'s
<command><link linkend="repmgr-standby-promote">repmgr standby promote</link></command> command.
</para>
<note>
<para>
When invoking <command>repmgr standby promote</command> (either directly via
the <option>promote_command</option>, or in a script called
via <option>promote_command</option>), <option>--siblings-follow</option>
<emphasis>must not</emphasis> be included as a
command line option for <command>repmgr standby promote</command>.
</para>
</note>
<para>
It is also possible to provide a shell script to e.g. perform user-defined tasks
before promoting the current node. In this case the script <emphasis>must</emphasis>
@@ -508,7 +519,8 @@
</indexterm>
<para>
If you are intending to use the <link linkend="repmgr-daemon-start"><command>repmgr daemon start</command></link>
and <link linkend="repmgr-daemon-stop"><command>repmgr daemon stop</command></link> commands, the following
and <link linkend="repmgr-daemon-stop"><command>repmgr daemon stop</command></link>
commands, the following
parameters <emphasis>must</emphasis> be set in <filename>repmgr.conf</filename>:
<itemizedlist spacing="compact" mark="bullet">
@@ -524,10 +536,10 @@
</para>
<para>
Example (for &repmgr; with PostgreSQL 11 on CentOS 7):
Example (for &repmgr; with PostgreSQL 12 on CentOS 7):
<programlisting>
repmgrd_service_start_command='sudo systemctl repmgr11 start'
repmgrd_service_stop_command='sudo systemctl repmgr11 stop'
repmgrd_service_start_command='sudo systemctl repmgr12 start'
repmgrd_service_stop_command='sudo systemctl repmgr12 stop'
</programlisting>
</para>
<para>
@@ -849,7 +861,7 @@ repmgrd_service_stop_command='sudo systemctl repmgr11 stop'
<para>
The commands <link linkend="repmgr-daemon-start"><command>repmgr daemon start</command></link> and
<link linkend="repmgr-daemon-stop"><command>repmgr daemon stop</command></link> can be used
as convenience wrappers to start and stop &repmgrd;.
as convenience wrappers to start and stop &repmgrd; on the local node.
</para>
<important>
<para>

View File

@@ -6,9 +6,9 @@
<secondary>operation</secondary>
</indexterm>
<sect1 id="repmgrd-pausing">
<sect1 id="repmgrd-pausing" xreflabel="pausing the repmgrd service">
<title>Pausing repmgrd</title>
<title>Pausing the repmgrd service</title>
<indexterm>
<primary>repmgrd</primary>
@@ -47,7 +47,7 @@
<note>
<para>
For major PostgreSQL upgrades, e.g. from PostgreSQL 10 to PostgreSQL 11,
For major PostgreSQL upgrades, e.g. from PostgreSQL 11 to PostgreSQL 12,
&repmgrd; should be shut down completely and only started up
once the &repmgr; packages for the new PostgreSQL major version have been installed.
</para>
@@ -88,17 +88,21 @@
<sect2 id="repmgrd-pausing-execution">
<title>Pausing/unpausing &repmgrd;</title>
<para>
To pause &repmgrd;, execute <link linkend="repmgr-daemon-pause"><command>repmgr daemon pause</command></link>, e.g.:
To pause &repmgrd;, execute <link linkend="repmgr-service-pause"><command>repmgr service pause</command></link>
(&repmgr; 4.2 - 4.4: <link linkend="repmgr-service-pause"><command>repmgr daemon pause</command></link>),
e.g.:
<programlisting>
$ repmgr -f /etc/repmgr.conf daemon pause
$ repmgr -f /etc/repmgr.conf service pause
NOTICE: node 1 (node1) paused
NOTICE: node 2 (node2) paused
NOTICE: node 3 (node3) paused</programlisting>
</para>
<para>
The state of &repmgrd; on each node can be checked with
<link linkend="repmgr-daemon-status"><command>repmgr daemon status</command></link>, e.g.:
<programlisting>$ repmgr -f /etc/repmgr.conf daemon status
<link linkend="repmgr-service-status"><command>repmgr service status</command></link>
(&repmgr; 4.2 - 4.4: <link linkend="repmgr-service-status"><command>repmgr daemon status</command></link>),
e.g.:
<programlisting>$ repmgr -f /etc/repmgr.conf service status
ID | Name | Role | Status | repmgrd | PID | Paused?
----+-------+---------+---------+---------+------+---------
1 | node1 | primary | running | running | 7851 | yes
@@ -108,8 +112,8 @@ NOTICE: node 3 (node3) paused</programlisting>
<note>
<para>
If executing a switchover with <link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>,
&repmgr; will automatically pause/unpause &repmgrd; as part of the switchover process.
If executing a switchover with <link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>,
&repmgr; will automatically pause/unpause the &repmgrd; service as part of the switchover process.
</para>
</note>
@@ -117,29 +121,32 @@ NOTICE: node 3 (node3) paused</programlisting>
If the primary (in this example, <literal>node1</literal>) is stopped, &repmgrd;
running on one of the standbys (here: <literal>node2</literal>) will react like this:
<programlisting>
[2018-09-20 12:22:21] [WARNING] unable to connect to upstream node "node1" (ID: 1)
[2018-09-20 12:22:21] [INFO] checking state of node 1, 1 of 5 attempts
[2018-09-20 12:22:21] [INFO] sleeping 1 seconds until next reconnection attempt
[2019-08-28 12:22:21] [WARNING] unable to connect to upstream node "node1" (node ID: 1)
[2019-08-28 12:22:21] [INFO] checking state of node 1, 1 of 5 attempts
[2019-08-28 12:22:21] [INFO] sleeping 1 seconds until next reconnection attempt
...
[2018-09-20 12:22:24] [INFO] sleeping 1 seconds until next reconnection attempt
[2018-09-20 12:22:25] [INFO] checking state of node 1, 5 of 5 attempts
[2018-09-20 12:22:25] [WARNING] unable to reconnect to node 1 after 5 attempts
[2018-09-20 12:22:25] [NOTICE] node is paused
[2018-09-20 12:22:33] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in degraded state
[2018-09-20 12:22:33] [DETAIL] repmgrd paused by administrator
[2018-09-20 12:22:33] [HINT] execute "repmgr daemon unpause" to resume normal failover mode</programlisting>
[2019-08-28 12:22:24] [INFO] sleeping 1 seconds until next reconnection attempt
[2019-08-28 12:22:25] [INFO] checking state of node 1, 5 of 5 attempts
[2019-08-28 12:22:25] [WARNING] unable to reconnect to node 1 after 5 attempts
[2019-08-28 12:22:25] [NOTICE] node is paused
[2019-08-28 12:22:33] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (node ID: 1) in degraded state
[2019-08-28 12:22:33] [DETAIL] repmgrd paused by administrator
[2019-08-28 12:22:33] [HINT] execute "repmgr service unpause" to resume normal failover mode</programlisting>
</para>
<para>
If the primary becomes available again (e.g. following a software upgrade), &repmgrd;
will automatically reconnect, e.g.:
<programlisting>
[2018-09-20 13:12:41] [NOTICE] reconnected to upstream node 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>
To unpause &repmgrd;, execute <link linkend="repmgr-daemon-unpause"><command>repmgr daemon unpause</command></link>, e.g.:
To unpause the &repmgrd; service, execute
<link linkend="repmgr-service-unpause"><command>repmgr service unpause</command></link>
((&repmgr; 4.2 - 4.4: <link linkend="repmgr-service-unpause"><command>repmgr daemon unpause</command></link>),
e.g.:
<programlisting>
$ repmgr -f /etc/repmgr.conf daemon unpause
$ repmgr -f /etc/repmgr.conf service unpause
NOTICE: node 1 (node1) unpaused
NOTICE: node 2 (node2) unpaused
NOTICE: node 3 (node3) unpaused</programlisting>
@@ -150,11 +157,11 @@ NOTICE: node 3 (node3) unpaused</programlisting>
If the previous primary is no longer accessible when &repmgrd;
is unpaused, no failover action will be taken. Instead, a new primary must be manually promoted using
<link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>,
and any standbys attached to the new primary with
<link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link>.
and any standbys attached to the new primary with
<link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link>.
</para>
<para>
This is to prevent <link linkend="repmgr-daemon-unpause"><command>repmgr daemon unpause</command></link>
This is to prevent execution of <link linkend="repmgr-service-unpause"><command>repmgr service unpause</command></link>
resulting in the automatic promotion of a new primary, which may be a problem particularly
in larger clusters, where &repmgrd; could select a different promotion
candidate to the one intended by the administrator.
@@ -168,17 +175,23 @@ NOTICE: node 3 (node3) unpaused</programlisting>
The pause state of each node will be stored over a PostgreSQL restart.
</para>
<para>
<link linkend="repmgr-daemon-pause"><command>repmgr daemon pause</command></link> and
<link linkend="repmgr-daemon-unpause"><command>repmgr daemon unpause</command></link> can be
executed even if &repmgrd; is not running; in this case,
&repmgrd; will start up in whichever pause state has been set.
</para>
<para>
<link linkend="repmgr-service-pause"><command>repmgr service pause</command></link> and
<link linkend="repmgr-service-unpause"><command>repmgr service unpause</command></link> can be
executed even if &repmgrd; is not running; in this case,
&repmgrd; will start up in whichever pause state has been set.
</para>
<note>
<para>
<link linkend="repmgr-daemon-pause"><command>repmgr daemon pause</command></link> and
<link linkend="repmgr-daemon-unpause"><command>repmgr daemon unpause</command></link>
<emphasis>do not</emphasis> stop/start &repmgrd;.
<link linkend="repmgr-service-pause"><command>repmgr service pause</command></link> and
<link linkend="repmgr-service-unpause"><command>repmgr service unpause</command></link>
<emphasis>do not</emphasis> start/stop &repmgrd;.
</para>
<para>
The commands <link linkend="repmgr-daemon-start"><command>repmgr daemon start</command></link>
and <link linkend="repmgr-daemon-stop"><command>repmgr daemon stop</command></link>
(<link linkend="repmgrd-service-configuration">if correctly configured</link>) can be used to start/stop
&repmgrd; on individual nodes.
</para>
</note>
</sect2>

View File

@@ -38,7 +38,7 @@
<simpara>
ability to <link linkend="repmgrd-pausing">pause repmgrd</link>
operation on all nodes with a
<link linkend="repmgr-daemon-pause"><command>single command</command></link>
<link linkend="repmgr-service-pause"><command>single command</command></link>
</simpara>
</listitem>
@@ -100,11 +100,11 @@
Start &repmgrd; on each standby and verify that it's running by examining the
log output, which at log level <literal>INFO</literal> will look like this:
<programlisting>
[2019-03-15 06:32:05] [NOTICE] repmgrd (repmgrd 4.3) starting up
[2019-03-15 06:32:05] [INFO] connecting to database "host=node2 dbname=repmgr user=repmgr connect_timeout=2"
INFO: set_repmgrd_pid(): provided pidfile is /var/run/repmgr/repmgrd-11.pid
[2019-03-15 06:32:05] [NOTICE] starting monitoring of node "node2" (ID: 2)
[2019-03-15 06:32:05] [INFO] monitoring connection to upstream node "node1" (ID: 1)</programlisting>
[2019-08-15 07:14:42] [NOTICE] repmgrd (repmgrd 5.0) starting up
[2019-08-15 07:14:42] [INFO] connecting to database "host=node2 dbname=repmgr user=repmgr connect_timeout=2"
INFO: set_repmgrd_pid(): provided pidfile is /var/run/repmgr/repmgrd-12.pid
[2019-08-15 07:14:42] [NOTICE] starting monitoring of node "node2" (ID: 2)
[2019-08-15 07:14:42] [INFO] monitoring connection to upstream node "node1" (ID: 1)</programlisting>
</para>
<para>
Each &repmgrd; should also have recorded its successful startup as an event:
@@ -112,9 +112,9 @@
$ repmgr -f /etc/repmgr.conf cluster event --event=repmgrd_start
Node ID | Name | Event | OK | Timestamp | Details
---------+-------+---------------+----+---------------------+--------------------------------------------------------
3 | node3 | repmgrd_start | t | 2019-03-14 04:17:30 | monitoring connection to upstream node "node1" (ID: 1)
2 | node2 | repmgrd_start | t | 2019-03-14 04:11:47 | monitoring connection to upstream node "node1" (ID: 1)
1 | node1 | repmgrd_start | t | 2019-03-14 04:04:31 | monitoring cluster primary "node1" (ID: 1)</programlisting>
3 | node3 | repmgrd_start | t | 2019-08-15 07:14:42 | monitoring connection to upstream node "node1" (ID: 1)
2 | node2 | repmgrd_start | t | 2019-08-15 07:14:41 | monitoring connection to upstream node "node1" (ID: 1)
1 | node1 | repmgrd_start | t | 2019-08-15 07:14:39 | monitoring cluster primary "node1" (ID: 1)</programlisting>
</para>
<para>
Now stop the current primary server with e.g.:
@@ -128,33 +128,33 @@
decision is made. This is an extract from the log of a standby server (<literal>node2</literal>)
which has promoted to new primary after failure of the original primary (<literal>node1</literal>).
<programlisting>
[2019-03-15 06:37:50] [WARNING] unable to connect to upstream node "node1" (ID: 1)
[2019-03-15 06:37:50] [INFO] checking state of node 1, 1 of 3 attempts
[2019-03-15 06:37:50] [INFO] sleeping 5 seconds until next reconnection attempt
[2019-03-15 06:37:55] [INFO] checking state of node 1, 2 of 3 attempts
[2019-03-15 06:37:55] [INFO] sleeping 5 seconds until next reconnection attempt
[2019-03-15 06:38:00] [INFO] checking state of node 1, 3 of 3 attempts
[2019-03-15 06:38:00] [WARNING] unable to reconnect to node 1 after 3 attempts
[2019-03-15 06:38:00] [INFO] primary and this node have the same location ("default")
[2019-03-15 06:38:00] [INFO] local node's last receive lsn: 0/900CBF8
[2019-03-15 06:38:00] [INFO] node 3 last saw primary node 12 second(s) ago
[2019-03-15 06:38:00] [INFO] last receive LSN for sibling node "node3" (ID: 3) is: 0/900CBF8
[2019-03-15 06:38:00] [INFO] node "node3" (ID: 3) has same LSN as current candidate "node2" (ID: 2)
[2019-03-15 06:38:00] [INFO] visible nodes: 2; total nodes: 2; no nodes have seen the primary within the last 4 seconds
[2019-03-15 06:38:00] [NOTICE] promotion candidate is "node2" (ID: 2)
[2019-03-15 06:38:00] [NOTICE] this node is the winner, will now promote itself and inform other nodes
[2019-03-15 06:38:00] [INFO] promote_command is:
"/usr/pgsql-11/bin/repmgr -f /etc/repmgr/11/repmgr.conf standby promote"
[2019-08-15 07:27:50] [WARNING] unable to connect to upstream node "node1" (ID: 1)
[2019-08-15 07:27:50] [INFO] checking state of node 1, 1 of 3 attempts
[2019-08-15 07:27:50] [INFO] sleeping 5 seconds until next reconnection attempt
[2019-08-15 07:27:55] [INFO] checking state of node 1, 2 of 3 attempts
[2019-08-15 07:27:55] [INFO] sleeping 5 seconds until next reconnection attempt
[2019-08-15 07:28:00] [INFO] checking state of node 1, 3 of 3 attempts
[2019-08-15 07:28:00] [WARNING] unable to reconnect to node 1 after 3 attempts
[2019-08-15 07:28:00] [INFO] primary and this node have the same location ("default")
[2019-08-15 07:28:00] [INFO] local node's last receive lsn: 0/900CBF8
[2019-08-15 07:28:00] [INFO] node 3 last saw primary node 12 second(s) ago
[2019-08-15 07:28:00] [INFO] last receive LSN for sibling node "node3" (ID: 3) is: 0/900CBF8
[2019-08-15 07:28:00] [INFO] node "node3" (ID: 3) has same LSN as current candidate "node2" (ID: 2)
[2019-08-15 07:28:00] [INFO] visible nodes: 2; total nodes: 2; no nodes have seen the primary within the last 4 seconds
[2019-08-15 07:28:00] [NOTICE] promotion candidate is "node2" (ID: 2)
[2019-08-15 07:28:00] [NOTICE] this node is the winner, will now promote itself and inform other nodes
[2019-08-15 07:28:00] [INFO] promote_command is:
"/usr/pgsql-12/bin/repmgr -f /etc/repmgr/12/repmgr.conf standby promote"
NOTICE: promoting standby to primary
DETAIL: promoting server "node2" (ID: 2) using "/usr/pgsql-11/bin/pg_ctl -w -D '/var/lib/pgsql/11/data' promote"
DETAIL: promoting server "node2" (ID: 2) using "/usr/pgsql-12/bin/pg_ctl -w -D '/var/lib/pgsql/12/data' promote"
NOTICE: waiting up to 60 seconds (parameter "promote_check_timeout") for promotion to complete
NOTICE: STANDBY PROMOTE successful
DETAIL: server "node2" (ID: 2) was successfully promoted to primary
[2019-03-15 06:38:01] [INFO] 3 followers to notify
[2019-03-15 06:38:01] [NOTICE] notifying node "node3" (ID: 3) to follow node 2
[2019-08-15 07:28:01] [INFO] 3 followers to notify
[2019-08-15 07:28:01] [NOTICE] notifying node "node3" (ID: 3) to follow node 2
INFO: node 3 received notification to follow node 2
[2019-03-15 06:38:01] [INFO] switching to primary monitoring mode
[2019-03-15 06:38:01] [NOTICE] monitoring cluster primary "node2" (ID: 2)</programlisting>
[2019-08-15 07:28:01] [INFO] switching to primary monitoring mode
[2019-08-15 07:28:01] [NOTICE] monitoring cluster primary "node2" (ID: 2)</programlisting>
</para>
<para>
The cluster status will now look like this, with the original primary (<literal>node1</literal>)
@@ -176,11 +176,11 @@
$ repmgr -f /etc/repmgr.conf cluster event
Node ID | Name | Event | OK | Timestamp | Details
---------+-------+----------------------------+----+---------------------+-------------------------------------------------------------
3 | node3 | repmgrd_failover_follow | t | 2019-03-15 06:38:03 | node 3 now following new upstream node 2
3 | node3 | standby_follow | t | 2019-03-15 06:38:02 | standby attached to upstream node "node2" (ID: 2)
2 | node2 | repmgrd_reload | t | 2019-03-15 06:38:01 | monitoring cluster primary "node2" (ID: 2)
2 | node2 | repmgrd_failover_promote | t | 2019-03-15 06:38:01 | node 2 promoted to primary; old primary 1 marked as failed
2 | node2 | standby_promote | t | 2019-03-15 06:38:01 | server "node2" (ID: 2) was successfully promoted to primary</programlisting>
3 | node3 | repmgrd_failover_follow | t | 2019-08-15 07:38:03 | node 3 now following new upstream node 2
3 | node3 | standby_follow | t | 2019-08-15 07:38:02 | standby attached to upstream node "node2" (ID: 2)
2 | node2 | repmgrd_reload | t | 2019-08-15 07:38:01 | monitoring cluster primary "node2" (ID: 2)
2 | node2 | repmgrd_failover_promote | t | 2019-08-15 07:38:01 | node 2 promoted to primary; old primary 1 marked as failed
2 | node2 | standby_promote | t | 2019-08-15 07:38:01 | server "node2" (ID: 2) was successfully promoted to primary</programlisting>
</para>
</sect1>

View File

@@ -317,7 +317,9 @@
</para>
<para>
If &repmgrd; is in use, it's worth double-checking that
all nodes are unpaused by executing <command><link linkend="repmgr-daemon-status">repmgr-daemon-status</link></command>.
all nodes are unpaused by executing
<command><link linkend="repmgr-service-status">repmgr service status</link></command>
(&repmgr; 4.2 - 4.4: <command><link linkend="repmgr-service-status">repmgr daemon status</link></command>).
</para>
<note>

View File

@@ -216,7 +216,9 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<secondary>checking repmgrd status</secondary>
</indexterm>
<para>
From <link linkend="release-4.2">repmgr 4.2</link>, once the upgrade is complete, execute the <command><link linkend="repmgr-daemon-status">repmgr daemon status</link></command>
From <link linkend="release-4.2">repmgr 4.2</link>, once the upgrade is complete, execute the
<command><link linkend="repmgr-service-status">repmgr service status</link></command>
(&repmgr; 4.2 - 4.4: <command><link linkend="repmgr-service-status">repmgr daemon status</link></command>)
command (on any node) to show an overview of the status of &repmgrd; on all nodes.
</para>
</sect2>
@@ -263,7 +265,7 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<tip>
<para>
Use <command><link linkend="repmgr-node-check">repmgr node check</link></command>
to determine which replacation slots need to be recreated.
to determine which replication slots need to be recreated.
</para>
</tip>
@@ -417,13 +419,13 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
<programlisting>
$ ./convert-config.pl /etc/repmgr.conf
node_id=2
node_name=node2
conninfo=host=node2 dbname=repmgr user=repmgr connect_timeout=2
node_name='node2'
conninfo='host=node2 dbname=repmgr user=repmgr connect_timeout=2'
pg_ctl_options='-l /var/log/postgres/startup.log'
rsync_options=--exclude=postgresql.local.conf --archive
log_level=INFO
pg_basebackup_options=--no-slot
data_directory=</programlisting>
rsync_options='--exclude=postgresql.local.conf --archive'
log_level='INFO'
pg_basebackup_options='--no-slot'
data_directory=''</programlisting>
</para>
<para>
The converted file is printed to <literal>STDOUT</literal> and the original file is not
@@ -432,8 +434,7 @@ 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 in repmgr4 and which <emphasis>must</emphasis>
be provided.
is a required parameter from &repmgr; 4.
</para>
</sect3>
</sect2>

View File

@@ -78,8 +78,6 @@ CREATE VIEW repmgr.show_nodes AS
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
/* XXX update upgrade scripts! */
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);

View File

@@ -78,8 +78,6 @@ CREATE VIEW repmgr.show_nodes AS
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
/* XXX update upgrade scripts! */
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);

View File

@@ -78,8 +78,6 @@ CREATE VIEW repmgr.show_nodes AS
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
/* XXX update upgrade scripts! */
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);

View File

@@ -78,8 +78,6 @@ CREATE VIEW repmgr.show_nodes AS
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
/* XXX update upgrade scripts! */
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);

5
repmgr--4.4--5.0.sql Normal file
View File

@@ -0,0 +1,5 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
ALTER FUNCTION set_repmgrd_pid(INT, TEXT) RETURNS NULL ON NULL INPUT;

View File

@@ -78,8 +78,6 @@ CREATE VIEW repmgr.show_nodes AS
LEFT JOIN repmgr.nodes un
ON un.node_id = n.upstream_node_id;
/* XXX update upgrade scripts! */
CREATE TABLE repmgr.voting_term (
term INT NOT NULL
);

224
repmgr--5.0.sql Normal file
View File

@@ -0,0 +1,224 @@
-- 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
);
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
);
DO $repmgr$
DECLARE
DECLARE server_version_num INT;
BEGIN
SELECT setting
FROM pg_catalog.pg_settings
WHERE name = 'server_version_num'
INTO server_version_num;
IF server_version_num >= 90400 THEN
EXECUTE $repmgr_func$
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
)
$repmgr_func$;
ELSE
EXECUTE $repmgr_func$
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 TEXT NOT NULL,
last_wal_standby_location TEXT,
replication_lag BIGINT NOT NULL,
apply_lag BIGINT NOT NULL
)
$repmgr_func$;
END IF;
END$repmgr$;
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;
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 am_bdr_failover_handler(INT)
RETURNS BOOL
AS 'MODULE_PATHNAME', 'am_bdr_failover_handler'
LANGUAGE C STRICT;
CREATE FUNCTION unset_bdr_failover_handler()
RETURNS VOID
AS 'MODULE_PATHNAME', 'unset_bdr_failover_handler'
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

@@ -987,7 +987,19 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, Ite
make_remote_repmgr_path(&command, cell->node_info);
appendPQExpBufferStr(&command,
" cluster show --csv -L NOTICE --terse\"");
" cluster show --csv --terse");
/*
* Usually we'll want NOTICE as the log level, but if the user
* explicitly provided one with --log-level, that will be passed
* in the remote repmgr invocation.
*/
if (runtime_options.log_level[0] == '\0')
{
appendPQExpBufferStr(&command,
" -L NOTICE");
}
appendPQExpBufferChar(&command, '"');
log_verbose(LOG_DEBUG, "build_cluster_matrix(): executing:\n %s", command.data);
@@ -1175,7 +1187,18 @@ build_cluster_crosscheck(t_node_status_cube ***dest_cube, int *name_length, Item
make_remote_repmgr_path(&command, cell->node_info);
appendPQExpBufferStr(&command,
" cluster matrix --csv -L NOTICE --terse");
" cluster matrix --csv --terse");
/*
* Usually we'll want NOTICE as the log level, but if the user
* explicitly provided one with --log-level, that will be passed
* in the remote repmgr invocation.
*/
if (runtime_options.log_level[0] == '\0')
{
appendPQExpBufferStr(&command,
" -L NOTICE");
}
initPQExpBuffer(&command_output);

View File

@@ -26,478 +26,9 @@
#include "repmgr-client-global.h"
#include "repmgr-action-daemon.h"
#define REPMGR_DAEMON_STOP_START_WAIT 15
#define REPMGR_DAEMON_STATUS_START_HINT _("use \"repmgr daemon status\" to confirm that repmgrd was successfully started")
#define REPMGR_DAEMON_STATUS_STOP_HINT _("use \"repmgr daemon status\" to confirm that repmgrd was successfully stopped")
/*
* Possibly also show:
* - repmgrd start time?
* - repmgrd mode
* - priority
* - whether promotion candidate (due to zero priority/different location)
*/
typedef enum
{
STATUS_ID = 0,
STATUS_NAME,
STATUS_ROLE,
STATUS_PG,
STATUS_UPSTREAM_NAME,
STATUS_LOCATION,
STATUS_PRIORITY,
STATUS_REPMGRD,
STATUS_PID,
STATUS_PAUSED,
STATUS_UPSTREAM_LAST_SEEN
} StatusHeader;
#define STATUS_HEADER_COUNT 11
struct ColHeader headers_status[STATUS_HEADER_COUNT];
static void fetch_node_records(PGconn *conn, NodeInfoList *node_list);
static void _do_repmgr_pause(bool pause);
void
do_daemon_status(void)
{
PGconn *conn = NULL;
NodeInfoList nodes = T_NODE_INFO_LIST_INITIALIZER;
NodeInfoListCell *cell = NULL;
int i;
RepmgrdInfo **repmgrd_info;
ItemList warnings = {NULL, NULL};
bool connection_error_found = false;
/* Connect to local database to obtain cluster connection data */
log_verbose(LOG_INFO, _("connecting to database"));
if (strlen(config_file_options.conninfo))
conn = establish_db_connection(config_file_options.conninfo, true);
else
conn = establish_db_connection_by_params(&source_conninfo, true);
fetch_node_records(conn, &nodes);
repmgrd_info = (RepmgrdInfo **) pg_malloc0(sizeof(RepmgrdInfo *) * nodes.node_count);
if (repmgrd_info == NULL)
{
log_error(_("unable to allocate memory"));
exit(ERR_OUT_OF_MEMORY);
}
strncpy(headers_status[STATUS_ID].title, _("ID"), MAXLEN);
strncpy(headers_status[STATUS_NAME].title, _("Name"), MAXLEN);
strncpy(headers_status[STATUS_ROLE].title, _("Role"), MAXLEN);
strncpy(headers_status[STATUS_PG].title, _("Status"), MAXLEN);
strncpy(headers_status[STATUS_UPSTREAM_NAME].title, _("Upstream"), MAXLEN);
/* following only displayed with the --detail option */
strncpy(headers_status[STATUS_LOCATION].title, _("Location"), MAXLEN);
if (runtime_options.compact == true)
strncpy(headers_status[STATUS_PRIORITY].title, _("Prio."), MAXLEN);
else
strncpy(headers_status[STATUS_PRIORITY].title, _("Priority"), MAXLEN);
strncpy(headers_status[STATUS_REPMGRD].title, _("repmgrd"), MAXLEN);
strncpy(headers_status[STATUS_PID].title, _("PID"), MAXLEN);
strncpy(headers_status[STATUS_PAUSED].title, _("Paused?"), MAXLEN);
if (runtime_options.compact == true)
strncpy(headers_status[STATUS_UPSTREAM_LAST_SEEN].title, _("Upstr. last"), MAXLEN);
else
strncpy(headers_status[STATUS_UPSTREAM_LAST_SEEN].title, _("Upstream last seen"), MAXLEN);
for (i = 0; i < STATUS_HEADER_COUNT; i++)
{
headers_status[i].max_length = strlen(headers_status[i].title);
headers_status[i].display = true;
}
if (runtime_options.detail == false)
{
headers_status[STATUS_LOCATION].display = false;
headers_status[STATUS_PRIORITY].display = false;
}
i = 0;
for (cell = nodes.head; cell; cell = cell->next)
{
int j;
PQExpBufferData node_status;
PQExpBufferData upstream;
repmgrd_info[i] = pg_malloc0(sizeof(RepmgrdInfo));
repmgrd_info[i]->node_id = cell->node_info->node_id;
repmgrd_info[i]->pid = UNKNOWN_PID;
repmgrd_info[i]->recovery_type = RECTYPE_UNKNOWN;
repmgrd_info[i]->paused = false;
repmgrd_info[i]->running = false;
repmgrd_info[i]->pg_running = true;
repmgrd_info[i]->wal_paused_pending_wal = false;
repmgrd_info[i]->upstream_last_seen = -1;
cell->node_info->conn = establish_db_connection_quiet(cell->node_info->conninfo);
if (PQstatus(cell->node_info->conn) != CONNECTION_OK)
{
connection_error_found = true;
if (runtime_options.verbose)
{
char error[MAXLEN];
strncpy(error, PQerrorMessage(cell->node_info->conn), MAXLEN);
item_list_append_format(&warnings,
"when attempting to connect to node \"%s\" (ID: %i), following error encountered :\n\"%s\"",
cell->node_info->node_name, cell->node_info->node_id, trim(error));
}
else
{
item_list_append_format(&warnings,
"unable to connect to node \"%s\" (ID: %i)",
cell->node_info->node_name, cell->node_info->node_id);
}
repmgrd_info[i]->pg_running = false;
maxlen_snprintf(repmgrd_info[i]->repmgrd_running, "%s", _("n/a"));
maxlen_snprintf(repmgrd_info[i]->pid_text, "%s", _("n/a"));
}
else
{
cell->node_info->node_status = NODE_STATUS_UP;
cell->node_info->recovery_type = get_recovery_type(cell->node_info->conn);
repmgrd_info[i]->pid = repmgrd_get_pid(cell->node_info->conn);
repmgrd_info[i]->running = repmgrd_is_running(cell->node_info->conn);
if (repmgrd_info[i]->running == true)
{
maxlen_snprintf(repmgrd_info[i]->repmgrd_running, "%s", _("running"));
}
else
{
maxlen_snprintf(repmgrd_info[i]->repmgrd_running, "%s", _("not running"));
}
if (repmgrd_info[i]->pid == UNKNOWN_PID)
{
maxlen_snprintf(repmgrd_info[i]->pid_text, "%s", _("n/a"));
}
else
{
maxlen_snprintf(repmgrd_info[i]->pid_text, "%i", repmgrd_info[i]->pid);
}
repmgrd_info[i]->paused = repmgrd_is_paused(cell->node_info->conn);
repmgrd_info[i]->recovery_type = get_recovery_type(cell->node_info->conn);
if (repmgrd_info[i]->recovery_type == RECTYPE_STANDBY)
{
repmgrd_info[i]->wal_paused_pending_wal = is_wal_replay_paused(cell->node_info->conn, true);
if (repmgrd_info[i]->wal_paused_pending_wal == true)
{
item_list_append_format(&warnings,
_("WAL replay is paused on node \"%s\" (ID: %i) with WAL replay pending; this node cannot be manually promoted until WAL replay is resumed"),
cell->node_info->node_name, cell->node_info->node_id);
}
}
repmgrd_info[i]->upstream_last_seen = get_upstream_last_seen(cell->node_info->conn, cell->node_info->type);
if (repmgrd_info[i]->upstream_last_seen < 0)
{
maxlen_snprintf(repmgrd_info[i]->upstream_last_seen_text, "%s", _("n/a"));
}
else
{
if (runtime_options.compact == true)
{
maxlen_snprintf(repmgrd_info[i]->upstream_last_seen_text, _("%i sec(s) ago"), repmgrd_info[i]->upstream_last_seen);
}
else
{
maxlen_snprintf(repmgrd_info[i]->upstream_last_seen_text, _("%i second(s) ago"), repmgrd_info[i]->upstream_last_seen);
}
}
}
initPQExpBuffer(&node_status);
initPQExpBuffer(&upstream);
(void)format_node_status(cell->node_info, &node_status, &upstream, &warnings);
snprintf(repmgrd_info[i]->pg_running_text, sizeof(cell->node_info->details),
"%s", node_status.data);
snprintf(cell->node_info->upstream_node_name, sizeof(cell->node_info->upstream_node_name),
"%s", upstream.data);
termPQExpBuffer(&node_status);
termPQExpBuffer(&upstream);
PQfinish(cell->node_info->conn);
headers_status[STATUS_NAME].cur_length = strlen(cell->node_info->node_name);
headers_status[STATUS_ROLE].cur_length = strlen(get_node_type_string(cell->node_info->type));
headers_status[STATUS_PG].cur_length = strlen(repmgrd_info[i]->pg_running_text);
headers_status[STATUS_UPSTREAM_NAME].cur_length = strlen(cell->node_info->upstream_node_name);
if (runtime_options.detail == true)
{
PQExpBufferData buf;
headers_status[STATUS_LOCATION].cur_length = strlen(cell->node_info->location);
initPQExpBuffer(&buf);
appendPQExpBuffer(&buf, "%i", cell->node_info->priority);
headers_status[STATUS_PRIORITY].cur_length = strlen(buf.data);
termPQExpBuffer(&buf);
}
headers_status[STATUS_PID].cur_length = strlen(repmgrd_info[i]->pid_text);
headers_status[STATUS_REPMGRD].cur_length = strlen(repmgrd_info[i]->repmgrd_running);
headers_status[STATUS_UPSTREAM_LAST_SEEN].cur_length = strlen(repmgrd_info[i]->upstream_last_seen_text);
for (j = 0; j < STATUS_HEADER_COUNT; j++)
{
if (headers_status[j].cur_length > headers_status[j].max_length)
{
headers_status[j].max_length = headers_status[j].cur_length;
}
}
i++;
}
/* Print column header row (text mode only) */
if (runtime_options.output_mode == OM_TEXT)
{
print_status_header(STATUS_HEADER_COUNT, headers_status);
}
i = 0;
for (cell = nodes.head; cell; cell = cell->next)
{
if (runtime_options.output_mode == OM_CSV)
{
int running = repmgrd_info[i]->running ? 1 : 0;
int paused = repmgrd_info[i]->paused ? 1 : 0;
/* If PostgreSQL is not running, repmgrd status is unknown */
if (repmgrd_info[i]->pg_running == false)
{
running = -1;
paused = -1;
}
printf("%i,%s,%s,%i,%i,%i,%i,%i,%i,%s\n",
cell->node_info->node_id,
cell->node_info->node_name,
get_node_type_string(cell->node_info->type),
repmgrd_info[i]->pg_running ? 1 : 0,
running,
repmgrd_info[i]->pid,
paused,
cell->node_info->priority,
repmgrd_info[i]->pid == UNKNOWN_PID
? -1
: repmgrd_info[i]->upstream_last_seen,
cell->node_info->location);
}
else
{
printf(" %-*i ", headers_status[STATUS_ID].max_length, cell->node_info->node_id);
printf("| %-*s ", headers_status[STATUS_NAME].max_length, cell->node_info->node_name);
printf("| %-*s ", headers_status[STATUS_ROLE].max_length, get_node_type_string(cell->node_info->type));
printf("| %-*s ", headers_status[STATUS_PG].max_length, repmgrd_info[i]->pg_running_text);
printf("| %-*s ", headers_status[STATUS_UPSTREAM_NAME].max_length, cell->node_info->upstream_node_name);
if (runtime_options.detail == true)
{
printf("| %-*s ", headers_status[STATUS_LOCATION].max_length, cell->node_info->location);
printf("| %-*i ", headers_status[STATUS_PRIORITY].max_length, cell->node_info->priority);
}
printf("| %-*s ", headers_status[STATUS_REPMGRD].max_length, repmgrd_info[i]->repmgrd_running);
printf("| %-*s ", headers_status[STATUS_PID].max_length, repmgrd_info[i]->pid_text);
if (repmgrd_info[i]->pid == UNKNOWN_PID)
{
printf("| %-*s ", headers_status[STATUS_PAUSED].max_length, _("n/a"));
printf("| %-*s ", headers_status[STATUS_UPSTREAM_LAST_SEEN].max_length, _("n/a"));
}
else
{
printf("| %-*s ", headers_status[STATUS_PAUSED].max_length, repmgrd_info[i]->paused ? _("yes") : _("no"));
printf("| %-*s ", headers_status[STATUS_UPSTREAM_LAST_SEEN].max_length, repmgrd_info[i]->upstream_last_seen_text);
}
printf("\n");
}
pfree(repmgrd_info[i]);
i++;
}
pfree(repmgrd_info);
/* emit any warnings */
if (warnings.head != NULL && runtime_options.terse == false && runtime_options.output_mode != OM_CSV)
{
ItemListCell *cell = NULL;
printf(_("\nWARNING: following issues were detected\n"));
for (cell = warnings.head; cell; cell = cell->next)
{
printf(_(" - %s\n"), cell->string);
}
if (runtime_options.verbose == false && connection_error_found == true)
{
log_hint(_("execute with --verbose option to see connection error messages"));
}
}
}
void
do_daemon_pause(void)
{
_do_repmgr_pause(true);
}
void
do_daemon_unpause(void)
{
_do_repmgr_pause(false);
}
static void
_do_repmgr_pause(bool pause)
{
PGconn *conn = NULL;
NodeInfoList nodes = T_NODE_INFO_LIST_INITIALIZER;
NodeInfoListCell *cell = NULL;
int i;
int error_nodes = 0;
/* Connect to local database to obtain cluster connection data */
log_verbose(LOG_INFO, _("connecting to database"));
if (strlen(config_file_options.conninfo))
conn = establish_db_connection(config_file_options.conninfo, true);
else
conn = establish_db_connection_by_params(&source_conninfo, true);
fetch_node_records(conn, &nodes);
i = 0;
for (cell = nodes.head; cell; cell = cell->next)
{
log_verbose(LOG_DEBUG, "pausing node %i (%s)",
cell->node_info->node_id,
cell->node_info->node_name);
cell->node_info->conn = establish_db_connection_quiet(cell->node_info->conninfo);
if (PQstatus(cell->node_info->conn) != CONNECTION_OK)
{
log_warning(_("unable to connect to node %i"),
cell->node_info->node_id);
error_nodes++;
}
else
{
if (runtime_options.dry_run == true)
{
if (pause == true)
{
log_info(_("would pause node %i (%s) "),
cell->node_info->node_id,
cell->node_info->node_name);
}
else
{
log_info(_("would unpause node %i (%s) "),
cell->node_info->node_id,
cell->node_info->node_name);
}
}
else
{
bool success = repmgrd_pause(cell->node_info->conn, pause);
if (success == false)
error_nodes++;
log_notice(_("node %i (%s) %s"),
cell->node_info->node_id,
cell->node_info->node_name,
success == true
? pause == true ? "paused" : "unpaused"
: pause == true ? "not paused" : "not unpaused");
}
PQfinish(cell->node_info->conn);
}
i++;
}
if (error_nodes > 0)
{
if (pause == true)
{
log_error(_("unable to pause %i node(s)"), error_nodes);
}
else
{
log_error(_("unable to unpause %i node(s)"), error_nodes);
}
log_hint(_("execute \"repmgr daemon status\" to view current status"));
exit(ERR_REPMGRD_PAUSE);
}
exit(SUCCESS);
}
void
fetch_node_records(PGconn *conn, NodeInfoList *node_list)
{
bool success = get_all_node_records_with_upstream(conn, node_list);
if (success == false)
{
/* get_all_node_records() will display any error message */
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
if (node_list->node_count == 0)
{
log_error(_("no node records were found"));
log_hint(_("ensure at least one node is registered"));
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
}
#define REPMGR_SERVICE_STOP_START_WAIT 15
#define REPMGR_SERVICE_STATUS_START_HINT _("use \"repmgr service status\" to confirm that repmgrd was successfully started")
#define REPMGR_SERVICE_STATUS_STOP_HINT _("use \"repmgr service status\" to confirm that repmgrd was successfully stopped")
void
do_daemon_start(void)
@@ -583,12 +114,12 @@ do_daemon_start(void)
if (runtime_options.no_wait == true || runtime_options.wait == 0)
{
log_hint(REPMGR_DAEMON_STATUS_START_HINT);
log_hint(REPMGR_SERVICE_STATUS_START_HINT);
}
else
{
int i = 0;
int timeout = REPMGR_DAEMON_STOP_START_WAIT;
int timeout = REPMGR_SERVICE_STOP_START_WAIT;
if (runtime_options.wait_provided)
timeout = runtime_options.wait;
@@ -598,7 +129,7 @@ do_daemon_start(void)
if (PQstatus(conn) != CONNECTION_OK)
{
log_notice(_("unable to connect to local node"));
log_hint(REPMGR_DAEMON_STATUS_START_HINT);
log_hint(REPMGR_SERVICE_STATUS_START_HINT);
exit(ERR_DB_CONN);
}
@@ -616,7 +147,7 @@ do_daemon_start(void)
PQfinish(conn);
log_error(_("repmgrd does not appear to have started after %i seconds"),
timeout);
log_hint(REPMGR_DAEMON_STATUS_START_HINT);
log_hint(REPMGR_SERVICE_STATUS_START_HINT);
exit(ERR_REPMGRD_SERVICE);
}
@@ -712,12 +243,12 @@ void do_daemon_stop(void)
if (runtime_options.no_wait == true || runtime_options.wait == 0)
{
if (have_db_connection == true)
log_hint(REPMGR_DAEMON_STATUS_STOP_HINT);
log_hint(REPMGR_SERVICE_STATUS_STOP_HINT);
}
else
{
int i = 0;
int timeout = REPMGR_DAEMON_STOP_START_WAIT;
int timeout = REPMGR_SERVICE_STOP_START_WAIT;
/*
*
*/
@@ -732,7 +263,7 @@ void do_daemon_stop(void)
log_warning(_("unable to determine repmgrd PID"));
if (have_db_connection == true)
log_hint(REPMGR_DAEMON_STATUS_STOP_HINT);
log_hint(REPMGR_SERVICE_STATUS_STOP_HINT);
exit(ERR_REPMGRD_SERVICE);
}
@@ -764,7 +295,7 @@ void do_daemon_stop(void)
timeout);
if (have_db_connection == true)
log_hint(REPMGR_DAEMON_STATUS_START_HINT);
log_hint(REPMGR_SERVICE_STATUS_START_HINT);
exit(ERR_REPMGRD_SERVICE);
}
@@ -783,53 +314,29 @@ void do_daemon_help(void)
print_help_header();
printf(_("Usage:\n"));
printf(_(" %s [OPTIONS] daemon status\n"), progname());
printf(_(" %s [OPTIONS] daemon pause\n"), progname());
printf(_(" %s [OPTIONS] daemon unpause\n"), progname());
printf(_(" %s [OPTIONS] daemon start\n"), progname());
printf(_(" %s [OPTIONS] daemon stop\n"), progname());
puts("");
printf(_("DAEMON STATUS\n"));
puts("");
printf(_(" \"daemon status\" shows the status of repmgrd on each node in the cluster\n"));
puts("");
printf(_(" --csv emit output as CSV\n"));
printf(_(" --detail show additional detail\n"));
printf(_(" --verbose show text of database connection error messages\n"));
puts("");
printf(_("DAEMON START\n"));
puts("");
printf(_(" \"daemon start\" attempts to start repmgrd\n"));
printf(_(" \"daemon start\" attempts to start repmgrd on the local node\n"));
puts("");
printf(_(" --dry-run check prerequisites but don't start repmgrd\n"));
printf(_(" -w/--wait wait for repmgrd to start (default: %i seconds)\n"), REPMGR_DAEMON_STOP_START_WAIT);
printf(_(" -w/--wait wait for repmgrd to start (default: %i seconds)\n"), REPMGR_SERVICE_STOP_START_WAIT);
printf(_(" --no-wait don't wait for repmgrd to start\n"));
puts("");
printf(_("DAEMON STOP\n"));
puts("");
printf(_(" \"daemon stop\" attempts to stop repmgrd\n"));
printf(_(" \"daemon stop\" attempts to stop repmgrd on the local node\n"));
puts("");
printf(_(" --dry-run check prerequisites but don't stop repmgrd\n"));
printf(_(" -w/--wait wait for repmgrd to stop (default: %i seconds)\n"), REPMGR_DAEMON_STOP_START_WAIT);
printf(_(" -w/--wait wait for repmgrd to stop (default: %i seconds)\n"), REPMGR_SERVICE_STOP_START_WAIT);
printf(_(" --no-wait don't wait for repmgrd to stop\n"));
puts("");
printf(_("DAEMON PAUSE\n"));
puts("");
printf(_(" \"daemon pause\" instructs repmgrd on each node to pause failover detection\n"));
puts("");
printf(_(" --dry-run check if nodes are reachable but don't pause repmgrd\n"));
puts("");
printf(_("DAEMON UNPAUSE\n"));
puts("");
printf(_(" \"daemon unpause\" instructs repmgrd on each node to resume failover detection\n"));
puts("");
printf(_(" --dry-run check if nodes are reachable but don't unpause repmgrd\n"));
puts("");
puts("");
}

View File

@@ -19,10 +19,6 @@
#ifndef _REPMGR_ACTION_DAEMON_H_
#define _REPMGR_ACTION_DAEMON_H_
extern void do_daemon_status(void);
extern void do_daemon_pause(void);
extern void do_daemon_unpause(void);
extern void do_daemon_start(void);
extern void do_daemon_stop(void);

View File

@@ -1833,7 +1833,7 @@ do_node_check_data_directory(PGconn *conn, OutputMode mode, t_node_info *node_in
* a superuser connection
*/
if (is_superuser_connection(conn, NULL) == 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)
@@ -1878,7 +1878,7 @@ do_node_check_data_directory(PGconn *conn, OutputMode mode, t_node_info *node_in
/* XXX add -S/--superuser option */
if (PQserverVersion(conn) >= 100000)
{
log_hint(_("add the \"%s\" user to group \"pg_read_all_settings\""),
log_hint(_("add the \"%s\" user to group \"pg_read_all_settings\" or \"pg_monitor\""),
PQuser(conn));
}
}

535
repmgr-action-service.c Normal file
View File

@@ -0,0 +1,535 @@
/*
* repmgr-action-service.c
*
* Implements repmgrd actions for the repmgr command line utility
* Copyright (c) 2ndQuadrant, 2010-2019
*
* 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 <signal.h>
#include <sys/stat.h> /* for stat() */
#include "repmgr.h"
#include "repmgr-client-global.h"
#include "repmgr-action-service.h"
/*
* Possibly also show:
* - repmgrd start time?
* - repmgrd mode
* - priority
* - whether promotion candidate (due to zero priority/different location)
*/
typedef enum
{
STATUS_ID = 0,
STATUS_NAME,
STATUS_ROLE,
STATUS_PG,
STATUS_UPSTREAM_NAME,
STATUS_LOCATION,
STATUS_PRIORITY,
STATUS_REPMGRD,
STATUS_PID,
STATUS_PAUSED,
STATUS_UPSTREAM_LAST_SEEN
} StatusHeader;
#define STATUS_HEADER_COUNT 11
struct ColHeader headers_status[STATUS_HEADER_COUNT];
static void fetch_node_records(PGconn *conn, NodeInfoList *node_list);
static void _do_repmgr_pause(bool pause);
void
do_service_status(void)
{
PGconn *conn = NULL;
NodeInfoList nodes = T_NODE_INFO_LIST_INITIALIZER;
NodeInfoListCell *cell = NULL;
int i;
RepmgrdInfo **repmgrd_info;
ItemList warnings = {NULL, NULL};
bool connection_error_found = false;
/* Connect to local database to obtain cluster connection data */
log_verbose(LOG_INFO, _("connecting to database"));
if (strlen(config_file_options.conninfo))
conn = establish_db_connection(config_file_options.conninfo, true);
else
conn = establish_db_connection_by_params(&source_conninfo, true);
fetch_node_records(conn, &nodes);
repmgrd_info = (RepmgrdInfo **) pg_malloc0(sizeof(RepmgrdInfo *) * nodes.node_count);
if (repmgrd_info == NULL)
{
log_error(_("unable to allocate memory"));
exit(ERR_OUT_OF_MEMORY);
}
strncpy(headers_status[STATUS_ID].title, _("ID"), MAXLEN);
strncpy(headers_status[STATUS_NAME].title, _("Name"), MAXLEN);
strncpy(headers_status[STATUS_ROLE].title, _("Role"), MAXLEN);
strncpy(headers_status[STATUS_PG].title, _("Status"), MAXLEN);
strncpy(headers_status[STATUS_UPSTREAM_NAME].title, _("Upstream"), MAXLEN);
/* following only displayed with the --detail option */
strncpy(headers_status[STATUS_LOCATION].title, _("Location"), MAXLEN);
if (runtime_options.compact == true)
strncpy(headers_status[STATUS_PRIORITY].title, _("Prio."), MAXLEN);
else
strncpy(headers_status[STATUS_PRIORITY].title, _("Priority"), MAXLEN);
strncpy(headers_status[STATUS_REPMGRD].title, _("repmgrd"), MAXLEN);
strncpy(headers_status[STATUS_PID].title, _("PID"), MAXLEN);
strncpy(headers_status[STATUS_PAUSED].title, _("Paused?"), MAXLEN);
if (runtime_options.compact == true)
strncpy(headers_status[STATUS_UPSTREAM_LAST_SEEN].title, _("Upstr. last"), MAXLEN);
else
strncpy(headers_status[STATUS_UPSTREAM_LAST_SEEN].title, _("Upstream last seen"), MAXLEN);
for (i = 0; i < STATUS_HEADER_COUNT; i++)
{
headers_status[i].max_length = strlen(headers_status[i].title);
headers_status[i].display = true;
}
if (runtime_options.detail == false)
{
headers_status[STATUS_LOCATION].display = false;
headers_status[STATUS_PRIORITY].display = false;
}
i = 0;
for (cell = nodes.head; cell; cell = cell->next)
{
int j;
PQExpBufferData node_status;
PQExpBufferData upstream;
repmgrd_info[i] = pg_malloc0(sizeof(RepmgrdInfo));
repmgrd_info[i]->node_id = cell->node_info->node_id;
repmgrd_info[i]->pid = UNKNOWN_PID;
repmgrd_info[i]->recovery_type = RECTYPE_UNKNOWN;
repmgrd_info[i]->paused = false;
repmgrd_info[i]->running = false;
repmgrd_info[i]->pg_running = true;
repmgrd_info[i]->wal_paused_pending_wal = false;
repmgrd_info[i]->upstream_last_seen = -1;
cell->node_info->conn = establish_db_connection_quiet(cell->node_info->conninfo);
if (PQstatus(cell->node_info->conn) != CONNECTION_OK)
{
connection_error_found = true;
if (runtime_options.verbose)
{
char error[MAXLEN];
strncpy(error, PQerrorMessage(cell->node_info->conn), MAXLEN);
item_list_append_format(&warnings,
"when attempting to connect to node \"%s\" (ID: %i), following error encountered :\n\"%s\"",
cell->node_info->node_name, cell->node_info->node_id, trim(error));
}
else
{
item_list_append_format(&warnings,
"unable to connect to node \"%s\" (ID: %i)",
cell->node_info->node_name, cell->node_info->node_id);
}
repmgrd_info[i]->pg_running = false;
maxlen_snprintf(repmgrd_info[i]->repmgrd_running, "%s", _("n/a"));
maxlen_snprintf(repmgrd_info[i]->pid_text, "%s", _("n/a"));
}
else
{
cell->node_info->node_status = NODE_STATUS_UP;
cell->node_info->recovery_type = get_recovery_type(cell->node_info->conn);
repmgrd_info[i]->pid = repmgrd_get_pid(cell->node_info->conn);
repmgrd_info[i]->running = repmgrd_is_running(cell->node_info->conn);
if (repmgrd_info[i]->running == true)
{
maxlen_snprintf(repmgrd_info[i]->repmgrd_running, "%s", _("running"));
}
else
{
maxlen_snprintf(repmgrd_info[i]->repmgrd_running, "%s", _("not running"));
}
if (repmgrd_info[i]->pid == UNKNOWN_PID)
{
maxlen_snprintf(repmgrd_info[i]->pid_text, "%s", _("n/a"));
}
else
{
maxlen_snprintf(repmgrd_info[i]->pid_text, "%i", repmgrd_info[i]->pid);
}
repmgrd_info[i]->paused = repmgrd_is_paused(cell->node_info->conn);
repmgrd_info[i]->recovery_type = get_recovery_type(cell->node_info->conn);
if (repmgrd_info[i]->recovery_type == RECTYPE_STANDBY)
{
repmgrd_info[i]->wal_paused_pending_wal = is_wal_replay_paused(cell->node_info->conn, true);
if (repmgrd_info[i]->wal_paused_pending_wal == true)
{
item_list_append_format(&warnings,
_("WAL replay is paused on node \"%s\" (ID: %i) with WAL replay pending; this node cannot be manually promoted until WAL replay is resumed"),
cell->node_info->node_name, cell->node_info->node_id);
}
}
repmgrd_info[i]->upstream_last_seen = get_upstream_last_seen(cell->node_info->conn, cell->node_info->type);
if (repmgrd_info[i]->upstream_last_seen < 0)
{
maxlen_snprintf(repmgrd_info[i]->upstream_last_seen_text, "%s", _("n/a"));
}
else
{
if (runtime_options.compact == true)
{
maxlen_snprintf(repmgrd_info[i]->upstream_last_seen_text, _("%i sec(s) ago"), repmgrd_info[i]->upstream_last_seen);
}
else
{
maxlen_snprintf(repmgrd_info[i]->upstream_last_seen_text, _("%i second(s) ago"), repmgrd_info[i]->upstream_last_seen);
}
}
}
initPQExpBuffer(&node_status);
initPQExpBuffer(&upstream);
(void)format_node_status(cell->node_info, &node_status, &upstream, &warnings);
snprintf(repmgrd_info[i]->pg_running_text, sizeof(cell->node_info->details),
"%s", node_status.data);
snprintf(cell->node_info->upstream_node_name, sizeof(cell->node_info->upstream_node_name),
"%s", upstream.data);
termPQExpBuffer(&node_status);
termPQExpBuffer(&upstream);
PQfinish(cell->node_info->conn);
headers_status[STATUS_NAME].cur_length = strlen(cell->node_info->node_name);
headers_status[STATUS_ROLE].cur_length = strlen(get_node_type_string(cell->node_info->type));
headers_status[STATUS_PG].cur_length = strlen(repmgrd_info[i]->pg_running_text);
headers_status[STATUS_UPSTREAM_NAME].cur_length = strlen(cell->node_info->upstream_node_name);
if (runtime_options.detail == true)
{
PQExpBufferData buf;
headers_status[STATUS_LOCATION].cur_length = strlen(cell->node_info->location);
initPQExpBuffer(&buf);
appendPQExpBuffer(&buf, "%i", cell->node_info->priority);
headers_status[STATUS_PRIORITY].cur_length = strlen(buf.data);
termPQExpBuffer(&buf);
}
headers_status[STATUS_PID].cur_length = strlen(repmgrd_info[i]->pid_text);
headers_status[STATUS_REPMGRD].cur_length = strlen(repmgrd_info[i]->repmgrd_running);
headers_status[STATUS_UPSTREAM_LAST_SEEN].cur_length = strlen(repmgrd_info[i]->upstream_last_seen_text);
for (j = 0; j < STATUS_HEADER_COUNT; j++)
{
if (headers_status[j].cur_length > headers_status[j].max_length)
{
headers_status[j].max_length = headers_status[j].cur_length;
}
}
i++;
}
/* Print column header row (text mode only) */
if (runtime_options.output_mode == OM_TEXT)
{
print_status_header(STATUS_HEADER_COUNT, headers_status);
}
i = 0;
for (cell = nodes.head; cell; cell = cell->next)
{
if (runtime_options.output_mode == OM_CSV)
{
int running = repmgrd_info[i]->running ? 1 : 0;
int paused = repmgrd_info[i]->paused ? 1 : 0;
/* If PostgreSQL is not running, repmgrd status is unknown */
if (repmgrd_info[i]->pg_running == false)
{
running = -1;
paused = -1;
}
printf("%i,%s,%s,%i,%i,%i,%i,%i,%i,%s\n",
cell->node_info->node_id,
cell->node_info->node_name,
get_node_type_string(cell->node_info->type),
repmgrd_info[i]->pg_running ? 1 : 0,
running,
repmgrd_info[i]->pid,
paused,
cell->node_info->priority,
repmgrd_info[i]->pid == UNKNOWN_PID
? -1
: repmgrd_info[i]->upstream_last_seen,
cell->node_info->location);
}
else
{
printf(" %-*i ", headers_status[STATUS_ID].max_length, cell->node_info->node_id);
printf("| %-*s ", headers_status[STATUS_NAME].max_length, cell->node_info->node_name);
printf("| %-*s ", headers_status[STATUS_ROLE].max_length, get_node_type_string(cell->node_info->type));
printf("| %-*s ", headers_status[STATUS_PG].max_length, repmgrd_info[i]->pg_running_text);
printf("| %-*s ", headers_status[STATUS_UPSTREAM_NAME].max_length, cell->node_info->upstream_node_name);
if (runtime_options.detail == true)
{
printf("| %-*s ", headers_status[STATUS_LOCATION].max_length, cell->node_info->location);
printf("| %-*i ", headers_status[STATUS_PRIORITY].max_length, cell->node_info->priority);
}
printf("| %-*s ", headers_status[STATUS_REPMGRD].max_length, repmgrd_info[i]->repmgrd_running);
printf("| %-*s ", headers_status[STATUS_PID].max_length, repmgrd_info[i]->pid_text);
if (repmgrd_info[i]->pid == UNKNOWN_PID)
{
printf("| %-*s ", headers_status[STATUS_PAUSED].max_length, _("n/a"));
printf("| %-*s ", headers_status[STATUS_UPSTREAM_LAST_SEEN].max_length, _("n/a"));
}
else
{
printf("| %-*s ", headers_status[STATUS_PAUSED].max_length, repmgrd_info[i]->paused ? _("yes") : _("no"));
printf("| %-*s ", headers_status[STATUS_UPSTREAM_LAST_SEEN].max_length, repmgrd_info[i]->upstream_last_seen_text);
}
printf("\n");
}
pfree(repmgrd_info[i]);
i++;
}
pfree(repmgrd_info);
/* emit any warnings */
if (warnings.head != NULL && runtime_options.terse == false && runtime_options.output_mode != OM_CSV)
{
ItemListCell *cell = NULL;
printf(_("\nWARNING: following issues were detected\n"));
for (cell = warnings.head; cell; cell = cell->next)
{
printf(_(" - %s\n"), cell->string);
}
if (runtime_options.verbose == false && connection_error_found == true)
{
log_hint(_("execute with --verbose option to see connection error messages"));
}
}
}
void
do_service_pause(void)
{
_do_repmgr_pause(true);
}
void
do_service_unpause(void)
{
_do_repmgr_pause(false);
}
static void
_do_repmgr_pause(bool pause)
{
PGconn *conn = NULL;
NodeInfoList nodes = T_NODE_INFO_LIST_INITIALIZER;
NodeInfoListCell *cell = NULL;
int i;
int error_nodes = 0;
/* Connect to local database to obtain cluster connection data */
log_verbose(LOG_INFO, _("connecting to database"));
if (strlen(config_file_options.conninfo))
conn = establish_db_connection(config_file_options.conninfo, true);
else
conn = establish_db_connection_by_params(&source_conninfo, true);
fetch_node_records(conn, &nodes);
i = 0;
for (cell = nodes.head; cell; cell = cell->next)
{
log_verbose(LOG_DEBUG, "pausing node %i (%s)",
cell->node_info->node_id,
cell->node_info->node_name);
cell->node_info->conn = establish_db_connection_quiet(cell->node_info->conninfo);
if (PQstatus(cell->node_info->conn) != CONNECTION_OK)
{
log_warning(_("unable to connect to node %i"),
cell->node_info->node_id);
error_nodes++;
}
else
{
if (runtime_options.dry_run == true)
{
if (pause == true)
{
log_info(_("would pause node %i (%s) "),
cell->node_info->node_id,
cell->node_info->node_name);
}
else
{
log_info(_("would unpause node %i (%s) "),
cell->node_info->node_id,
cell->node_info->node_name);
}
}
else
{
bool success = repmgrd_pause(cell->node_info->conn, pause);
if (success == false)
error_nodes++;
log_notice(_("node %i (%s) %s"),
cell->node_info->node_id,
cell->node_info->node_name,
success == true
? pause == true ? "paused" : "unpaused"
: pause == true ? "not paused" : "not unpaused");
}
PQfinish(cell->node_info->conn);
}
i++;
}
if (error_nodes > 0)
{
if (pause == true)
{
log_error(_("unable to pause %i node(s)"), error_nodes);
}
else
{
log_error(_("unable to unpause %i node(s)"), error_nodes);
}
log_hint(_("execute \"repmgr service status\" to view current status"));
exit(ERR_REPMGRD_PAUSE);
}
exit(SUCCESS);
}
void
fetch_node_records(PGconn *conn, NodeInfoList *node_list)
{
bool success = get_all_node_records_with_upstream(conn, node_list);
if (success == false)
{
/* get_all_node_records() will display any error message */
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
if (node_list->node_count == 0)
{
log_error(_("no node records were found"));
log_hint(_("ensure at least one node is registered"));
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
}
void do_service_help(void)
{
print_help_header();
printf(_("Usage:\n"));
printf(_(" %s [OPTIONS] service status\n"), progname());
printf(_(" %s [OPTIONS] service pause\n"), progname());
printf(_(" %s [OPTIONS] service unpause\n"), progname());
puts("");
printf(_("SERVICE STATUS\n"));
puts("");
printf(_(" \"service status\" shows the status of repmgrd on each node in the cluster\n"));
puts("");
printf(_(" --csv emit output as CSV\n"));
printf(_(" --detail show additional detail\n"));
printf(_(" --verbose show text of database connection error messages\n"));
puts("");
printf(_("SERVICE PAUSE\n"));
puts("");
printf(_(" \"service pause\" instructs repmgrd on each node to pause failover detection\n"));
puts("");
printf(_(" --dry-run check if nodes are reachable but don't pause repmgrd\n"));
puts("");
printf(_("SERVICE UNPAUSE\n"));
puts("");
printf(_(" \"service unpause\" instructs repmgrd on each node to resume failover detection\n"));
puts("");
printf(_(" --dry-run check if nodes are reachable but don't unpause repmgrd\n"));
puts("");
puts("");
}

28
repmgr-action-service.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* repmgr-action-service.h
* Copyright (c) 2ndQuadrant, 2010-2019
*
* 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/>.
*/
#ifndef _REPMGR_ACTION_SERVICE_H_
#define _REPMGR_ACTION_SERVICE_H_
extern void do_service_status(void);
extern void do_service_pause(void);
extern void do_service_unpause(void);
extern void do_service_help(void);
#endif

View File

@@ -112,8 +112,10 @@ static void get_barman_property(char *dst, char *name, char *local_repmgr_direct
static int get_tablespace_data_barman(char *, TablespaceDataList *);
static char *make_barman_ssh_command(char *buf);
static bool create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, char *dest, bool as_file);
static bool create_recovery_file(t_node_info *node_record, t_conninfo_param_list *primary_conninfo, int server_version_num, char *dest, bool as_file);
static void write_primary_conninfo(PQExpBufferData *dest, t_conninfo_param_list *param_list);
static bool write_standby_signal(void);
static bool check_sibling_nodes(NodeInfoList *sibling_nodes, SiblingNodeStats *sibling_nodes_stats);
static bool check_free_wal_senders(int available_wal_senders, SiblingNodeStats *sibling_nodes_stats, bool *dry_run_success);
static bool check_free_slots(t_node_info *local_node_record, SiblingNodeStats *sibling_nodes_stats, bool *dry_run_success);
@@ -133,12 +135,13 @@ static bool parse_data_directory_config(const char *node_check_output);
*
* Parameters:
* --upstream-conninfo
* --upstream-node-id
* --no-upstream-connection
* -F/--force
* --dry-run
* -c/--fast-checkpoint
* --copy-external-config-files
* --recovery-min-apply-delay
* -R/--remote-user
* --replication-user (only required if no upstream record)
* --without-barman
* --recovery-conf-only
@@ -215,7 +218,7 @@ do_standby_clone(void)
/*
* Initialise list of conninfo parameters which will later be used to
* create the `primary_conninfo` string in recovery.conf .
* create the "primary_conninfo" recovery parameter.
*
* We'll initialise it with the host settings specified on the command
* line. As it's possible the standby will be cloned from a node different
@@ -665,10 +668,21 @@ do_standby_clone(void)
/* Write the recovery.conf file */
if (create_recovery_file(&local_node_record, &recovery_conninfo, local_data_directory, true) == false)
if (create_recovery_file(&local_node_record,
&recovery_conninfo,
source_server_version_num,
local_data_directory,
true) == false)
{
/* create_recovery_file() will log an error */
log_notice(_("unable to create recovery.conf; see preceding error messages"));
if (source_server_version_num >= 120000)
{
log_notice(_("unable to write replication configuration; see preceding error messages"));
}
else
{
log_notice(_("unable to create recovery.conf; see preceding error messages"));
}
log_hint(_("data directory (\"%s\") may need to be cleaned up manually"),
local_data_directory);
@@ -1171,60 +1185,92 @@ _do_create_recovery_conf(void)
}
}
/* check if recovery.conf exists */
snprintf(recovery_file_path, sizeof(recovery_file_path),
"%s/%s",
local_data_directory,
RECOVERY_COMMAND_FILE);
if (stat(recovery_file_path, &st) == -1)
/* check if recovery.conf exists (Pg11 and earlier only) */
if (PQserverVersion(upstream_conn) < 120000)
{
if (errno != ENOENT)
{
log_error(_("unable to check for existing \"recovery.conf\" file in \"%s\""),
local_data_directory);
log_detail("%s", strerror(errno));
exit(ERR_BAD_CONFIG);
}
}
else
{
if (runtime_options.force == false)
{
log_error(_("\"recovery.conf\" already exists in \"%s\""),
local_data_directory);
log_hint(_("use -F/--force to overwrite an existing \"recovery.conf\" file"));
exit(ERR_BAD_CONFIG);
}
snprintf(recovery_file_path, sizeof(recovery_file_path),
"%s/%s",
local_data_directory,
RECOVERY_COMMAND_FILE);
if (runtime_options.dry_run == true)
if (stat(recovery_file_path, &st) == -1)
{
log_warning(_("the existing \"recovery.conf\" file would be overwritten"));
if (errno != ENOENT)
{
log_error(_("unable to check for existing \"recovery.conf\" file in \"%s\""),
local_data_directory);
log_detail("%s", strerror(errno));
exit(ERR_BAD_CONFIG);
}
}
else
{
log_warning(_("the existing \"recovery.conf\" file will be overwritten"));
if (runtime_options.force == false)
{
log_error(_("\"recovery.conf\" already exists in \"%s\""),
local_data_directory);
log_hint(_("use -F/--force to overwrite an existing \"recovery.conf\" file"));
exit(ERR_BAD_CONFIG);
}
if (runtime_options.dry_run == true)
{
log_warning(_("the existing \"recovery.conf\" file would be overwritten"));
}
else
{
log_warning(_("the existing \"recovery.conf\" file will be overwritten"));
}
}
}
if (runtime_options.dry_run == true)
{
char recovery_conf_contents[MAXLEN] = "";
create_recovery_file(&local_node_record, &recovery_conninfo, recovery_conf_contents, false);
create_recovery_file(&local_node_record,
&recovery_conninfo,
PQserverVersion(upstream_conn),
recovery_conf_contents,
false);
if (PQserverVersion(upstream_conn) >= 120000)
{
log_info(_("following items would be added to \"postgresql.auto.conf\" in \"%s\""), local_data_directory);
}
else
{
log_info(_("would create \"recovery.conf\" file in \"%s\""), local_data_directory);
}
log_info(_("would create \"recovery.conf\" file in \"%s\""), local_data_directory);
log_detail(_("\n%s"), recovery_conf_contents);
}
else
{
if (!create_recovery_file(&local_node_record, &recovery_conninfo, local_data_directory, true))
if (!create_recovery_file(&local_node_record,
&recovery_conninfo,
PQserverVersion(upstream_conn),
local_data_directory,
true))
{
log_error(_("unable to create \"recovery.conf\""));
if (PQserverVersion(upstream_conn) >= 120000)
{
log_error(_("unable to write replication configuration to \"postgresql.auto.conf\""));
}
else
{
log_error(_("unable to create \"recovery.conf\""));
}
}
else
{
log_notice(_("\"recovery.conf\" created as \"%s\""), recovery_file_path);
if (PQserverVersion(upstream_conn) >= 120000)
{
log_notice(_("replication configuration written to \"postgresql.auto.conf\""));
}
else
{
log_notice(_("\"recovery.conf\" created as \"%s\""), recovery_file_path);
}
if (node_is_running == true)
{
@@ -1233,6 +1279,23 @@ _do_create_recovery_conf(void)
}
}
/* Pg12 and later: add standby.signal, if not already there */
if (PQserverVersion(upstream_conn) >= 120000)
{
if (runtime_options.dry_run == true)
{
log_info(_("would write \"standby.signal\" file"));
}
else
{
if (write_standby_signal() == false)
{
log_error(_("unable to write \"standby.signal\" file"));
}
}
}
/* add replication slot, if required */
if (slot_creation_required == true)
{
@@ -2784,12 +2847,6 @@ do_standby_follow(void)
PQfinish(local_conn);
if (runtime_options.dry_run == true)
{
log_info(_("prerequisites for executing STANDBY FOLLOW are met"));
exit(SUCCESS);
}
/*
* Here we'll need a connection to the primary, if the upstream is not a primary.
*/
@@ -2802,12 +2859,30 @@ do_standby_follow(void)
primary_conn = get_primary_connection_quiet(follow_target_conn,
&primary_node_id,
NULL);
/*
* If follow target is not primary and no other primary could be found,
* abort because we won't be able to update the node record.
*/
if (PQstatus(primary_conn) != CONNECTION_OK)
{
log_error(_("unable to determine the cluster primary"));
log_detail(_("an active primary node is required for \"repmgr standby follow\""));
PQfinish(follow_target_conn);
exit(ERR_FOLLOW_FAIL);
}
}
else
{
primary_conn = follow_target_conn;
}
if (runtime_options.dry_run == true)
{
log_info(_("prerequisites for executing STANDBY FOLLOW are met"));
exit(SUCCESS);
}
initPQExpBuffer(&follow_output);
success = do_standby_follow_internal(
@@ -2991,57 +3066,22 @@ do_standby_follow_internal(PGconn *primary_conn, PGconn *follow_target_conn, t_n
}
}
/* Initialise connection parameters to write as `primary_conninfo` */
initialize_conninfo_params(&recovery_conninfo, false);
/* We ignore any application_name set in the primary's conninfo */
parse_conninfo_string(follow_target_node_record->conninfo, &recovery_conninfo, &errmsg, true);
/*
* store the original upstream node id so we can delete the
* replication slot, if exists
*/
if (local_node_record.upstream_node_id != UNKNOWN_NODE_ID)
{
t_conninfo_param_list local_node_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
bool parse_success;
original_upstream_node_id = local_node_record.upstream_node_id;
}
else
{
original_upstream_node_id = follow_target_node_record->node_id;
}
initialize_conninfo_params(&local_node_conninfo, false);
parse_success = parse_conninfo_string(local_node_record.conninfo, &local_node_conninfo, &errmsg, false);
if (parse_success == false)
{
/*
* this shouldn't happen, but if it does we'll plough on
* regardless
*/
log_warning(_("unable to parse conninfo string \"%s\":\n %s"),
local_node_record.conninfo, errmsg);
}
else
{
char *application_name = param_get(&local_node_conninfo, "application_name");
if (application_name != NULL && strlen(application_name))
param_set(&recovery_conninfo, "application_name", application_name);
}
free_conninfo_params(&local_node_conninfo);
/*
* store the original upstream node id so we can delete the
* replication slot, if exists
*/
if (local_node_record.upstream_node_id != UNKNOWN_NODE_ID)
{
original_upstream_node_id = local_node_record.upstream_node_id;
}
else
{
original_upstream_node_id = follow_target_node_record->node_id;
}
if (config_file_options.use_replication_slots && runtime_options.host_param_provided == false && original_upstream_node_id != UNKNOWN_NODE_ID)
{
remove_old_replication_slot = true;
}
if (config_file_options.use_replication_slots && runtime_options.host_param_provided == false && original_upstream_node_id != UNKNOWN_NODE_ID)
{
remove_old_replication_slot = true;
}
/* Fetch original upstream's record */
@@ -3066,6 +3106,12 @@ do_standby_follow_internal(PGconn *primary_conn, PGconn *follow_target_conn, t_n
}
}
/* Initialise connection parameters to write as "primary_conninfo" */
initialize_conninfo_params(&recovery_conninfo, false);
/* We ignore any application_name set in the primary's conninfo */
parse_conninfo_string(follow_target_node_record->conninfo, &recovery_conninfo, &errmsg, true);
/* Set the application name to this node's name */
param_set(&recovery_conninfo, "application_name", config_file_options.node_name);
@@ -3075,7 +3121,11 @@ do_standby_follow_internal(PGconn *primary_conn, PGconn *follow_target_conn, t_n
log_notice(_("setting node %i's upstream to node %i"),
config_file_options.node_id, follow_target_node_record->node_id);
if (!create_recovery_file(&local_node_record, &recovery_conninfo, config_file_options.data_directory, true))
if (!create_recovery_file(&local_node_record,
&recovery_conninfo,
PQserverVersion(primary_conn),
config_file_options.data_directory,
true))
{
*error_code = general_error_code;
return false;
@@ -4732,7 +4782,7 @@ do_standby_switchover(void)
log_warning(_("unable to unpause repmgrd on %i node(s)"),
error_node_count);
log_detail(_("errors encountered for following node(s):\n%s"), detail.data);
log_hint(_("check node connection and status; unpause manually with \"repmgr daemon unpause\""));
log_hint(_("check node connection and status; unpause manually with \"repmgr service unpause\""));
termPQExpBuffer(&detail);
}
@@ -4769,6 +4819,7 @@ check_source_server()
t_node_info upstream_node_record = T_NODE_INFO_INITIALIZER;
RecordStatus record_status = RECORD_NOT_FOUND;
ExtensionStatus extension_status = REPMGR_UNKNOWN;
t_extension_versions extversions = T_EXTENSION_VERSIONS_INITIALIZER;
/* Attempt to connect to the upstream server to verify its configuration */
log_verbose(LOG_DEBUG, "check_source_server()");
@@ -4832,7 +4883,7 @@ check_source_server()
* to be used as a standalone clone tool)
*/
extension_status = get_repmgr_extension_status(primary_conn, NULL);
extension_status = get_repmgr_extension_status(primary_conn, &extversions);
if (extension_status != REPMGR_INSTALLED)
{
@@ -4847,20 +4898,25 @@ check_source_server()
exit(ERR_DB_QUERY);
}
/* schema doesn't exist */
log_error(_("repmgr extension not found on source node"));
if (extension_status == REPMGR_AVAILABLE)
{
log_detail(_("repmgr extension is available but not installed in database \"%s\""),
log_error(_("repmgr extension is available but not installed in database \"%s\""),
param_get(&source_conninfo, "dbname"));
log_hint(_("check that you are cloning from the database where \"repmgr\" is installed"));
}
else if (extension_status == REPMGR_UNAVAILABLE)
{
log_detail(_("repmgr extension is not available on the upstream node"));
log_error(_("repmgr extension is not available on the upstream node"));
}
else if (extension_status == REPMGR_OLD_VERSION_INSTALLED)
{
log_error(_("an older version of the extension is installed on the upstream node"));
log_detail(_("version %s is installed but newer version %s is available"),
extversions.installed_version,
extversions.default_version);
log_hint(_("upgrade \"repmgr\" on the source node first"));
}
log_hint(_("check that the upstream node is part of a repmgr cluster"));
PQfinish(source_conn);
exit(ERR_BAD_CONFIG);
}
@@ -4885,6 +4941,13 @@ check_source_server()
* later, as this is a precautionary check and we can retrieve the system
* identifier with a normal connection.
*/
if (runtime_options.dry_run == true)
{
log_info(_("\"repmgr\" extension is installed in database \"%s\""),
param_get(&source_conninfo, "dbname"));
}
if (get_recovery_type(source_conn) == RECTYPE_PRIMARY && PQserverVersion(source_conn) >= 90600)
{
uint64 source_system_identifier = system_identifier(source_conn);
@@ -6663,9 +6726,8 @@ make_barman_ssh_command(char *buf)
static int
get_tablespace_data_barman
(char *tablespace_data_barman,
TablespaceDataList *tablespace_list)
get_tablespace_data_barman(char *tablespace_data_barman,
TablespaceDataList *tablespace_list)
{
/*
* Example: [('main', 24674, '/var/lib/postgresql/tablespaces/9.5/main'),
@@ -6939,71 +7001,55 @@ check_recovery_type(PGconn *conn)
/*
* Creates a recovery.conf file for a standby
* Creates recovery configuration for a standby.
*
* A database connection pointer is required for escaping primary_conninfo
* parameters. When cloning from Barman and --no-upstream-connection ) this
* might not be available.
* parameters. When cloning from Barman and --no-upstream-connection supplied,
* this might not be available.
*/
bool
create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, char *dest, bool as_file)
static bool
create_recovery_file(t_node_info *node_record, t_conninfo_param_list *primary_conninfo, int server_version_num, char *dest, bool as_file)
{
PQExpBufferData recovery_file_buf;
PQExpBufferData primary_conninfo_buf;
char recovery_file_path[MAXPGPATH] = "";
FILE *recovery_file;
mode_t um;
/* create file in buffer */
initPQExpBuffer(&recovery_file_buf);
KeyValueList recovery_config = {NULL, NULL};
KeyValueListCell *cell = NULL;
/* standby_mode = 'on' */
appendPQExpBufferStr(&recovery_file_buf,
"standby_mode = 'on'\n");
initPQExpBuffer(&primary_conninfo_buf);
/* standby_mode = 'on' (Pg 11 and earlier) */
if (server_version_num < 120000)
{
key_value_list_set(&recovery_config,
"standby_mode", "on");
}
/* primary_conninfo = '...' */
/*
* the user specified --upstream-conninfo string - copy that
*/
if (strlen(runtime_options.upstream_conninfo))
{
char *escaped = escape_recovery_conf_value(runtime_options.upstream_conninfo);
appendPQExpBuffer(&recovery_file_buf,
"primary_conninfo = '%s'\n",
escaped);
free(escaped);
}
/*
* otherwise use the conninfo inferred from the upstream connection and/or
* node record
*/
else
{
write_primary_conninfo(&recovery_file_buf, recovery_conninfo);
}
write_primary_conninfo(&primary_conninfo_buf, primary_conninfo);
key_value_list_set(&recovery_config,
"primary_conninfo", primary_conninfo_buf.data);
/* recovery_target_timeline = 'latest' */
appendPQExpBufferStr(&recovery_file_buf,
"recovery_target_timeline = 'latest'\n");
key_value_list_set(&recovery_config,
"recovery_target_timeline", "latest");
/* recovery_min_apply_delay = ... (optional) */
if (config_file_options.recovery_min_apply_delay_provided == true)
{
appendPQExpBuffer(&recovery_file_buf,
"recovery_min_apply_delay = %s\n",
config_file_options.recovery_min_apply_delay);
key_value_list_set(&recovery_config,
"recovery_min_apply_delay", config_file_options.recovery_min_apply_delay);
}
/* primary_slot_name = '...' (optional, for 9.4 and later) */
if (config_file_options.use_replication_slots)
{
appendPQExpBuffer(&recovery_file_buf,
"primary_slot_name = %s\n",
node_record->slot_name);
key_value_list_set(&recovery_config,
"primary_slot_name", node_record->slot_name);
}
/*
@@ -7014,9 +7060,8 @@ create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_c
{
char *escaped = escape_recovery_conf_value(config_file_options.restore_command);
appendPQExpBuffer(&recovery_file_buf,
"restore_command = '%s'\n",
escaped);
key_value_list_set(&recovery_config,
"restore_command", escaped);
free(escaped);
}
@@ -7024,33 +7069,84 @@ create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_c
if (config_file_options.archive_cleanup_command[0] != '\0')
{
char *escaped = escape_recovery_conf_value(config_file_options.archive_cleanup_command);
appendPQExpBuffer(&recovery_file_buf,
"archive_cleanup_command = '%s'\n",
escaped);
key_value_list_set(&recovery_config,
"archive_cleanup_command", escaped);
free(escaped);
}
if (as_file == true)
if (as_file == false)
{
maxpath_snprintf(recovery_file_path, "%s/%s", dest, RECOVERY_COMMAND_FILE);
log_debug("create_recovery_file(): creating \"%s\"...",
recovery_file_path);
/* create file in buffer */
initPQExpBuffer(&recovery_file_buf);
/* Set umask to 0600 */
um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO));
recovery_file = fopen(recovery_file_path, "w");
umask(um);
if (recovery_file == NULL)
for (cell = recovery_config.head; cell; cell = cell->next)
{
log_error(_("unable to create recovery.conf file at \"%s\""),
recovery_file_path);
log_detail("%s", strerror(errno));
appendPQExpBuffer(&recovery_file_buf,
"%s = '%s'\n",
cell->key, cell->value);
}
maxlen_snprintf(dest, "%s", recovery_file_buf.data);
termPQExpBuffer(&recovery_file_buf);
return true;
}
/*
* PostgreSQL 12 and later: modify postgresql.auto.conf
*
*/
if (server_version_num >= 120000)
{
if (modify_auto_conf(dest, &recovery_config) == false)
{
return false;
}
log_debug("recovery file is:\n%s", recovery_file_buf.data);
if (write_standby_signal() == false)
{
return false;
}
return true;
}
/*
* PostgreSQL 11 and earlier: write recovery.conf
*/
maxpath_snprintf(recovery_file_path, "%s/%s", dest, RECOVERY_COMMAND_FILE);
log_debug("create_recovery_file(): creating \"%s\"...",
recovery_file_path);
/* Set umask to 0600 */
um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO));
recovery_file = fopen(recovery_file_path, "w");
umask(um);
if (recovery_file == NULL)
{
log_error(_("unable to create recovery.conf file at \"%s\""),
recovery_file_path);
log_detail("%s", strerror(errno));
return false;
}
for (cell = recovery_config.head; cell; cell = cell->next)
{
initPQExpBuffer(&recovery_file_buf);
appendPQExpBuffer(&recovery_file_buf,
"%s = '%s'\n",
cell->key, cell->value);
log_debug("recovery.conf line: %s", recovery_file_buf.data);
if (fputs(recovery_file_buf.data, recovery_file) == EOF)
{
@@ -7060,14 +7156,58 @@ create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_c
return false;
}
fclose(recovery_file);
}
else
{
maxlen_snprintf(dest, "%s", recovery_file_buf.data);
termPQExpBuffer(&recovery_file_buf);
}
termPQExpBuffer(&recovery_file_buf);
fclose(recovery_file);
return true;
}
/*
* create standby.signal (PostgreSQL 12 and later)
*/
static bool
write_standby_signal(void)
{
char standby_signal_file_path[MAXPGPATH] = "";
FILE *file;
mode_t um;
snprintf(standby_signal_file_path, MAXPGPATH,
"%s/%s",
config_file_options.data_directory,
STANDBY_SIGNAL_FILE);
/* 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;
}
@@ -7157,8 +7297,7 @@ write_primary_conninfo(PQExpBufferData *dest, t_conninfo_param_list *param_list)
escaped = escape_recovery_conf_value(conninfo_buf.data);
appendPQExpBuffer(dest,
"primary_conninfo = '%s'\n", escaped);
appendPQExpBufferStr(dest, escaped);
free(escaped);
free_conninfo_params(&env_conninfo);

View File

@@ -260,5 +260,6 @@ extern void drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slo
extern bool check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *follow_target_conn, t_node_info *follow_target_node_record, bool is_rejoin);
extern void check_shared_library(PGconn *conn);
extern bool is_repmgrd_running(PGconn *conn);
extern int parse_repmgr_version(const char *version_string);
#endif /* _REPMGR_CLIENT_GLOBAL_H_ */

View File

@@ -33,9 +33,10 @@
* NODE SERVICE
* NODE CONTROL
*
* DAEMON STATUS
* DAEMON PAUSE
* DAEMON UNPAUSE
* SERVICE STATUS
* SERVICE PAUSE
* SERVICE UNPAUSE
*
* DAEMON START
* DAEMON STOP
*
@@ -57,6 +58,7 @@
#include <sys/stat.h>
#include <signal.h>
#include "repmgr.h"
#include "compat.h"
#include "controldata.h"
@@ -68,11 +70,11 @@
#include "repmgr-action-bdr.h"
#include "repmgr-action-node.h"
#include "repmgr-action-cluster.h"
#include "repmgr-action-service.h"
#include "repmgr-action-daemon.h"
#include <storage/fd.h> /* for PG_TEMP_FILE_PREFIX */
/* globally available variables *
* ============================ */
@@ -184,7 +186,7 @@ main(int argc, char **argv)
strncpy(runtime_options.username, pw->pw_name, MAXLEN);
}
/* Make getopt emitting errors */
/* Make getopt emit errors */
opterr = 1;
while ((c = getopt_long(argc, argv, "?Vb:f:FwWd:h:p:U:R:S:D:ck:L:qtvC:", long_options,
@@ -669,33 +671,20 @@ main(int argc, char **argv)
break;
/*-----------------------------
* options deprecated since 3.3
* options deprecated since 4.0
*-----------------------------
*/
case OPT_CHECK_UPSTREAM_CONFIG:
item_list_append(&cli_warnings,
_("--check-upstream-config is deprecated; use --dry-run instead"));
break;
case OPT_DATA_DIR:
item_list_append(&cli_warnings,
_("--data-dir is deprecated; use -D/--pgdata instead"));
break;
case OPT_NO_CONNINFO_PASSWORD:
item_list_append(&cli_warnings,
_("--no-conninfo-password is deprecated; use --use-recovery-conninfo-password to explicitly set a password"));
break;
/* -C/--remote-config-file */
case 'C':
item_list_append(&cli_warnings,
_("--remote-config-file is no longer required"));
break;
/* --recovery-min-apply-delay */
case OPT_RECOVERY_MIN_APPLY_DELAY:
item_list_append(&cli_warnings,
_("--recovery-min-apply-delay is now a configuration file parameter, \"recovery_min_apply_delay\""));
break;
case ':': /* missing option argument */
option_error_found = true;
break;
@@ -817,7 +806,6 @@ main(int argc, char **argv)
exit_with_cli_errors(&cli_errors, NULL);
}
/*----------
* Determine the node type and action; following are valid:
*
@@ -827,7 +815,7 @@ main(int argc, char **argv)
* BDR { REGISTER | UNREGISTER } |
* NODE { STATUS | CHECK | REJOIN | SERVICE } |
* CLUSTER { CROSSCHECK | MATRIX | SHOW | EVENT | CLEANUP }
* DAEMON { STATUS | PAUSE | UNPAUSE | START | STOP }
* SERVICE { STATUS | PAUSE | UNPAUSE | START | STOP }
*
* [node] is an optional hostname, provided instead of the -h/--host
* option
@@ -966,6 +954,22 @@ main(int argc, char **argv)
else if (strcasecmp(repmgr_action, "CLEANUP") == 0)
action = CLUSTER_CLEANUP;
}
else if (strcasecmp(repmgr_command, "SERVICE") == 0)
{
if (help_option == true)
{
do_service_help();
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "STATUS") == 0)
action = SERVICE_STATUS;
else if (strcasecmp(repmgr_action, "PAUSE") == 0)
action = SERVICE_PAUSE;
else if (strcasecmp(repmgr_action, "UNPAUSE") == 0)
action = SERVICE_UNPAUSE;
}
else if (strcasecmp(repmgr_command, "DAEMON") == 0)
{
if (help_option == true)
@@ -974,16 +978,18 @@ main(int argc, char **argv)
exit(SUCCESS);
}
if (strcasecmp(repmgr_action, "STATUS") == 0)
action = DAEMON_STATUS;
else if (strcasecmp(repmgr_action, "PAUSE") == 0)
action = DAEMON_PAUSE;
else if (strcasecmp(repmgr_action, "UNPAUSE") == 0)
action = DAEMON_UNPAUSE;
else if (strcasecmp(repmgr_action, "START") == 0)
if (strcasecmp(repmgr_action, "START") == 0)
action = DAEMON_START;
else if (strcasecmp(repmgr_action, "STOP") == 0)
action = DAEMON_STOP;
/* allow "daemon" as an alias for "service" for repmgr 4.x compatibility */
if (strcasecmp(repmgr_action, "STATUS") == 0)
action = SERVICE_STATUS;
else if (strcasecmp(repmgr_action, "PAUSE") == 0)
action = SERVICE_PAUSE;
else if (strcasecmp(repmgr_action, "UNPAUSE") == 0)
action = SERVICE_UNPAUSE;
}
else
{
@@ -1386,16 +1392,18 @@ main(int argc, char **argv)
do_cluster_cleanup();
break;
/* SERVICE */
case SERVICE_STATUS:
do_service_status();
break;
case SERVICE_PAUSE:
do_service_pause();
break;
case SERVICE_UNPAUSE:
do_service_unpause();
break;
/* DAEMON */
case DAEMON_STATUS:
do_daemon_status();
break;
case DAEMON_PAUSE:
do_daemon_pause();
break;
case DAEMON_UNPAUSE:
do_daemon_unpause();
break;
case DAEMON_START:
do_daemon_start();
break;
@@ -1906,8 +1914,9 @@ check_cli_parameters(const int action)
case WITNESS_UNREGISTER:
case NODE_REJOIN:
case NODE_SERVICE:
case DAEMON_PAUSE:
case DAEMON_UNPAUSE:
case SERVICE_PAUSE:
case SERVICE_UNPAUSE:
case SERVICE_STATUS:
case DAEMON_START:
case DAEMON_STOP:
break;
@@ -1946,7 +1955,7 @@ check_cli_parameters(const int action)
{
case CLUSTER_SHOW:
case CLUSTER_EVENT:
case DAEMON_STATUS:
case SERVICE_STATUS:
break;
default:
item_list_append_format(&cli_warnings,
@@ -1960,7 +1969,7 @@ check_cli_parameters(const int action)
{
switch (action)
{
case DAEMON_STATUS:
case SERVICE_STATUS:
break;
default:
item_list_append_format(&cli_warnings,
@@ -2010,7 +2019,7 @@ check_cli_parameters(const int action)
/*
* Generate formatted node status output for display by "cluster show" and
* "daemon status".
* "service status".
*/
bool
format_node_status(t_node_info *node_info, PQExpBufferData *node_status, PQExpBufferData *upstream, ItemList *warnings)
@@ -2476,12 +2485,13 @@ action_name(const int action)
case CLUSTER_CROSSCHECK:
return "CLUSTER CROSSCHECK";
case DAEMON_STATUS:
return "DAEMON STATUS";
case DAEMON_PAUSE:
return "DAEMON PAUSE";
case DAEMON_UNPAUSE:
return "DAEMON UNPAUSE";
case SERVICE_STATUS:
return "SERVICE STATUS";
case SERVICE_PAUSE:
return "SERVICE PAUSE";
case SERVICE_UNPAUSE:
return "SERVICE UNPAUSE";
case DAEMON_START:
return "DAEMON START";
case DAEMON_STOP:
@@ -2596,11 +2606,12 @@ do_help(void)
printf(_(" %s [OPTIONS] node {status|check|rejoin|service}\n"), progname());
printf(_(" %s [OPTIONS] cluster {show|event|matrix|crosscheck|cleanup}\n"), progname());
printf(_(" %s [OPTIONS] witness {register|unregister}\n"), progname());
printf(_(" %s [OPTIONS] daemon {status|pause|unpause|start|stop}\n"), progname());
printf(_(" %s [OPTIONS] service {status|pause|unpause}\n"), progname());
printf(_(" %s [OPTIONS] daemon {start|stop}\n"), progname());
puts("");
printf(_(" Execute \"%s {primary|standby|bdr|node|cluster|witness|daemon} --help\" to see command-specific options\n"), progname());
printf(_(" Execute \"%s {primary|standby|bdr|node|cluster|witness|service} --help\" to see command-specific options\n"), progname());
puts("");
@@ -3029,19 +3040,19 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
if (*config_file_options.rsync_options == '\0')
{
appendPQExpBuffer(&rsync_flags, "%s",
"--archive --checksum --compress --progress --rsh=ssh");
appendPQExpBufferStr(&rsync_flags,
"--archive --checksum --compress --progress --rsh=ssh");
}
else
{
appendPQExpBuffer(&rsync_flags, "%s",
config_file_options.rsync_options);
appendPQExpBufferStr(&rsync_flags,
config_file_options.rsync_options);
}
if (runtime_options.force)
{
appendPQExpBuffer(&rsync_flags, "%s",
" --delete --checksum");
appendPQExpBufferStr(&rsync_flags,
" --delete --checksum");
}
if (!remote_user[0])
@@ -3067,11 +3078,11 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
if (is_directory)
{
/* Files which we don't want */
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=postmaster.pid --exclude=postmaster.opts --exclude=global/pg_control");
appendPQExpBufferStr(&rsync_flags,
" --exclude=postmaster.pid --exclude=postmaster.opts --exclude=global/pg_control");
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=recovery.conf --exclude=recovery.done");
appendPQExpBufferStr(&rsync_flags,
" --exclude=recovery.conf --exclude=recovery.done");
if (server_version_num >= 90400)
{
@@ -3079,8 +3090,8 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
* Ideally we'd use PG_AUTOCONF_FILENAME from utils/guc.h, but
* that has too many dependencies for a mere client program.
*/
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=postgresql.auto.conf.tmp");
appendPQExpBuffer(&rsync_flags, " --exclude=%s.tmp",
PG_AUTOCONF_FILENAME);
}
/* Temporary files which we don't want, if they exist */
@@ -3091,17 +3102,17 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
if (server_version_num >= 100000)
{
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=pg_wal/*");
appendPQExpBufferStr(&rsync_flags,
" --exclude=pg_wal/*");
}
else
{
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=pg_xlog/*");
appendPQExpBufferStr(&rsync_flags,
" --exclude=pg_xlog/*");
}
appendPQExpBuffer(&rsync_flags, "%s",
" --exclude=pg_log/* --exclude=pg_stat_tmp/*");
appendPQExpBufferStr(&rsync_flags,
" --exclude=pg_log/* --exclude=pg_stat_tmp/*");
maxlen_snprintf(script, "rsync %s %s:%s/* %s",
rsync_flags.data, host_string, remote_path, local_path);
@@ -3156,6 +3167,18 @@ make_remote_repmgr_path(PQExpBufferData *output_buf, t_node_info *remote_node_re
"%s -f %s ",
progname(),
remote_node_record->config_file);
/*
* If --log-level was explicitly supplied, pass that through
* to the remote repmgr client too.
*/
if (runtime_options.log_level[0] != '\0')
{
appendPQExpBuffer(output_buf,
" -L %s ",
runtime_options.log_level);
}
}
@@ -3869,3 +3892,44 @@ is_repmgrd_running(PGconn *conn)
return is_running;
}
/**
* Parse the string returned by "repmgr --version", e.g. "repmgr 4.1.2",
* and return it as a version integer (e.g. 40102).
*
* This is required for backwards compatibility as versions prior to
* 4.3 do not have the --version-number option.
*/
int
parse_repmgr_version(const char *version_string)
{
int series, major, minor;
int version_integer = UNKNOWN_REPMGR_VERSION_NUM;
PQExpBufferData sscanf_string;
initPQExpBuffer(&sscanf_string);
appendPQExpBuffer(&sscanf_string, "%s ",
progname());
appendPQExpBufferStr(&sscanf_string, "%i.%i.%i");
if (sscanf(version_string, sscanf_string.data, &series, &major, &minor) == 3)
{
version_integer = (series * 10000) + (major * 100) + minor;
}
else
{
resetPQExpBuffer(&sscanf_string);
appendPQExpBuffer(&sscanf_string, "%s ",
progname());
appendPQExpBufferStr(&sscanf_string, "%i.%i");
if (sscanf(version_string, "repmgr %i.%i", &series, &major) == 2)
{
version_integer = (series * 10000) + (major * 100);
}
}
return version_integer;
}

View File

@@ -46,9 +46,9 @@
#define CLUSTER_MATRIX 20
#define CLUSTER_CROSSCHECK 21
#define CLUSTER_EVENT 22
#define DAEMON_STATUS 23
#define DAEMON_PAUSE 24
#define DAEMON_UNPAUSE 25
#define SERVICE_STATUS 23
#define SERVICE_PAUSE 24
#define SERVICE_UNPAUSE 25
#define DAEMON_START 26
#define DAEMON_STOP 27
@@ -101,13 +101,9 @@
#define OPT_DETAIL 1046
#define OPT_REPMGRD_FORCE_UNPAUSE 1047
/* deprecated since 3.3 */
#define OPT_DATA_DIR 999
#define OPT_NO_CONNINFO_PASSWORD 998
#define OPT_RECOVERY_MIN_APPLY_DELAY 997
/* deprecated since 4.0 */
#define OPT_CHECK_UPSTREAM_CONFIG 996
#define OPT_NODE 995
#define OPT_CHECK_UPSTREAM_CONFIG 999
#define OPT_NODE 998
static struct option long_options[] =
@@ -157,7 +153,6 @@ static struct option long_options[] =
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
{"fast-checkpoint", no_argument, NULL, 'c'},
{"no-upstream-connection", no_argument, NULL, OPT_NO_UPSTREAM_CONNECTION},
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY},
{"replication-user", required_argument, NULL, OPT_REPLICATION_USER},
{"upstream-conninfo", required_argument, NULL, OPT_UPSTREAM_CONNINFO},
{"upstream-node-id", required_argument, NULL, OPT_UPSTREAM_NODE_ID},
@@ -215,11 +210,8 @@ static struct option long_options[] =
/* deprecated */
{"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG},
{"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD},
/* previously used by "standby switchover" */
{"remote-config-file", required_argument, NULL, 'C'},
/* legacy alias for -D/--pgdata */
{"data-dir", required_argument, NULL, OPT_DATA_DIR},
/* replaced by --node-id */
{"node", required_argument, NULL, OPT_NODE},

View File

@@ -702,7 +702,7 @@ set_repmgrd_pid(PG_FUNCTION_ARGS)
shared_state->repmgrd_pid = repmgrd_pid;
memset(shared_state->repmgrd_pidfile, 0, MAXPGPATH);
if(repmgrd_pidfile != NULL)
if (repmgrd_pidfile != NULL)
{
strncpy(shared_state->repmgrd_pidfile, repmgrd_pidfile, MAXPGPATH);
}

View File

@@ -6,12 +6,13 @@
# is noted for each item. Where no default value is shown, the
# parameter will be treated as empty or false.
#
# IMPORTANT: string values can be provided as-is, or enclosed in single quotes
# (but not double-quotes, which will be interpreted as part of the string),
# e.g.:
# repmgr parses its configuration file in the same way as PostgreSQL itself
# does. In particular, strings must be enclosed in single quotes (although
# simple identifiers may be provided as-is).
#
# node_name=foo
# node_name = 'foo'
# For details on the configuration file format see the documentation at:
#
# https://repmgr.org/docs/current/configuration-file.html#CONFIGURATION-FILE-FORMAT
#
# =============================================================================
# Required configuration items
@@ -51,7 +52,6 @@
# =============================================================================
# Optional configuration items
# =============================================================================
@@ -68,16 +68,16 @@
# Replication settings
#------------------------------------------------------------------------------
#replication_user='repmgr' # User to make replication connections with, if not set defaults
# to the user defined in "conninfo".
#replication_user='repmgr' # User to make replication connections with, if not set
# defaults to the user defined in "conninfo".
#replication_type=physical # Must be one of 'physical' or 'bdr'.
#replication_type='physical' # Must be one of "physical" or "bdr".
# NOTE: "bdr" can only be used with BDR 2.x
#location=default # arbitrary string defining the location of the node; this
# is used during failover to check visibilty of the
# current primary node. See the 'repmgrd' documentation
# in README.md for further details.
#location='default' # An arbitrary string defining the location of the node; this
# is used during failover to check visibility of the
# current primary node. For further details see:
# https://repmgr.org/docs/current/repmgrd-network-split.html
#use_replication_slots=no # whether to use physical replication slots
# NOTE: when using replication slots,
@@ -102,10 +102,10 @@
# This is mainly intended for those cases when `repmgr` is executed directly
# by `repmgrd`.
#log_level=INFO # Log level: possible values are DEBUG, INFO, NOTICE,
#log_level='INFO' # Log level: possible values are DEBUG, INFO, NOTICE,
# WARNING, ERROR, ALERT, CRIT or EMERG
#log_facility=STDERR # Logging facility: possible values are STDERR, or for
#log_facility='STDERR' # Logging facility: possible values are STDERR, or for
# syslog integration, one of LOCAL0, LOCAL1, ..., LOCAL7, USER
#log_file='' # STDERR can be redirected to an arbitrary file
@@ -282,7 +282,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# These settings are only applied when repmgrd is running. Values shown
# are defaults.
#failover=manual # one of 'automatic', 'manual'.
#failover='manual' # one of 'automatic', 'manual'.
# determines what action to take in the event of upstream failure
#
# 'automatic': repmgrd will automatically attempt to promote the
@@ -340,7 +340,7 @@ 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.
#failover_validation_command= # Script to execute for an external mechanism to validate the failover
#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
# value: %n (node_id), %a (node_name). *Must* be the same on all nodes.
@@ -355,7 +355,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
#child_nodes_disconnect_min_count=-1 # Minimum number of disconnected child nodes required to execute disconnection command
# (ignored if "child_nodes_connected_min_count" set)
#child_nodes_disconnect_timeout=30 # Interval between child node disconnection and disconnection command execution
#child_nodes_disconnect_command= # Command to execute if child node disconnection detected
#child_nodes_disconnect_command='' # Command to execute if child node disconnection detected
#------------------------------------------------------------------------------
# service control commands
@@ -403,7 +403,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# for "promote_command"; do not use "repmgr standby promote"
# (or a script which executes "repmgr standby promote") here.
# Used by "repmgr daemon (start|stop)" to control repmgrd
# Used by "repmgr service (start|stop)" to control repmgrd
#
#repmgrd_service_start_command = ''
#repmgrd_service_stop_command = ''

View File

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

View File

@@ -21,6 +21,37 @@
#include <config.h>
#endif
#ifdef vsnprintf
#undef vsnprintf
#endif
#ifdef snprintf
#undef snprintf
#endif
#ifdef vsprintf
#undef vsprintf
#endif
#ifdef sprintf
#undef sprintf
#endif
#ifdef vfprintf
#undef vfprintf
#endif
#ifdef fprintf
#undef fprintf
#endif
#ifdef vprintf
#undef vprintf
#endif
#ifdef printf
#undef printf
#endif
#ifdef strerror
#undef strerror
#endif
#ifdef strerror_r
#undef strerror_r
#endif
#ifndef _REPMGR_H_
#define _REPMGR_H_
@@ -51,6 +82,7 @@
#define UNKNOWN_SERVER_VERSION_NUM -1
#define UNKNOWN_BDR_VERSION_NUM -1
#define UNKNOWN_REPMGR_VERSION_NUM -1
#define UNKNOWN_TIMELINE_ID -1
#define UNKNOWN_SYSTEM_IDENTIFIER 0
@@ -108,6 +140,11 @@
#define RECOVERY_COMMAND_FILE "recovery.conf"
#endif
#ifndef STANDBY_SIGNAL_FILE
#define STANDBY_SIGNAL_FILE "standby.signal"
#define RECOVERY_SIGNAL_FILE "recovery.signal"
#endif
#ifndef TABLESPACE_MAP
#define TABLESPACE_MAP "tablespace_map"
#endif

View File

@@ -1,3 +1,5 @@
#define REPMGR_VERSION_DATE ""
#define REPMGR_VERSION "4.4dev"
#define REPMGR_VERSION_NUM 40400
#define REPMGR_VERSION "5.0.0"
#define REPMGR_VERSION_NUM 50000
#define REPMGR_RELEASE_DATE "2019-10-15"
#define PG_ACTUAL_VERSION_NUM

View File

@@ -1580,7 +1580,7 @@ monitor_streaming_standby(void)
{
log_notice(_("repmgrd on this node is paused"));
log_detail(_("no failover will be carried out"));
log_hint(_("execute \"repmgr daemon unpause\" to resume normal failover mode"));
log_hint(_("execute \"repmgr service unpause\" to resume normal failover mode"));
monitoring_state = MS_DEGRADED;
INSTR_TIME_SET_CURRENT(degraded_monitoring_start);
}
@@ -1710,9 +1710,6 @@ monitor_streaming_standby(void)
* has been promoted
*/
NodeInfoListCell *cell;
int follow_node_id = UNKNOWN_NODE_ID;
/* local node has been promoted */
if (get_recovery_type(local_conn) == RECTYPE_PRIMARY)
{
@@ -1802,6 +1799,9 @@ monitor_streaming_standby(void)
if (sibling_nodes.node_count > 0)
{
NodeInfoListCell *cell;
t_node_info *follow_node_info = NULL;
log_debug("scanning %i node records to detect new primary...", sibling_nodes.node_count);
for (cell = sibling_nodes.head; cell; cell = cell->next)
{
@@ -1828,16 +1828,19 @@ monitor_streaming_standby(void)
if (get_recovery_type(cell->node_info->conn) == RECTYPE_PRIMARY)
{
follow_node_id = cell->node_info->node_id;
follow_node_info = cell->node_info;
close_connection(&cell->node_info->conn);
break;
}
close_connection(&cell->node_info->conn);
}
if (follow_node_id != UNKNOWN_NODE_ID)
if (follow_node_info != NULL)
{
follow_new_primary(follow_node_id);
log_info(_("node \"%s\" (node ID: %i) detected as primary"),
follow_node_info->node_name,
follow_node_info->node_id);
follow_new_primary(follow_node_info->node_id);
}
}
@@ -1881,7 +1884,7 @@ loop:
if (PQstatus(local_conn) == CONNECTION_OK && repmgrd_is_paused(local_conn))
{
log_detail(_("repmgrd paused by administrator"));
log_hint(_("execute \"repmgr daemon unpause\" to resume normal failover mode"));
log_hint(_("execute \"repmgr service unpause\" to resume normal failover mode"));
}
else
{
@@ -2083,7 +2086,6 @@ loop:
}
}
if (got_SIGHUP)
{
handle_sighup(&local_conn, STANDBY);
@@ -2093,13 +2095,34 @@ loop:
if (local_monitoring_state == MS_NORMAL && last_known_upstream_node_id != local_node_info.upstream_node_id)
{
log_notice(_("local node %i's upstream appears to have changed, restarting monitoring"),
local_node_info.node_id);
log_detail(_("currently monitoring upstream %i; new upstream is %i"),
last_known_upstream_node_id,
local_node_info.upstream_node_id);
close_connection(&upstream_conn);
return;
/*
* It's possible that after a change of upstream, the local node record will not
* yet have been updated with the new upstream node ID. Therefore we check the
* node record on the upstream, and if that matches "last_known_upstream_node_id",
* take that as the correct value.
*/
if (monitoring_state == MS_NORMAL)
{
t_node_info node_info_on_upstream = T_NODE_INFO_INITIALIZER;
record_status = get_node_record(primary_conn, config_file_options.node_id, &node_info_on_upstream);
if (last_known_upstream_node_id == node_info_on_upstream.upstream_node_id)
{
local_node_info.upstream_node_id = last_known_upstream_node_id;
}
}
if (last_known_upstream_node_id != local_node_info.upstream_node_id)
{
log_notice(_("local node %i's upstream appears to have changed, restarting monitoring"),
local_node_info.node_id);
log_detail(_("currently monitoring upstream %i; new upstream is %i"),
last_known_upstream_node_id,
local_node_info.upstream_node_id);
close_connection(&upstream_conn);
return;
}
}
log_verbose(LOG_DEBUG, "sleeping %i seconds (parameter \"monitor_interval_secs\")",
@@ -2380,8 +2403,6 @@ monitor_streaming_witness(void)
* has been promoted
*/
NodeInfoListCell *cell;
int follow_node_id = UNKNOWN_NODE_ID;
NodeInfoList sibling_nodes = T_NODE_INFO_LIST_INITIALIZER;
get_active_sibling_node_records(local_conn,
@@ -2391,6 +2412,9 @@ monitor_streaming_witness(void)
if (sibling_nodes.node_count > 0)
{
NodeInfoListCell *cell;
t_node_info *follow_node_info = NULL;
log_debug("scanning %i node records to detect new primary...", sibling_nodes.node_count);
for (cell = sibling_nodes.head; cell; cell = cell->next)
{
@@ -2416,18 +2440,22 @@ monitor_streaming_witness(void)
if (get_recovery_type(cell->node_info->conn) == RECTYPE_PRIMARY)
{
follow_node_id = cell->node_info->node_id;
follow_node_info = cell->node_info;
close_connection(&cell->node_info->conn);
break;
}
close_connection(&cell->node_info->conn);
}
if (follow_node_id != UNKNOWN_NODE_ID)
if (follow_node_info != NULL)
{
witness_follow_new_primary(follow_node_id);
log_info(_("node \"%s\" (node ID: %i) detected as primary"),
follow_node_info->node_name,
follow_node_info->node_id);
witness_follow_new_primary(follow_node_info->node_id);
}
}
clear_node_info_list(&sibling_nodes);
}
}
@@ -3594,6 +3622,10 @@ follow_new_primary(int new_primary_id)
return FAILOVER_STATE_FOLLOW_FAIL;
}
log_notice(_("attempting to follow new primary \"%s\" (node ID: %i)"),
new_primary.node_name,
new_primary_id);
record_status = get_node_record(local_conn, local_node_info.upstream_node_id, &failed_primary);
if (record_status != RECORD_FOUND)
@@ -4909,7 +4941,8 @@ check_node_can_follow(PGconn *local_conn, XLogRecPtr local_xlogpos, PGconn *foll
*/
if (local_xlogpos > follow_target_history->end)
{
log_error(_("this node cannot attach to follow target node %i"),
log_error(_("this node cannot attach to follow target node \"%s\" (ID: %i)"),
follow_target_node_info->node_name,
follow_target_node_info->node_id);
can_follow = false;
@@ -4921,8 +4954,10 @@ check_node_can_follow(PGconn *local_conn, XLogRecPtr local_xlogpos, PGconn *foll
if (can_follow == true)
{
log_info(_("local node %i can attach to follow target node %i"),
log_info(_("local node \"%s\" (ID: %i) can attach to follow target node \"%s\" (ID: %i)"),
config_file_options.node_name,
config_file_options.node_id,
follow_target_node_info->node_name,
follow_target_node_info->node_id);
log_detail(_("local node's recovery point: %X/%X; follow target node's fork point: %X/%X"),

View File

@@ -29,6 +29,9 @@ static int
xvsnprintf(char *str, size_t size, const char *format, va_list ap)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 0)));
static void
_key_value_list_set(KeyValueList *item_list, bool replace, const char *key, const char *value);
static int
xvsnprintf(char *str, size_t size, const char *format, va_list ap)
{
@@ -164,16 +167,74 @@ item_list_free(ItemList *item_list)
void
key_value_list_set(KeyValueList *item_list, const char *key, const char *value)
{
key_value_list_set_format(item_list, key, "%s", value);
_key_value_list_set(item_list, false, key, value);
return;
}
void
key_value_list_set_format(KeyValueList *item_list, const char *key, const char *value,...)
key_value_list_replace_or_set(KeyValueList *item_list, const char *key, const char *value)
{
_key_value_list_set(item_list, true, key, value);
return;
}
void
key_value_list_set_format(KeyValueList *item_list, const char *key, const char *value, ...)
{
va_list arglist;
char formatted_value[MAXLEN];
va_start(arglist, value);
(void) xvsnprintf(formatted_value, MAXLEN, value, arglist);
va_end(arglist);
return _key_value_list_set(item_list, false, key, formatted_value);
}
static void
_key_value_list_set(KeyValueList *item_list, bool replace, const char *key, const char *value)
{
KeyValueListCell *cell = NULL;
va_list arglist;
int keylen = 0;
int vallen = 0;
if (replace == true)
{
KeyValueListCell *prev_cell = NULL;
KeyValueListCell *next_cell = NULL;
for (cell = item_list->head; cell; cell = next_cell)
{
next_cell = cell->next;
if (strcmp(cell->key, key) == 0)
{
if (item_list->head == cell)
item_list->head = cell->next;
if (prev_cell)
{
prev_cell->next = cell->next;
if (item_list->tail == cell)
item_list->tail = prev_cell;
}
else if (item_list->tail == cell)
{
item_list->tail = NULL;
}
pfree(cell->key);
pfree(cell->value);
pfree(cell);
}
else
{
prev_cell = cell;
}
}
}
cell = (KeyValueListCell *) pg_malloc0(sizeof(KeyValueListCell));
@@ -184,17 +245,14 @@ key_value_list_set_format(KeyValueList *item_list, const char *key, const char *
}
keylen = strlen(key);
vallen = strlen(value);
cell->key = pg_malloc0(keylen + 1);
cell->value = pg_malloc0(MAXLEN);
cell->value = pg_malloc0(vallen + 1);
cell->output_mode = OM_NOT_SET;
strncpy(cell->key, key, keylen);
va_start(arglist, value);
(void) xvsnprintf(cell->value, MAXLEN, value, arglist);
va_end(arglist);
strncpy(cell->value, value, vallen);
if (item_list->tail)
item_list->tail->next = cell;

View File

@@ -119,6 +119,9 @@ extern void
extern void
key_value_list_set(KeyValueList *item_list, const char *key, const char *value);
extern void
key_value_list_replace_or_set(KeyValueList *item_list, const char *key, const char *value);
extern void
key_value_list_set_format(KeyValueList *item_list, const char *key, const char *value,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));

View File

@@ -1,6 +1,8 @@
/*
* sysutils.c
*
* Functions which need to be executed on the local system.
*
* Copyright (c) 2ndQuadrant, 2010-2019
*
* This program is free software: you can redistribute it and/or modify
@@ -115,7 +117,7 @@ bool
remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *outputbuf)
{
FILE *fp;
char ssh_command[MAXLEN] = "";
PQExpBufferData ssh_command;
PQExpBufferData ssh_host;
char output[MAXLEN] = "";
@@ -129,24 +131,29 @@ remote_command(const char *host, const char *user, const char *command, const ch
appendPQExpBufferStr(&ssh_host, host);
maxlen_snprintf(ssh_command,
"ssh -o Batchmode=yes %s %s %s",
ssh_options,
ssh_host.data,
command);
initPQExpBuffer(&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);
log_debug("remote_command():\n %s", ssh_command.data);
fp = popen(ssh_command, "r");
fp = popen(ssh_command.data, "r");
if (fp == NULL)
{
log_error(_("unable to execute remote command:\n %s"), ssh_command);
log_error(_("unable to execute remote command:\n %s"), ssh_command.data);
termPQExpBuffer(&ssh_command);
return false;
}
termPQExpBuffer(&ssh_command);
if (outputbuf != NULL)
{
/* TODO: better error handling */
@@ -364,3 +371,5 @@ enable_wal_receiver(PGconn *conn, bool wait_startup)
return wal_receiver_pid;
}

View File

@@ -28,5 +28,4 @@ extern bool remote_command(const char *host, const char *user, const char *comma
extern pid_t disable_wal_receiver(PGconn *conn);
extern pid_t enable_wal_receiver(PGconn *conn, bool wait_startup);
#endif /* _SYSUTILS_H_ */