mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 15:16:29 +00:00
Compare commits
2 Commits
REL3_0_STA
...
v3.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf4de71523 | ||
|
|
770e6f758c |
@@ -1,29 +0,0 @@
|
|||||||
License and Contributions
|
|
||||||
=========================
|
|
||||||
|
|
||||||
`repmgr` is licensed under the GPL v3. All of its code and documentation is
|
|
||||||
Copyright 2010-2016, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
|
|
||||||
details.
|
|
||||||
|
|
||||||
The development of repmgr has primarily been sponsored by 2ndQuadrant customers.
|
|
||||||
|
|
||||||
Additional work has been sponsored by the 4CaaST project for cloud computing,
|
|
||||||
which has received funding from the European Union's Seventh Framework Programme
|
|
||||||
(FP7/2007-2013) under grant agreement 258862.
|
|
||||||
|
|
||||||
Contributions to `repmgr` are welcome, and will be listed in the file `CREDITS`.
|
|
||||||
2ndQuadrant Limited requires that any contributions provide a copyright
|
|
||||||
assignment and a disclaimer of any work-for-hire ownership claims from the
|
|
||||||
employer of the developer. This lets us make sure that all of the repmgr
|
|
||||||
distribution remains free code. Please contact info@2ndQuadrant.com for a
|
|
||||||
copy of the relevant Copyright Assignment Form.
|
|
||||||
|
|
||||||
Code style
|
|
||||||
----------
|
|
||||||
|
|
||||||
Code in repmgr is formatted to a consistent style using the following command:
|
|
||||||
|
|
||||||
astyle --style=ansi --indent=tab --suffix=none *.c *.h
|
|
||||||
|
|
||||||
Contributors should reformat their code similarly before submitting code to
|
|
||||||
the project, in order to minimize merge conflicts with other work.
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2010-2016, 2ndQuadrant Limited
|
Copyright (c) 2010-2015, 2ndQuadrant Limited
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
|||||||
13
FAILOVER.rst
13
FAILOVER.rst
@@ -93,6 +93,7 @@ Create the user and database to manage replication::
|
|||||||
su - postgres
|
su - postgres
|
||||||
createuser -s repmgr
|
createuser -s repmgr
|
||||||
createdb -O repmgr repmgr
|
createdb -O repmgr repmgr
|
||||||
|
psql -f /usr/share/postgresql/9.0/contrib/repmgr_funcs.sql repmgr
|
||||||
|
|
||||||
Restart the PostgreSQL server::
|
Restart the PostgreSQL server::
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ Log in to node2.
|
|||||||
Clone node1 (the current Master)::
|
Clone node1 (the current Master)::
|
||||||
|
|
||||||
su - postgres
|
su - postgres
|
||||||
repmgr -d repmgr -U repmgr -h node1 standby clone
|
repmgr -d repmgr -U repmgr -h node1 standby clone
|
||||||
|
|
||||||
Start the PostgreSQL server::
|
Start the PostgreSQL server::
|
||||||
|
|
||||||
@@ -171,13 +172,11 @@ Register Master and Standby
|
|||||||
|
|
||||||
Log in to node1.
|
Log in to node1.
|
||||||
|
|
||||||
Register the node as master::
|
Register the node as Master::
|
||||||
|
|
||||||
su - postgres
|
su - postgres
|
||||||
repmgr -f /etc/repmgr/repmgr.conf master register
|
repmgr -f /etc/repmgr/repmgr.conf master register
|
||||||
|
|
||||||
This will also create the repmgr schema and functions.
|
|
||||||
|
|
||||||
Log in to node2. Register it as a standby::
|
Log in to node2. Register it as a standby::
|
||||||
|
|
||||||
su - postgres
|
su - postgres
|
||||||
@@ -204,12 +203,6 @@ repmgr will also ask for the superuser password on the witness database so
|
|||||||
it can reconnect when needed (the command line option --initdb-no-pwprompt
|
it can reconnect when needed (the command line option --initdb-no-pwprompt
|
||||||
will set up a password-less superuser).
|
will set up a password-less superuser).
|
||||||
|
|
||||||
By default the witness server will listen on port 5499; this value can be
|
|
||||||
overridden by explicitly providing the port number in the conninfo string
|
|
||||||
in repmgr.conf. (Note that it is also possible to specify the port number
|
|
||||||
with the -l/--local-port option, however this option is now deprecated and
|
|
||||||
will be overridden by a port setting in the conninfo string).
|
|
||||||
|
|
||||||
Start the repmgrd daemons
|
Start the repmgrd daemons
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|||||||
23
FAQ.md
23
FAQ.md
@@ -34,11 +34,6 @@ General
|
|||||||
replication slots, setting a higher figure will make adding new nodes
|
replication slots, setting a higher figure will make adding new nodes
|
||||||
easier.
|
easier.
|
||||||
|
|
||||||
- Does `repmgr` support hash indexes?
|
|
||||||
|
|
||||||
No. Hash indexes and replication do not mix well and their use is
|
|
||||||
explicitly discouraged; see:
|
|
||||||
http://www.postgresql.org/docs/current/interactive/sql-createindex.html#AEN74175
|
|
||||||
|
|
||||||
`repmgr`
|
`repmgr`
|
||||||
--------
|
--------
|
||||||
@@ -101,9 +96,8 @@ General
|
|||||||
is intended to support running the witness server as a separate
|
is intended to support running the witness server as a separate
|
||||||
instance on a normal node server, rather than on its own dedicated server.
|
instance on a normal node server, rather than on its own dedicated server.
|
||||||
|
|
||||||
To specify different port for the witness server, supply the port number
|
To specify a port for the witness server, supply the port number to
|
||||||
in the `conninfo` string in `repmgr.conf`
|
repmgr with the `-l/--local-port` command line option.
|
||||||
(repmgr 3.0.1 and earlier: use the `-l/--local-port` option)
|
|
||||||
|
|
||||||
- Do I need to include `shared_preload_libraries = 'repmgr_funcs'`
|
- Do I need to include `shared_preload_libraries = 'repmgr_funcs'`
|
||||||
in `postgresql.conf` if I'm not using `repmgrd`?
|
in `postgresql.conf` if I'm not using `repmgrd`?
|
||||||
@@ -112,14 +106,6 @@ General
|
|||||||
If you later decide to run `repmgrd`, you just need to add
|
If you later decide to run `repmgrd`, you just need to add
|
||||||
`shared_preload_libraries = 'repmgr_funcs'` and restart PostgreSQL.
|
`shared_preload_libraries = 'repmgr_funcs'` and restart PostgreSQL.
|
||||||
|
|
||||||
- I've provided replication permission for the `repmgr` user in `pg_hba.conf`
|
|
||||||
but `repmgr`/`repmgrd` complains it can't connect to the server... Why?
|
|
||||||
|
|
||||||
`repmgr`/`repmgrd` need to be able to connect to the repmgr database
|
|
||||||
with a normal connection to query metadata. The `replication` connection
|
|
||||||
permission is for PostgreSQL's streaming replication and doesn't
|
|
||||||
necessarily need to be the `repmgr` user.
|
|
||||||
|
|
||||||
|
|
||||||
`repmgrd`
|
`repmgrd`
|
||||||
---------
|
---------
|
||||||
@@ -148,8 +134,3 @@ General
|
|||||||
|
|
||||||
Note that after registering a delayed standby, `repmgrd` will only start
|
Note that after registering a delayed standby, `repmgrd` will only start
|
||||||
once the metadata added in the master node has been replicated.
|
once the metadata added in the master node has been replicated.
|
||||||
|
|
||||||
- How can I get `repmgrd` to rotate its logfile?
|
|
||||||
|
|
||||||
Configure your system's `logrotate` service to do this; see example
|
|
||||||
in README.md
|
|
||||||
|
|||||||
38
HISTORY
38
HISTORY
@@ -1,41 +1,3 @@
|
|||||||
3.0.4 2016-01-
|
|
||||||
Remove requirement for 'archive_mode' to be enabled (Ian)
|
|
||||||
|
|
||||||
3.0.3 2016-01-04
|
|
||||||
Create replication slot if required before base backup is run (Abhijit)
|
|
||||||
standy clone: when using rsync, clean up "pg_replslot" directory (Ian)
|
|
||||||
Improve --help output (Ian)
|
|
||||||
Improve config file parsing (Ian)
|
|
||||||
Various logging output improvements, including explicit HINTS (Ian)
|
|
||||||
Add --log-level to explicitly set log level on command line (Ian)
|
|
||||||
Repurpose --verbose to display extra log output (Ian)
|
|
||||||
Add --terse to hide hints and other non-critical output (Ian)
|
|
||||||
Reference internal functions with explicit catalog path (Ian)
|
|
||||||
When following a new primary, have repmgr (not repmgrd) create the new slot (Ian)
|
|
||||||
Add /etc/repmgr.conf as a default configuration file location (Ian)
|
|
||||||
Prevent repmgrd's -v/--verbose option expecting a parameter (Ian)
|
|
||||||
Prevent invalid replication_lag values being written to the monitoring table (Ian)
|
|
||||||
Improve repmgrd behaviour when monitored standby node is temporarily
|
|
||||||
unavailable (Martín)
|
|
||||||
|
|
||||||
3.0.2 2015-10-02
|
|
||||||
Improve handling of --help/--version options; and improve help output (Ian)
|
|
||||||
Improve handling of situation where logfile can't be opened (Ian)
|
|
||||||
Always pass -D/--pgdata option to pg_basebackup (Ian)
|
|
||||||
Bugfix: standby clone --force does not empty pg_xlog (Gianni)
|
|
||||||
Bugfix: autofailover with reconnect_attempts > 1 (Gianni)
|
|
||||||
Bugfix: ignore comments after values (soxwellfb)
|
|
||||||
Bugfix: handle string values in 'node' parameter correctly (Gregory Duchatelet)
|
|
||||||
Allow repmgr to be compiled with a newer libpq (Marco)
|
|
||||||
Bugfix: call update_node_record_set_upstream() for STANDBY FOLLOW (Tomas)
|
|
||||||
Update `repmgr --help` output (per Github report from renard)
|
|
||||||
Update tablespace remapping in --rsync-only mode for 9.5 and later (Ian)
|
|
||||||
Deprecate `-l/--local-port` option - the port can be extracted
|
|
||||||
from the conninfo string in repmgr.conf (Ian)
|
|
||||||
Add STANDBY UNREGISTER (Vik Fearing)
|
|
||||||
Don't fail with error when registering master if schema already defined (Ian)
|
|
||||||
Fixes to whitespace handling when parsing config file (Ian)
|
|
||||||
|
|
||||||
3.0.1 2015-04-16
|
3.0.1 2015-04-16
|
||||||
Prevent repmgrd from looping infinitely if node was not registered (Ian)
|
Prevent repmgrd from looping infinitely if node was not registered (Ian)
|
||||||
When promoting a standby, have repmgr (not repmgrd) handle metadata updates (Ian)
|
When promoting a standby, have repmgr (not repmgrd) handle metadata updates (Ian)
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
|||||||
#
|
#
|
||||||
# Makefile
|
# Makefile
|
||||||
# Copyright (c) 2ndQuadrant, 2010-2016
|
# Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
|
|
||||||
repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
|
repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
|
||||||
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o
|
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o
|
||||||
|
|||||||
94
PACKAGES.md
94
PACKAGES.md
@@ -4,10 +4,10 @@ Packaging
|
|||||||
Notes on RedHat Linux, Fedora, and CentOS Builds
|
Notes on RedHat Linux, Fedora, and CentOS Builds
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
The RPM packages of PostgreSQL put `pg_config` into the `postgresql-devel`
|
The RPM packages of PostgreSQL put ``pg_config`` into the ``postgresql-devel``
|
||||||
package, not the main server one. And if you have a RPM install of PostgreSQL
|
package, not the main server one. And if you have a RPM install of PostgreSQL
|
||||||
9.0, the entire PostgreSQL binary directory will not be in your PATH by default
|
9.0, the entire PostgreSQL binary directory will not be in your PATH by default
|
||||||
either. Individual utilities are made available via the `alternatives`
|
either. Individual utilities are made available via the ``alternatives``
|
||||||
mechanism, but not all commands will be wrapped that way. The files installed
|
mechanism, but not all commands will be wrapped that way. The files installed
|
||||||
by repmgr will certainly not be in the default PATH for the postgres user
|
by repmgr will certainly not be in the default PATH for the postgres user
|
||||||
on such a system. They will instead be in /usr/pgsql-9.0/bin/ on this
|
on such a system. They will instead be in /usr/pgsql-9.0/bin/ on this
|
||||||
@@ -15,61 +15,57 @@ type of system.
|
|||||||
|
|
||||||
When building repmgr against a RPM packaged build, you may discover that some
|
When building repmgr against a RPM packaged build, you may discover that some
|
||||||
development packages are needed as well. The following build errors can
|
development packages are needed as well. The following build errors can
|
||||||
occur:
|
occur::
|
||||||
|
|
||||||
/usr/bin/ld: cannot find -lxslt
|
/usr/bin/ld: cannot find -lxslt
|
||||||
/usr/bin/ld: cannot find -lpam
|
/usr/bin/ld: cannot find -lpam
|
||||||
|
|
||||||
Install the following packages to correct those:
|
Install the following packages to correct those::
|
||||||
|
|
||||||
|
yum install libxslt-devel
|
||||||
yum install libxslt-devel
|
yum install pam-devel
|
||||||
yum install pam-devel
|
|
||||||
|
|
||||||
If building repmgr as a regular user, then doing the install into the system
|
If building repmgr as a regular user, then doing the install into the system
|
||||||
directories using sudo, the syntax is hard. `pg_config` won't be in root's
|
directories using sudo, the syntax is hard. ``pg_config`` won't be in root's
|
||||||
path either. The following recipe should work:
|
path either. The following recipe should work::
|
||||||
|
|
||||||
sudo PATH="/usr/pgsql-9.0/bin:$PATH" make USE_PGXS=1 install
|
|
||||||
|
|
||||||
|
sudo PATH="/usr/pgsql-9.0/bin:$PATH" make USE_PGXS=1 install
|
||||||
|
|
||||||
Issues with 32 and 64 bit RPMs
|
Issues with 32 and 64 bit RPMs
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
If when building, you receive a series of errors of this form:
|
If when building, you receive a series of errors of this form::
|
||||||
|
|
||||||
/usr/bin/ld: skipping incompatible /usr/pgsql-9.0/lib/libpq.so when searching for -lpq
|
/usr/bin/ld: skipping incompatible /usr/pgsql-9.0/lib/libpq.so when searching for -lpq
|
||||||
|
|
||||||
This is likely because you have both the 32 and 64 bit versions of the
|
This is likely because you have both the 32 and 64 bit versions of the
|
||||||
`postgresql90-devel` package installed. You can check that like this:
|
``postgresql90-devel`` package installed. You can check that like this::
|
||||||
|
|
||||||
rpm -qa --queryformat '%{NAME}\t%{ARCH}\n' | grep postgresql90-devel
|
rpm -qa --queryformat '%{NAME}\t%{ARCH}\n' | grep postgresql90-devel
|
||||||
|
|
||||||
And if two packages appear, one for i386 and one for x86_64, that's not supposed
|
And if two packages appear, one for i386 and one for x86_64, that's not supposed
|
||||||
to be allowed.
|
to be allowed.
|
||||||
|
|
||||||
This can happen when using the PGDG repo to install that package;
|
This can happen when using the PGDG repo to install that package;
|
||||||
here is an example sessions demonstrating the problem case appearing:
|
here is an example sessions demonstrating the problem case appearing::
|
||||||
|
|
||||||
|
# yum install postgresql-devel
|
||||||
|
..
|
||||||
|
Setting up Install Process
|
||||||
|
Resolving Dependencies
|
||||||
|
--> Running transaction check
|
||||||
|
---> Package postgresql90-devel.i386 0:9.0.2-2PGDG.rhel5 set to be updated
|
||||||
|
---> Package postgresql90-devel.x86_64 0:9.0.2-2PGDG.rhel5 set to be updated
|
||||||
|
--> Finished Dependency Resolution
|
||||||
|
|
||||||
# yum install postgresql-devel
|
Dependencies Resolved
|
||||||
..
|
|
||||||
Setting up Install Process
|
|
||||||
Resolving Dependencies
|
|
||||||
--> Running transaction check
|
|
||||||
---> Package postgresql90-devel.i386 0:9.0.2-2PGDG.rhel5 set to be updated
|
|
||||||
---> Package postgresql90-devel.x86_64 0:9.0.2-2PGDG.rhel5 set to be updated
|
|
||||||
--> Finished Dependency Resolution
|
|
||||||
|
|
||||||
Dependencies Resolved
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
Package Arch Version Repository Size
|
|
||||||
=========================================================================
|
|
||||||
Installing:
|
|
||||||
postgresql90-devel i386 9.0.2-2PGDG.rhel5 pgdg90 1.5 M
|
|
||||||
postgresql90-devel x86_64 9.0.2-2PGDG.rhel5 pgdg90 1.6 M
|
|
||||||
|
|
||||||
|
=========================================================================
|
||||||
|
Package Arch Version Repository Size
|
||||||
|
=========================================================================
|
||||||
|
Installing:
|
||||||
|
postgresql90-devel i386 9.0.2-2PGDG.rhel5 pgdg90 1.5 M
|
||||||
|
postgresql90-devel x86_64 9.0.2-2PGDG.rhel5 pgdg90 1.6 M
|
||||||
|
|
||||||
Note how both the i386 and x86_64 platform architectures are selected for
|
Note how both the i386 and x86_64 platform architectures are selected for
|
||||||
installation. Your main PostgreSQL package will only be compatible with one of
|
installation. Your main PostgreSQL package will only be compatible with one of
|
||||||
@@ -77,14 +73,14 @@ those, and if the repmgr build finds the wrong postgresql90-devel these
|
|||||||
"skipping incompatible" messages appear.
|
"skipping incompatible" messages appear.
|
||||||
|
|
||||||
In this case, you can temporarily remove both packages, then just install the
|
In this case, you can temporarily remove both packages, then just install the
|
||||||
correct one for your architecture. Example:
|
correct one for your architecture. Example::
|
||||||
|
|
||||||
rpm -e postgresql90-devel --allmatches
|
rpm -e postgresql90-devel --allmatches
|
||||||
yum install postgresql90-devel-9.0.2-2PGDG.rhel5.x86_64
|
yum install postgresql90-devel-9.0.2-2PGDG.rhel5.x86_64
|
||||||
|
|
||||||
Instead just deleting the package from the wrong platform might not leave behind
|
Instead just deleting the package from the wrong platform might not leave behind
|
||||||
the correct files, due to the way in which these accidentally happen to interact.
|
the correct files, due to the way in which these accidentally happen to interact.
|
||||||
If you already tried to build repmgr before doing this, you'll need to do:
|
If you already tried to build repmgr before doing this, you'll need to do::
|
||||||
|
|
||||||
make USE_PGXS=1 clean
|
make USE_PGXS=1 clean
|
||||||
|
|
||||||
@@ -93,19 +89,19 @@ to get rid of leftover files from the wrong architecture.
|
|||||||
Notes on Ubuntu, Debian or other Debian-based Builds
|
Notes on Ubuntu, Debian or other Debian-based Builds
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
The Debian packages of PostgreSQL put `pg_config` into the development package
|
The Debian packages of PostgreSQL put ``pg_config`` into the development package
|
||||||
called `postgresql-server-dev-$version`.
|
called ``postgresql-server-dev-$version``.
|
||||||
|
|
||||||
When building repmgr against a Debian packages build, you may discover that some
|
When building repmgr against a Debian packages build, you may discover that some
|
||||||
development packages are needed as well. You will need the following development
|
development packages are needed as well. You will need the following development
|
||||||
packages installed:
|
packages installed::
|
||||||
|
|
||||||
sudo apt-get install libxslt-dev libxml2-dev libpam-dev libedit-dev
|
sudo apt-get install libxslt-dev libxml2-dev libpam-dev libedit-dev
|
||||||
|
|
||||||
If you're using Debian packages for PostgreSQL and are building repmgr with the
|
If you're using Debian packages for PostgreSQL and are building repmgr with the
|
||||||
USE_PGXS option you also need to install the corresponding development package:
|
USE_PGXS option you also need to install the corresponding development package::
|
||||||
|
|
||||||
sudo apt-get install postgresql-server-dev-9.0
|
sudo apt-get install postgresql-server-dev-9.0
|
||||||
|
|
||||||
If you build and install repmgr manually it will not be on the system path. The
|
If you build and install repmgr manually it will not be on the system path. The
|
||||||
binaries will be installed in /usr/lib/postgresql/$version/bin/ which is not on
|
binaries will be installed in /usr/lib/postgresql/$version/bin/ which is not on
|
||||||
@@ -114,14 +110,14 @@ multiple installed versions of PostgreSQL on the same system through a wrapper
|
|||||||
called pg_wrapper and repmgr is not (yet) known to this wrapper.
|
called pg_wrapper and repmgr is not (yet) known to this wrapper.
|
||||||
|
|
||||||
You can solve this in many different ways, the most Debian like is to make an
|
You can solve this in many different ways, the most Debian like is to make an
|
||||||
alternate for repmgr and repmgrd:
|
alternate for repmgr and repmgrd::
|
||||||
|
|
||||||
sudo update-alternatives --install /usr/bin/repmgr repmgr /usr/lib/postgresql/9.0/bin/repmgr 10
|
sudo update-alternatives --install /usr/bin/repmgr repmgr /usr/lib/postgresql/9.0/bin/repmgr 10
|
||||||
sudo update-alternatives --install /usr/bin/repmgrd repmgrd /usr/lib/postgresql/9.0/bin/repmgrd 10
|
sudo update-alternatives --install /usr/bin/repmgrd repmgrd /usr/lib/postgresql/9.0/bin/repmgrd 10
|
||||||
|
|
||||||
You can also make a deb package of repmgr using:
|
You can also make a deb package of repmgr using::
|
||||||
|
|
||||||
make USE_PGXS=1 deb
|
make USE_PGXS=1 deb
|
||||||
|
|
||||||
This will build a Debian package one level up from where you build, normally the
|
This will build a Debian package one level up from where you build, normally the
|
||||||
same directory that you have your repmgr/ directory in.
|
same directory that you have your repmgr/ directory in.
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ Master setup
|
|||||||
CREATE DATABASE repmgr_db OWNER repmgr_usr;
|
CREATE DATABASE repmgr_db OWNER repmgr_usr;
|
||||||
```
|
```
|
||||||
|
|
||||||
- configure `postgresql.conf` for replication (see README.md for sample
|
- configure `postgresql.conf` for replication (see above)
|
||||||
settings)
|
|
||||||
|
|
||||||
- update `pg_hba.conf`, e.g.:
|
- update `pg_hba.conf`, e.g.:
|
||||||
|
|
||||||
@@ -111,8 +110,8 @@ This concludes the basic `repmgr` setup of master and standby. The records
|
|||||||
created in the `repl_nodes` table should look something like this:
|
created in the `repl_nodes` table should look something like this:
|
||||||
|
|
||||||
repmgr_db=# SELECT * from repmgr_test.repl_nodes;
|
repmgr_db=# SELECT * from repmgr_test.repl_nodes;
|
||||||
id | type | upstream_node_id | cluster | name | conninfo | slot_name | priority | active
|
id | type | upstream_node_id | cluster | name | conninfo | slot_name | priority | active
|
||||||
----+---------+------------------+---------+-------+----------------------------------------------------+-----------+----------+--------
|
----+---------+------------------+---------+-------+-------------------------------------------------+-----------+----------+--------
|
||||||
1 | primary | | test | node1 | host=repmgr_node1 user=repmgr_usr dbname=repmgr_db | | 0 | t
|
1 | primary | | test | node1 | host=localhost user=repmgr_usr dbname=repmgr_db | | 0 | t
|
||||||
2 | standby | 1 | test | node2 | host=repmgr_node2 user=repmgr_usr dbname=repmgr_db | | 0 | t
|
2 | standby | 1 | test | node2 | host=localhost user=repmgr_usr dbname=repmgr_db | | 0 | t
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|||||||
107
README.md
107
README.md
@@ -7,18 +7,15 @@ hot-standby capabilities with tools to set up standby servers, monitor
|
|||||||
replication, and perform administrative tasks such as failover or manual
|
replication, and perform administrative tasks such as failover or manual
|
||||||
switchover operations.
|
switchover operations.
|
||||||
|
|
||||||
This document covers `repmgr 3`, which supports PostgreSQL 9.3 and later.
|
This document covers `repmgr 3`, which supports PostgreSQL 9.4 and 9.3.
|
||||||
This version can use `pg_basebackup` to clone standby servers, supports
|
This version can use `pg_basebackup` to clone standby servers, supports
|
||||||
replication slots and cascading replication, doesn't require a restart
|
replication slots and cascading replication, doesn't require a restart
|
||||||
after promotion, and has many usability improvements.
|
after promotion, and has many usability improvements.
|
||||||
|
|
||||||
Please continue to use `repmgr 2` with PostgreSQL 9.2 and earlier.
|
Please continue to use `repmgr 2` with earlier PostgreSQL 9.x versions.
|
||||||
For a list of changes since `repmgr 2` and instructions on upgrading to
|
For a list of changes since `repmgr 2` and instructions on upgrading to
|
||||||
`repmgr 3`, see the "Upgrading from repmgr 2" section below.
|
`repmgr 3`, see the "Upgrading from repmgr 2" section below.
|
||||||
|
|
||||||
For a list of frequently asked questions about `repmgr`, please refer
|
|
||||||
to the file `FAQ.md`.
|
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@@ -56,7 +53,7 @@ on any UNIX-like system which PostgreSQL itself supports.
|
|||||||
|
|
||||||
All nodes must be running the same major version of PostgreSQL, and we
|
All nodes must be running the same major version of PostgreSQL, and we
|
||||||
recommend that they also run the same minor version. This version of
|
recommend that they also run the same minor version. This version of
|
||||||
`repmgr` (v3) supports PostgreSQL 9.3 and later.
|
`repmgr` (v3) supports PostgreSQL 9.3 and 9.4.
|
||||||
|
|
||||||
Earlier versions of `repmgr` needed password-less SSH access between
|
Earlier versions of `repmgr` needed password-less SSH access between
|
||||||
nodes in order to clone standby servers using `rsync`. `repmgr 3` can
|
nodes in order to clone standby servers using `rsync`. `repmgr 3` can
|
||||||
@@ -101,8 +98,8 @@ for details.
|
|||||||
|
|
||||||
### PostgreSQL configuration
|
### PostgreSQL configuration
|
||||||
|
|
||||||
The primary server needs to be configured for replication with settings
|
The primary server needs to be configured for replication with the
|
||||||
like the following in `postgresql.conf`:
|
following settings in `postgresql.conf`:
|
||||||
|
|
||||||
# Allow read-only queries on standby servers. The number of WAL
|
# Allow read-only queries on standby servers. The number of WAL
|
||||||
# senders should be larger than the number of standby servers.
|
# senders should be larger than the number of standby servers.
|
||||||
@@ -114,7 +111,7 @@ like the following in `postgresql.conf`:
|
|||||||
# How much WAL to retain on the primary to allow a temporarily
|
# How much WAL to retain on the primary to allow a temporarily
|
||||||
# disconnected standby to catch up again. The larger this is, the
|
# disconnected standby to catch up again. The larger this is, the
|
||||||
# longer the standby can be disconnected. This is needed only in
|
# longer the standby can be disconnected. This is needed only in
|
||||||
# 9.3; from 9.4, replication slots can be used instead (see below).
|
# 9.3; in 9.4, replication slots can be used instead (see below).
|
||||||
|
|
||||||
wal_keep_segments = 5000
|
wal_keep_segments = 5000
|
||||||
|
|
||||||
@@ -124,18 +121,13 @@ like the following in `postgresql.conf`:
|
|||||||
archive_mode = on
|
archive_mode = on
|
||||||
archive_command = 'cd .'
|
archive_command = 'cd .'
|
||||||
|
|
||||||
# If you plan to use repmgrd, ensure that shared_preload_libraries
|
# You can also set additional replication parameters here, such as
|
||||||
# is configured to load 'repmgr_funcs'
|
# hot_standby_feedback or synchronous_standby_names.
|
||||||
|
|
||||||
shared_preload_libraries = 'repmgr_funcs'
|
|
||||||
|
|
||||||
PostgreSQL 9.4 makes it possible to use replication slots, which means
|
PostgreSQL 9.4 makes it possible to use replication slots, which means
|
||||||
the value of `wal_keep_segments` need no longer be set. See section
|
the value of wal_keep_segments need no longer be set. With 9.3, `repmgr`
|
||||||
"Replication slots" below for more details.
|
expects it to be set to at least 5000 (= 80GB of WAL) by default, though
|
||||||
|
this can be overriden with the `-w N` argument.
|
||||||
With PostgreSQL 9.3, `repmgr` expects `wal_keep_segments` to be set to
|
|
||||||
at least 5000 (= 80GB of WAL) by default, though this can be overriden
|
|
||||||
with the `-w N` argument.
|
|
||||||
|
|
||||||
A dedicated PostgreSQL superuser account and a database in which to
|
A dedicated PostgreSQL superuser account and a database in which to
|
||||||
store monitoring and replication data are required. Create them by
|
store monitoring and replication data are required. Create them by
|
||||||
@@ -144,14 +136,10 @@ running the following commands:
|
|||||||
createuser -s repmgr
|
createuser -s repmgr
|
||||||
createdb repmgr -O repmgr
|
createdb repmgr -O repmgr
|
||||||
|
|
||||||
We recommend using the name `repmgr` for both user and database, but you
|
We recommend using the name `repmgr` for both, but you can use whatever
|
||||||
can use whatever name you like (and you need to set the names you chose
|
name you like (and you need to set the names you chose in the `conninfo`
|
||||||
in the `conninfo` string in `repmgr.conf`; see below). We also recommend
|
string in `repmgr.conf`; see below). `repmgr` will create the schema and
|
||||||
that you set the `repmgr` user's search path to include the `repmgr` schema
|
objects it needs when it connects to the server.
|
||||||
for convenience when querying the metadata tables and views.
|
|
||||||
|
|
||||||
The `repmgr` application will create its metadata schema in the `repmgr`
|
|
||||||
database when the master server is registered.
|
|
||||||
|
|
||||||
### repmgr configuration
|
### repmgr configuration
|
||||||
|
|
||||||
@@ -235,7 +223,7 @@ The node can then be restarted.
|
|||||||
The node will then need to be re-registered with `repmgr`; again
|
The node will then need to be re-registered with `repmgr`; again
|
||||||
the `--force` option is required to update the existing record:
|
the `--force` option is required to update the existing record:
|
||||||
|
|
||||||
repmgr -f /etc/repmgr/repmgr.conf \
|
repmgr -f /etc/repmgr/repmgr.conf
|
||||||
--force \
|
--force \
|
||||||
standby register
|
standby register
|
||||||
|
|
||||||
@@ -267,20 +255,6 @@ Example log output (at default log level):
|
|||||||
[2015-03-11 13:15:40] [INFO] reloading configuration file and updating repmgr tables
|
[2015-03-11 13:15:40] [INFO] reloading configuration file and updating repmgr tables
|
||||||
[2015-03-11 13:15:40] [INFO] starting continuous standby node monitoring
|
[2015-03-11 13:15:40] [INFO] starting continuous standby node monitoring
|
||||||
|
|
||||||
Note that currently `repmgrd` does not provide logfile rotation. To ensure
|
|
||||||
the current logfile does not grow indefinitely, configure your system's `logrotate`
|
|
||||||
to do this. Sample configuration to rotate logfiles weekly with retention
|
|
||||||
for up to 52 weeks and rotation forced if a file grows beyond 100Mb:
|
|
||||||
|
|
||||||
/var/log/postgresql/repmgr-9.4.log {
|
|
||||||
missingok
|
|
||||||
compress
|
|
||||||
rotate 52
|
|
||||||
maxsize 100M
|
|
||||||
weekly
|
|
||||||
create 0600 postgres postgres
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Witness server
|
Witness server
|
||||||
--------------
|
--------------
|
||||||
@@ -371,12 +345,10 @@ Following event types currently exist:
|
|||||||
|
|
||||||
master_register
|
master_register
|
||||||
standby_register
|
standby_register
|
||||||
standby_unregister
|
|
||||||
standby_clone
|
standby_clone
|
||||||
standby_promote
|
standby_promote
|
||||||
witness_create
|
witness_create
|
||||||
repmgrd_start
|
repmgrd_start
|
||||||
repmgrd_monitor
|
|
||||||
repmgrd_failover_promote
|
repmgrd_failover_promote
|
||||||
repmgrd_failover_follow
|
repmgrd_failover_follow
|
||||||
|
|
||||||
@@ -426,18 +398,6 @@ stored in the `repl_nodes` table.
|
|||||||
Note that `repmgr` will fail with an error if this option is specified when
|
Note that `repmgr` will fail with an error if this option is specified when
|
||||||
working with PostgreSQL 9.3.
|
working with PostgreSQL 9.3.
|
||||||
|
|
||||||
Be aware that when initially cloning a standby, you will need to ensure
|
|
||||||
that all required WAL files remain available while the cloning is taking
|
|
||||||
place. If using the default `pg_basebackup` method, we recommend setting
|
|
||||||
`pg_basebackup`'s `--xlog-method` parameter to `stream` like this:
|
|
||||||
|
|
||||||
pg_basebackup_options='--xlog-method=stream'
|
|
||||||
|
|
||||||
See the `pg_basebackup` documentation [*] for details. Otherwise you'll need
|
|
||||||
to set `wal_keep_segments` to an appropriately high value.
|
|
||||||
|
|
||||||
[*] http://www.postgresql.org/docs/current/static/app-pgbasebackup.html
|
|
||||||
|
|
||||||
Further reading:
|
Further reading:
|
||||||
* http://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION-SLOTS
|
* http://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION-SLOTS
|
||||||
* http://blog.2ndquadrant.com/postgresql-9-4-slots/
|
* http://blog.2ndquadrant.com/postgresql-9-4-slots/
|
||||||
@@ -475,19 +435,12 @@ its port if is different from the default one.
|
|||||||
Registers a master in a cluster. This command needs to be executed before any
|
Registers a master in a cluster. This command needs to be executed before any
|
||||||
standby nodes are registered.
|
standby nodes are registered.
|
||||||
|
|
||||||
`primary register` can be used as an alias for `master register`.
|
|
||||||
|
|
||||||
* `standby register`
|
* `standby register`
|
||||||
|
|
||||||
Registers a standby with `repmgr`. This command needs to be executed to enable
|
Registers a standby with `repmgr`. This command needs to be executed to enable
|
||||||
promote/follow operations and to allow `repmgrd` to work with the node.
|
promote/follow operations and to allow `repmgrd` to work with the node.
|
||||||
An existing standby can be registered using this command.
|
An existing standby can be registered using this command.
|
||||||
|
|
||||||
* `standby unregister`
|
|
||||||
|
|
||||||
Unregisters a standby with `repmgr`. This command does not affect the actual
|
|
||||||
replication.
|
|
||||||
|
|
||||||
* `standby clone [node to be cloned]`
|
* `standby clone [node to be cloned]`
|
||||||
|
|
||||||
Clones a new standby node from the data directory of the master (or
|
Clones a new standby node from the data directory of the master (or
|
||||||
@@ -607,20 +560,20 @@ and one view:
|
|||||||
`repmgr` or `repmgrd` will return one of the following error codes on program
|
`repmgr` or `repmgrd` will return one of the following error codes on program
|
||||||
exit:
|
exit:
|
||||||
|
|
||||||
* SUCCESS (0) Program ran successfully.
|
* SUCCESS (0) Program ran successfully.
|
||||||
* ERR_BAD_CONFIG (1) Configuration file could not be parsed or was invalid
|
* ERR_BAD_CONFIG (1) Configuration file could not be parsed or was invalid
|
||||||
* ERR_BAD_RSYNC (2) An rsync call made by the program returned an error
|
* ERR_BAD_RSYNC (2) An rsync call made by the program returned an error
|
||||||
* ERR_NO_RESTART (4) An attempt to restart a PostgreSQL instance failed
|
* ERR_NO_RESTART (4) An attempt to restart a PostgreSQL instance failed
|
||||||
* ERR_DB_CON (6) Error when trying to connect to a database
|
* ERR_DB_CON (6) Error when trying to connect to a database
|
||||||
* ERR_DB_QUERY (7) Error while executing a database query
|
* ERR_DB_QUERY (7) Error while executing a database query
|
||||||
* ERR_PROMOTED (8) Exiting program because the node has been promoted to master
|
* ERR_PROMOTED (8) Exiting program because the node has been promoted to master
|
||||||
* ERR_BAD_PASSWORD (9) Password used to connect to a database was rejected
|
* ERR_BAD_PASSWORD (9) Password used to connect to a database was rejected
|
||||||
* ERR_STR_OVERFLOW (10) String overflow error
|
* ERR_STR_OVERFLOW (10) String overflow error
|
||||||
* ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only)
|
* ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only)
|
||||||
* ERR_BAD_SSH (12) Error when connecting to remote host via SSH
|
* ERR_BAD_SSH (12) Error when connecting to remote host via SSH
|
||||||
* ERR_SYS_FAILURE (13) Error when forking (repmgrd only)
|
* ERR_SYS_FAILURE (13) Error when forking (repmgrd only)
|
||||||
* ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup
|
* ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup
|
||||||
* ERR_MONITORING_FAIL (16) Unrecoverable error encountered during monitoring (repmgrd only)
|
|
||||||
|
|
||||||
Support and Assistance
|
Support and Assistance
|
||||||
----------------------
|
----------------------
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ REPMGRD_ENABLED=no
|
|||||||
#REPMGRD_USER=postgres
|
#REPMGRD_USER=postgres
|
||||||
|
|
||||||
# repmgrd binary
|
# repmgrd binary
|
||||||
#REPMGRD_BIN=/usr/bin/repmgrd
|
#REPMGRD_BIN=/usr/bin/repmgr
|
||||||
|
|
||||||
# pid file
|
# pid file
|
||||||
#REPMGRD_PIDFILE=/var/lib/pgsql/repmgr/repmgrd.pid
|
#REPMGRD_PIDFILE=/var/lib/pgsql/repmgr/repmgrd.pid
|
||||||
|
|||||||
49
SSH-RSYNC.md
49
SSH-RSYNC.md
@@ -1,36 +1,35 @@
|
|||||||
Set up trusted copy between postgres accounts
|
Set up trusted copy between postgres accounts
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
If you need to use `rsync` to clone standby servers, the `postgres` account
|
If you need to use rsync to clone standby servers, the postgres account
|
||||||
on your primary and standby servers must be each able to access the other
|
on your master and standby servers must be each able to access the other
|
||||||
using SSH without a password.
|
using SSH without a password.
|
||||||
|
|
||||||
First generate an ssh key, using an empty passphrase, and copy the resulting
|
First generate a ssh key, using an empty passphrase, and copy the resulting
|
||||||
keys and a matching authorization file to a privileged user account on the other
|
keys and a maching authorization file to a privledged user on the other system::
|
||||||
system:
|
|
||||||
|
|
||||||
[postgres@node1]$ ssh-keygen -t rsa
|
[postgres@node1]$ ssh-keygen -t rsa
|
||||||
Generating public/private rsa key pair.
|
Generating public/private rsa key pair.
|
||||||
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_rsa):
|
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_rsa):
|
||||||
Enter passphrase (empty for no passphrase):
|
Enter passphrase (empty for no passphrase):
|
||||||
Enter same passphrase again:
|
Enter same passphrase again:
|
||||||
Your identification has been saved in /var/lib/pgsql/.ssh/id_rsa.
|
Your identification has been saved in /var/lib/pgsql/.ssh/id_rsa.
|
||||||
Your public key has been saved in /var/lib/pgsql/.ssh/id_rsa.pub.
|
Your public key has been saved in /var/lib/pgsql/.ssh/id_rsa.pub.
|
||||||
The key fingerprint is:
|
The key fingerprint is:
|
||||||
aa:bb:cc:dd:ee:ff:aa:11:22:33:44:55:66:77:88:99 postgres@db1.domain.com
|
aa:bb:cc:dd:ee:ff:aa:11:22:33:44:55:66:77:88:99 postgres@db1.domain.com
|
||||||
[postgres@node1]$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
[postgres@node1]$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
||||||
[postgres@node1]$ chmod go-rwx ~/.ssh/*
|
[postgres@node1]$ chmod go-rwx ~/.ssh/*
|
||||||
[postgres@node1]$ cd ~/.ssh
|
[postgres@node1]$ cd ~/.ssh
|
||||||
[postgres@node1]$ scp id_rsa.pub id_rsa authorized_keys user@node2:
|
[postgres@node1]$ scp id_rsa.pub id_rsa authorized_keys user@node2:
|
||||||
|
|
||||||
Login as a user on the other system, and install the files into the `postgres`
|
Login as a user on the other system, and install the files into the postgres
|
||||||
user's account:
|
user's account::
|
||||||
|
|
||||||
[user@node2 ~]$ sudo chown postgres.postgres authorized_keys id_rsa.pub id_rsa
|
[user@node2 ~]$ sudo chown postgres.postgres authorized_keys id_rsa.pub id_rsa
|
||||||
[user@node2 ~]$ sudo mkdir -p ~postgres/.ssh
|
[user@node2 ~]$ sudo mkdir -p ~postgres/.ssh
|
||||||
[user@node2 ~]$ sudo chown postgres.postgres ~postgres/.ssh
|
[user@node2 ~]$ sudo chown postgres.postgres ~postgres/.ssh
|
||||||
[user@node2 ~]$ sudo mv authorized_keys id_rsa.pub id_rsa ~postgres/.ssh
|
[user@node2 ~]$ sudo mv authorized_keys id_rsa.pub id_rsa ~postgres/.ssh
|
||||||
[user@node2 ~]$ sudo chmod -R go-rwx ~postgres/.ssh
|
[user@node2 ~]$ sudo chmod -R go-rwx ~postgres/.ssh
|
||||||
|
|
||||||
Now test that ssh in both directions works. You may have to accept some new
|
Now test that ssh in both directions works. You may have to accept some new
|
||||||
known hosts in the process.
|
known hosts in the process.
|
||||||
|
|||||||
58
TODO
58
TODO
@@ -7,14 +7,9 @@ Known issues in repmgr
|
|||||||
|
|
||||||
* PGPASSFILE may not be passed to pg_basebackup
|
* PGPASSFILE may not be passed to pg_basebackup
|
||||||
|
|
||||||
|
|
||||||
Planned feature improvements
|
Planned feature improvements
|
||||||
============================
|
============================
|
||||||
|
|
||||||
* Use 'primary' instead of 'master' in documentation and log output
|
|
||||||
for consistency with PostgreSQL documentation. See also commit
|
|
||||||
870b0a53b627eeb9aca1fc14cbafe25b5beafe12.
|
|
||||||
|
|
||||||
* A better check which standby did receive most of the data
|
* A better check which standby did receive most of the data
|
||||||
|
|
||||||
* Make the fact that a standby may be delayed a factor in the voting
|
* Make the fact that a standby may be delayed a factor in the voting
|
||||||
@@ -25,53 +20,8 @@ Planned feature improvements
|
|||||||
* Create the repmgr user/database on "master register".
|
* Create the repmgr user/database on "master register".
|
||||||
|
|
||||||
* Use pg_basebackup for the data directory, and ALSO rsync for the
|
* Use pg_basebackup for the data directory, and ALSO rsync for the
|
||||||
configuration files.
|
configuration files.
|
||||||
|
|
||||||
* If no configuration file supplied, search in sensible default locations
|
* Use pg_basebackup -X s
|
||||||
(currently: current directory and `pg_config --sysconfdir`); if
|
NOTE: this can be used by including `-X s` in the configuration parameter
|
||||||
possible this should include the location provided by the package,
|
`pg_basebackup_options`
|
||||||
if installed.
|
|
||||||
|
|
||||||
* repmgrd: if connection to the upstream node fails on startup, optionally
|
|
||||||
retry for a certain period before giving up; this will cover cases when
|
|
||||||
e.g. primary and standby are both starting up, and the standby comes up
|
|
||||||
before the primary. See github issue #80.
|
|
||||||
|
|
||||||
* make old master node ID available for event notification commands
|
|
||||||
(See github issue #80).
|
|
||||||
|
|
||||||
* Have pg_basebackup use replication slots, if and when support for
|
|
||||||
this is added; see:
|
|
||||||
http://www.postgresql.org/message-id/555DD2B2.7020000@gmx.net
|
|
||||||
|
|
||||||
* use "primary/standby" terminology in place of "master/slave" for consistency
|
|
||||||
with main PostrgreSQL usage
|
|
||||||
|
|
||||||
* repmgr standby clone: possibility to use barman instead of performing a new base backup
|
|
||||||
|
|
||||||
* possibility to transform a failed master into a new standby with pg_rewind
|
|
||||||
|
|
||||||
* "repmgr standby switchover" to promote a standby in a controlled manner
|
|
||||||
and convert the existing primary into a standby
|
|
||||||
|
|
||||||
* make repmgrd more robust
|
|
||||||
|
|
||||||
* repmgr: when cloning a standby using pg_basebackup and replication slots are
|
|
||||||
requested, activate the replication slot using pg_receivexlog to negate the
|
|
||||||
need to set `wal_keep_segments` just for the initial clone (9.4 and 9.5).
|
|
||||||
|
|
||||||
Usability improvements
|
|
||||||
======================
|
|
||||||
|
|
||||||
* repmgr: add interrupt handler, so that if the program is interrupted
|
|
||||||
while running a backup, an attempt can be made to execute pg_stop_backup()
|
|
||||||
on the primary, to prevent an orphaned backup state existing.
|
|
||||||
|
|
||||||
* repmgr: when unregistering a node, delete any entries in the repl_monitoring
|
|
||||||
table.
|
|
||||||
|
|
||||||
* repmgr: for "standby unregister", accept connection parameters for the
|
|
||||||
primary and perform metadata updates (and slot removal) directly on
|
|
||||||
the primary, to allow a shutdown standby to be unregistered
|
|
||||||
(currently the standby must still be running, which means the replication
|
|
||||||
slot can't be dropped).
|
|
||||||
63
check_dir.c
63
check_dir.c
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* check_dir.c - Directories management functions
|
* check_dir.c - Directories management functions
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,19 +23,14 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ftw.h>
|
|
||||||
|
|
||||||
/* NB: postgres_fe must be included BEFORE check_dir */
|
/* NB: postgres_fe must be included BEFORE check_dir */
|
||||||
#include <libpq-fe.h>
|
#include "postgres_fe.h"
|
||||||
#include <postgres_fe.h>
|
|
||||||
|
|
||||||
#include "check_dir.h"
|
#include "check_dir.h"
|
||||||
|
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
static bool _create_pg_dir(char *dir, bool force, bool for_witness);
|
|
||||||
static int unlink_dir_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make sure the directory either doesn't exist or is empty
|
* make sure the directory either doesn't exist or is empty
|
||||||
* we use this function to check the new data directory and
|
* we use this function to check the new data directory and
|
||||||
@@ -248,19 +243,6 @@ is_pg_dir(char *dir)
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
create_pg_dir(char *dir, bool force)
|
create_pg_dir(char *dir, bool force)
|
||||||
{
|
|
||||||
return _create_pg_dir(dir, force, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
create_witness_pg_dir(char *dir, bool force)
|
|
||||||
{
|
|
||||||
return _create_pg_dir(dir, force, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool
|
|
||||||
_create_pg_dir(char *dir, bool force, bool for_witness)
|
|
||||||
{
|
{
|
||||||
bool pg_dir = false;
|
bool pg_dir = false;
|
||||||
|
|
||||||
@@ -297,33 +279,21 @@ _create_pg_dir(char *dir, bool force, bool for_witness)
|
|||||||
|
|
||||||
pg_dir = is_pg_dir(dir);
|
pg_dir = is_pg_dir(dir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we use force to reduce the time needed to restore a node which
|
||||||
|
* turn async after a failover or anything else
|
||||||
|
*/
|
||||||
if (pg_dir && force)
|
if (pg_dir && force)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
|
||||||
* The witness server does not store any data other than a copy of the
|
|
||||||
* repmgr metadata, so in --force mode we can simply overwrite the
|
|
||||||
* directory.
|
|
||||||
*
|
|
||||||
* For non-witness servers, we'll leave the data in place, both to reduce
|
|
||||||
* the risk of unintentional data loss and to make it possible for the
|
|
||||||
* data directory to be brought up-to-date with rsync.
|
|
||||||
*/
|
|
||||||
if (for_witness)
|
|
||||||
{
|
|
||||||
log_notice(_("deleting existing data directory \"%s\"\n"), dir);
|
|
||||||
nftw(dir, unlink_dir_callback, 64, FTW_DEPTH | FTW_PHYS);
|
|
||||||
}
|
|
||||||
/* Let it continue */
|
/* Let it continue */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (pg_dir && !force)
|
else if (pg_dir && !force)
|
||||||
{
|
{
|
||||||
log_hint(_("This looks like a PostgreSQL directory.\n"
|
log_warning(_("\nThis looks like a PostgreSQL directory.\n"
|
||||||
"If you are sure you want to clone here, "
|
"If you are sure you want to clone here, "
|
||||||
"please check there is no PostgreSQL server "
|
"please check there is no PostgreSQL server "
|
||||||
"running and use the -F/--force option\n"));
|
"running and use the --force option\n"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,14 +306,3 @@ _create_pg_dir(char *dir, bool force, bool for_witness)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
unlink_dir_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
|
|
||||||
{
|
|
||||||
int rv = remove(fpath);
|
|
||||||
|
|
||||||
if (rv)
|
|
||||||
perror(fpath);
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* check_dir.h
|
* check_dir.h
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,6 +26,5 @@ bool create_dir(char *dir);
|
|||||||
bool set_dir_permissions(char *dir);
|
bool set_dir_permissions(char *dir);
|
||||||
bool is_pg_dir(char *dir);
|
bool is_pg_dir(char *dir);
|
||||||
bool create_pg_dir(char *dir, bool force);
|
bool create_pg_dir(char *dir, bool force);
|
||||||
bool create_witness_pg_dir(char *dir, bool force);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
546
config.c
546
config.c
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* config.c - Functions to parse the config file
|
* config.c - Functions to parse the config file
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,28 +26,10 @@
|
|||||||
|
|
||||||
static void parse_event_notifications_list(t_configuration_options *options, const char *arg);
|
static void parse_event_notifications_list(t_configuration_options *options, const char *arg);
|
||||||
static void tablespace_list_append(t_configuration_options *options, const char *arg);
|
static void tablespace_list_append(t_configuration_options *options, const char *arg);
|
||||||
static void exit_with_errors(ErrorList *config_errors);
|
|
||||||
|
|
||||||
const static char *_progname = '\0';
|
|
||||||
static char config_file_path[MAXPGPATH];
|
|
||||||
static bool config_file_provided = false;
|
|
||||||
static bool config_file_found = false;
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
set_progname(const char *argv0)
|
|
||||||
{
|
|
||||||
_progname = get_progname(argv0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
progname(void)
|
|
||||||
{
|
|
||||||
return _progname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* load_config()
|
* parse_config()
|
||||||
*
|
*
|
||||||
* Set default options and overwrite with values from provided configuration
|
* Set default options and overwrite with values from provided configuration
|
||||||
* file.
|
* file.
|
||||||
@@ -56,161 +38,83 @@ progname(void)
|
|||||||
*
|
*
|
||||||
* Any configuration options changed in this function must also be changed in
|
* Any configuration options changed in this function must also be changed in
|
||||||
* reload_config()
|
* reload_config()
|
||||||
*
|
|
||||||
* NOTE: this function is called before the logger is set up, so we need
|
|
||||||
* to handle the verbose option ourselves; also the default log level is NOTICE,
|
|
||||||
* so we can't use DEBUG.
|
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
load_config(const char *config_file, bool verbose, t_configuration_options *options, char *argv0)
|
parse_config(const char *config_file, t_configuration_options *options)
|
||||||
{
|
{
|
||||||
struct stat stat_config;
|
char *s,
|
||||||
|
buff[MAXLINELENGTH];
|
||||||
|
char config_file_buf[MAXLEN];
|
||||||
|
char name[MAXLEN];
|
||||||
|
char value[MAXLEN];
|
||||||
|
bool config_file_provided = false;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/* Sanity checks */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a configuration file was provided, check it exists, otherwise
|
* If a configuration file was provided, check it exists, otherwise
|
||||||
* emit an error and terminate. We assume that if a user explicitly
|
* emit an error
|
||||||
* provides a configuration file, they'll want to make sure it's
|
|
||||||
* used and not fall back to any of the defaults.
|
|
||||||
*/
|
*/
|
||||||
if (config_file[0])
|
if (config_file[0])
|
||||||
{
|
{
|
||||||
strncpy(config_file_path, config_file, MAXPGPATH);
|
struct stat config;
|
||||||
canonicalize_path(config_file_path);
|
|
||||||
|
|
||||||
if (stat(config_file_path, &stat_config) != 0)
|
strncpy(config_file_buf, config_file, MAXLEN);
|
||||||
|
canonicalize_path(config_file_buf);
|
||||||
|
|
||||||
|
if(stat(config_file_buf, &config) != 0)
|
||||||
{
|
{
|
||||||
log_err(_("provided configuration file \"%s\" not found: %s\n"),
|
log_err(_("provided configuration file '%s' not found: %s\n"),
|
||||||
config_file,
|
config_file,
|
||||||
strerror(errno)
|
strerror(errno)
|
||||||
);
|
);
|
||||||
exit(ERR_BAD_CONFIG);
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose == true)
|
|
||||||
{
|
|
||||||
log_notice(_("using configuration file \"%s\"\n"), config_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
config_file_provided = true;
|
config_file_provided = true;
|
||||||
config_file_found = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If no configuration file was provided, attempt to find a default file
|
* If no configuration file was provided, set to a default file
|
||||||
* in this order:
|
* which `parse_config()` will attempt to read if it exists
|
||||||
* - current directory
|
|
||||||
* - /etc/repmgr.conf
|
|
||||||
* - default sysconfdir
|
|
||||||
*
|
|
||||||
* here we just check for the existence of the file; parse_config()
|
|
||||||
* will handle read errors etc.
|
|
||||||
*/
|
*/
|
||||||
if (config_file_provided == false)
|
else
|
||||||
{
|
{
|
||||||
char my_exec_path[MAXPGPATH];
|
strncpy(config_file_buf, DEFAULT_CONFIG_FILE, MAXLEN);
|
||||||
char sysconf_etc_path[MAXPGPATH];
|
|
||||||
|
|
||||||
/* 1. "./repmgr.conf" */
|
|
||||||
if (verbose == true)
|
|
||||||
{
|
|
||||||
log_notice(_("looking for configuration file in current directory\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(config_file_path, MAXPGPATH, "./%s", CONFIG_FILE_NAME);
|
|
||||||
canonicalize_path(config_file_path);
|
|
||||||
|
|
||||||
if (stat(config_file_path, &stat_config) == 0)
|
|
||||||
{
|
|
||||||
config_file_found = true;
|
|
||||||
goto end_search;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. "/etc/repmgr.conf" */
|
|
||||||
if (verbose == true)
|
|
||||||
{
|
|
||||||
log_notice(_("looking for configuration file in /etc\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(config_file_path, MAXPGPATH, "/etc/%s", CONFIG_FILE_NAME);
|
|
||||||
if (stat(config_file_path, &stat_config) == 0)
|
|
||||||
{
|
|
||||||
config_file_found = true;
|
|
||||||
goto end_search;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 3. default sysconfdir */
|
|
||||||
if (find_my_exec(argv0, my_exec_path) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, _("%s: could not find own program executable\n"), argv0);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_etc_path(my_exec_path, sysconf_etc_path);
|
|
||||||
|
|
||||||
if (verbose == true)
|
|
||||||
{
|
|
||||||
log_notice(_("looking for configuration file in %s"), sysconf_etc_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(config_file_path, MAXPGPATH, "%s/%s", sysconf_etc_path, CONFIG_FILE_NAME);
|
|
||||||
if (stat(config_file_path, &stat_config) == 0)
|
|
||||||
{
|
|
||||||
config_file_found = true;
|
|
||||||
goto end_search;
|
|
||||||
}
|
|
||||||
|
|
||||||
end_search:
|
|
||||||
if (config_file_found == true)
|
|
||||||
{
|
|
||||||
if (verbose == true)
|
|
||||||
{
|
|
||||||
log_notice(_("configuration file found at: %s\n"), config_file_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (verbose == true)
|
|
||||||
{
|
|
||||||
log_notice(_("no configuration file provided or found\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse_config(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fp = fopen(config_file_buf, "r");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse configuration file; if any errors are encountered,
|
* Since some commands don't require a config file at all, not having one
|
||||||
* list them and exit.
|
* isn't necessarily a problem.
|
||||||
*
|
*
|
||||||
* Ensure any default values set here are synced with repmgr.conf.sample
|
* If the user explictly provided a configuration file and we can't
|
||||||
* and any other documentation.
|
* read it we'll raise an error.
|
||||||
*/
|
*
|
||||||
bool
|
* If no configuration file was provided, we'll try and read the default\
|
||||||
parse_config(t_configuration_options *options)
|
* file if it exists and is readable, but won't worry if it's not.
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
char *s,
|
|
||||||
buf[MAXLINELENGTH];
|
|
||||||
char name[MAXLEN];
|
|
||||||
char value[MAXLEN];
|
|
||||||
|
|
||||||
/* For sanity-checking provided conninfo string */
|
|
||||||
PQconninfoOption *conninfo_options;
|
|
||||||
char *conninfo_errmsg = NULL;
|
|
||||||
|
|
||||||
/* Collate configuration file errors here for friendlier reporting */
|
|
||||||
static ErrorList config_errors = { NULL, NULL };
|
|
||||||
|
|
||||||
/* Initialize configuration options with sensible defaults
|
|
||||||
* note: the default log level is set in log.c and does not need
|
|
||||||
* to be initialised here
|
|
||||||
*/
|
*/
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
if(config_file_provided)
|
||||||
|
{
|
||||||
|
log_err(_("unable to open provided configuration file '%s'; terminating\n"), config_file_buf);
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_notice(_("no configuration file provided and default file '%s' not found - "
|
||||||
|
"continuing with default values\n"),
|
||||||
|
DEFAULT_CONFIG_FILE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize configuration options with sensible defaults */
|
||||||
memset(options->cluster_name, 0, sizeof(options->cluster_name));
|
memset(options->cluster_name, 0, sizeof(options->cluster_name));
|
||||||
options->node = -1;
|
options->node = -1;
|
||||||
options->upstream_node = NO_UPSTREAM_NODE;
|
options->upstream_node = NO_UPSTREAM_NODE;
|
||||||
options->use_replication_slots = 0;
|
|
||||||
memset(options->conninfo, 0, sizeof(options->conninfo));
|
memset(options->conninfo, 0, sizeof(options->conninfo));
|
||||||
options->failover = MANUAL_FAILOVER;
|
options->failover = MANUAL_FAILOVER;
|
||||||
options->priority = DEFAULT_PRIORITY;
|
options->priority = DEFAULT_PRIORITY;
|
||||||
@@ -228,7 +132,7 @@ parse_config(t_configuration_options *options)
|
|||||||
|
|
||||||
/* default to 6 reconnection attempts at intervals of 10 seconds */
|
/* default to 6 reconnection attempts at intervals of 10 seconds */
|
||||||
options->reconnect_attempts = 6;
|
options->reconnect_attempts = 6;
|
||||||
options->reconnect_interval = 10;
|
options->reconnect_intvl = 10;
|
||||||
|
|
||||||
options->monitor_interval_secs = 2;
|
options->monitor_interval_secs = 2;
|
||||||
options->retry_promote_interval_secs = 300;
|
options->retry_promote_interval_secs = 300;
|
||||||
@@ -238,61 +142,27 @@ parse_config(t_configuration_options *options)
|
|||||||
options->tablespace_mapping.head = NULL;
|
options->tablespace_mapping.head = NULL;
|
||||||
options->tablespace_mapping.tail = NULL;
|
options->tablespace_mapping.tail = NULL;
|
||||||
|
|
||||||
/*
|
|
||||||
* If no configuration file available (user didn't specify and none found
|
|
||||||
* in the default locations), return with default values
|
|
||||||
*/
|
|
||||||
if (config_file_found == false)
|
|
||||||
{
|
|
||||||
log_notice(_("no configuration file provided and no default file found - "
|
|
||||||
"continuing with default values\n"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = fopen(config_file_path, "r");
|
|
||||||
|
|
||||||
/*
|
/* Read next line */
|
||||||
* A configuration file has been found, either provided by the user
|
while ((s = fgets(buff, sizeof buff, fp)) != NULL)
|
||||||
* or found in one of the default locations. If we can't open it,
|
|
||||||
* fail with an error.
|
|
||||||
*/
|
|
||||||
if (fp == NULL)
|
|
||||||
{
|
|
||||||
if (config_file_provided)
|
|
||||||
{
|
|
||||||
log_err(_("unable to open provided configuration file \"%s\"; terminating\n"), config_file_path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log_err(_("unable to open default configuration file \"%s\"; terminating\n"), config_file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(ERR_BAD_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read file */
|
|
||||||
while ((s = fgets(buf, sizeof buf, fp)) != NULL)
|
|
||||||
{
|
{
|
||||||
bool known_parameter = true;
|
bool known_parameter = true;
|
||||||
|
|
||||||
|
/* Skip blank lines and comments */
|
||||||
|
if (buff[0] == '\n' || buff[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Parse name/value pair from line */
|
/* Parse name/value pair from line */
|
||||||
parse_line(buf, name, value);
|
parse_line(buff, name, value);
|
||||||
|
|
||||||
/* Skip blank lines */
|
|
||||||
if (!strlen(name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Skip comments */
|
|
||||||
if (name[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Copy into correct entry in parameters struct */
|
/* Copy into correct entry in parameters struct */
|
||||||
if (strcmp(name, "cluster") == 0)
|
if (strcmp(name, "cluster") == 0)
|
||||||
strncpy(options->cluster_name, value, MAXLEN);
|
strncpy(options->cluster_name, value, MAXLEN);
|
||||||
else if (strcmp(name, "node") == 0)
|
else if (strcmp(name, "node") == 0)
|
||||||
options->node = repmgr_atoi(value, "node", &config_errors);
|
options->node = atoi(value);
|
||||||
else if (strcmp(name, "upstream_node") == 0)
|
else if (strcmp(name, "upstream_node") == 0)
|
||||||
options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors);
|
options->upstream_node = atoi(value);
|
||||||
else if (strcmp(name, "conninfo") == 0)
|
else if (strcmp(name, "conninfo") == 0)
|
||||||
strncpy(options->conninfo, value, MAXLEN);
|
strncpy(options->conninfo, value, MAXLEN);
|
||||||
else if (strcmp(name, "rsync_options") == 0)
|
else if (strcmp(name, "rsync_options") == 0)
|
||||||
@@ -319,11 +189,12 @@ parse_config(t_configuration_options *options)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error_list_append(&config_errors,_("value for 'failover' must be 'automatic' or 'manual'\n"));
|
log_err(_("value for 'failover' must be 'automatic' or 'manual'\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(name, "priority") == 0)
|
else if (strcmp(name, "priority") == 0)
|
||||||
options->priority = repmgr_atoi(value, "priority", &config_errors);
|
options->priority = atoi(value);
|
||||||
else if (strcmp(name, "node_name") == 0)
|
else if (strcmp(name, "node_name") == 0)
|
||||||
strncpy(options->node_name, value, MAXLEN);
|
strncpy(options->node_name, value, MAXLEN);
|
||||||
else if (strcmp(name, "promote_command") == 0)
|
else if (strcmp(name, "promote_command") == 0)
|
||||||
@@ -331,16 +202,11 @@ parse_config(t_configuration_options *options)
|
|||||||
else if (strcmp(name, "follow_command") == 0)
|
else if (strcmp(name, "follow_command") == 0)
|
||||||
strncpy(options->follow_command, value, MAXLEN);
|
strncpy(options->follow_command, value, MAXLEN);
|
||||||
else if (strcmp(name, "master_response_timeout") == 0)
|
else if (strcmp(name, "master_response_timeout") == 0)
|
||||||
options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors);
|
options->master_response_timeout = atoi(value);
|
||||||
/* 'primary_response_timeout' as synonym for 'master_response_timeout' -
|
|
||||||
* we'll switch terminology in a future release (3.1?)
|
|
||||||
*/
|
|
||||||
else if (strcmp(name, "primary_response_timeout") == 0)
|
|
||||||
options->master_response_timeout = repmgr_atoi(value, "primary_response_timeout", &config_errors);
|
|
||||||
else if (strcmp(name, "reconnect_attempts") == 0)
|
else if (strcmp(name, "reconnect_attempts") == 0)
|
||||||
options->reconnect_attempts = repmgr_atoi(value, "reconnect_attempts", &config_errors);
|
options->reconnect_attempts = atoi(value);
|
||||||
else if (strcmp(name, "reconnect_interval") == 0)
|
else if (strcmp(name, "reconnect_interval") == 0)
|
||||||
options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", &config_errors);
|
options->reconnect_intvl = atoi(value);
|
||||||
else if (strcmp(name, "pg_bindir") == 0)
|
else if (strcmp(name, "pg_bindir") == 0)
|
||||||
strncpy(options->pg_bindir, value, MAXLEN);
|
strncpy(options->pg_bindir, value, MAXLEN);
|
||||||
else if (strcmp(name, "pg_ctl_options") == 0)
|
else if (strcmp(name, "pg_ctl_options") == 0)
|
||||||
@@ -350,12 +216,11 @@ parse_config(t_configuration_options *options)
|
|||||||
else if (strcmp(name, "logfile") == 0)
|
else if (strcmp(name, "logfile") == 0)
|
||||||
strncpy(options->logfile, value, MAXLEN);
|
strncpy(options->logfile, value, MAXLEN);
|
||||||
else if (strcmp(name, "monitor_interval_secs") == 0)
|
else if (strcmp(name, "monitor_interval_secs") == 0)
|
||||||
options->monitor_interval_secs = repmgr_atoi(value, "monitor_interval_secs", &config_errors);
|
options->monitor_interval_secs = atoi(value);
|
||||||
else if (strcmp(name, "retry_promote_interval_secs") == 0)
|
else if (strcmp(name, "retry_promote_interval_secs") == 0)
|
||||||
options->retry_promote_interval_secs = repmgr_atoi(value, "retry_promote_interval_secs", &config_errors);
|
options->retry_promote_interval_secs = atoi(value);
|
||||||
else if (strcmp(name, "use_replication_slots") == 0)
|
else if (strcmp(name, "use_replication_slots") == 0)
|
||||||
/* XXX we should have a dedicated boolean argument format */
|
options->use_replication_slots = atoi(value);
|
||||||
options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", &config_errors);
|
|
||||||
else if (strcmp(name, "event_notification_command") == 0)
|
else if (strcmp(name, "event_notification_command") == 0)
|
||||||
strncpy(options->event_notification_command, value, MAXLEN);
|
strncpy(options->event_notification_command, value, MAXLEN);
|
||||||
else if (strcmp(name, "event_notifications") == 0)
|
else if (strcmp(name, "event_notifications") == 0)
|
||||||
@@ -374,14 +239,9 @@ parse_config(t_configuration_options *options)
|
|||||||
* we want to accept those, we'd need to add stricter default checking,
|
* we want to accept those, we'd need to add stricter default checking,
|
||||||
* as currently e.g. an empty `node` value will be converted to '0'.
|
* as currently e.g. an empty `node` value will be converted to '0'.
|
||||||
*/
|
*/
|
||||||
if (known_parameter == true && !strlen(value)) {
|
if(known_parameter == true && !strlen(value)) {
|
||||||
char error_message_buf[MAXLEN] = "";
|
log_err(_("no value provided for parameter '%s'\n"), name);
|
||||||
snprintf(error_message_buf,
|
exit(ERR_BAD_CONFIG);
|
||||||
MAXLEN,
|
|
||||||
_("no value provided for parameter \"%s\""),
|
|
||||||
name);
|
|
||||||
|
|
||||||
error_list_append(&config_errors, error_message_buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,49 +252,45 @@ parse_config(t_configuration_options *options)
|
|||||||
/* The following checks are for the presence of the parameter */
|
/* The following checks are for the presence of the parameter */
|
||||||
if (*options->cluster_name == '\0')
|
if (*options->cluster_name == '\0')
|
||||||
{
|
{
|
||||||
error_list_append(&config_errors, _("\"cluster\": parameter was not found\n"));
|
log_err(_("required parameter 'cluster' was not found\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->node == -1)
|
if (options->node == -1)
|
||||||
{
|
{
|
||||||
error_list_append(&config_errors, _("\"node\": parameter was not found\n"));
|
log_err(_("required parameter 'node' was not found\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*options->node_name == '\0')
|
if (*options->node_name == '\0')
|
||||||
{
|
{
|
||||||
error_list_append(&config_errors, _("\"node_name\": parameter was not found\n"));
|
log_err(_("required parameter 'node_name' was not found\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*options->conninfo == '\0')
|
if (*options->conninfo == '\0')
|
||||||
{
|
{
|
||||||
error_list_append(&config_errors, _("\"conninfo\": parameter was not found\n"));
|
log_err(_("required parameter 'conninfo' was not found\n"));
|
||||||
}
|
exit(ERR_BAD_CONFIG);
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Sanity check the provided conninfo string
|
|
||||||
*
|
|
||||||
* NOTE: PQconninfoParse() verifies the string format and checks for valid options
|
|
||||||
* but does not sanity check values
|
|
||||||
*/
|
|
||||||
conninfo_options = PQconninfoParse(options->conninfo, &conninfo_errmsg);
|
|
||||||
if (conninfo_options == NULL)
|
|
||||||
{
|
|
||||||
char error_message_buf[MAXLEN] = "";
|
|
||||||
snprintf(error_message_buf,
|
|
||||||
MAXLEN,
|
|
||||||
_("\"conninfo\": %s"),
|
|
||||||
conninfo_errmsg);
|
|
||||||
|
|
||||||
error_list_append(&config_errors, error_message_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
PQconninfoFree(conninfo_options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_errors.head != NULL)
|
/* The following checks are for valid parameter values */
|
||||||
|
if (options->master_response_timeout <= 0)
|
||||||
{
|
{
|
||||||
exit_with_errors(&config_errors);
|
log_err(_("'master_response_timeout' must be greater than zero\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options->reconnect_attempts < 0)
|
||||||
|
{
|
||||||
|
log_err(_("'reconnect_attempts' must be zero or greater\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options->reconnect_intvl < 0)
|
||||||
|
{
|
||||||
|
log_err(_("'reconnect_interval' must be zero or greater\n"));
|
||||||
|
exit(ERR_BAD_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -449,7 +305,7 @@ trim(char *s)
|
|||||||
*s2 = &s[strlen(s) - 1];
|
*s2 = &s[strlen(s) - 1];
|
||||||
|
|
||||||
/* If string is empty, no action needed */
|
/* If string is empty, no action needed */
|
||||||
if (s2 < s1)
|
if(s2 < s1)
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
/* Trim and delimit right side */
|
/* Trim and delimit right side */
|
||||||
@@ -469,58 +325,32 @@ trim(char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
parse_line(char *buf, char *name, char *value)
|
parse_line(char *buff, char *name, char *value)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract parameter name, if present
|
* first we find the name of the parameter
|
||||||
*/
|
*/
|
||||||
for (; i < MAXLEN; ++i)
|
for (; i < MAXLEN; ++i)
|
||||||
{
|
{
|
||||||
|
if (buff[i] != '=')
|
||||||
if (buf[i] == '=')
|
name[j++] = buff[i];
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch(buf[i])
|
|
||||||
{
|
|
||||||
/* Ignore whitespace */
|
|
||||||
case ' ':
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
case '\t':
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
name[j++] = buf[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
name[j] = '\0';
|
name[j] = '\0';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ignore any whitespace following the '=' sign
|
* Now the value
|
||||||
*/
|
|
||||||
for (; i < MAXLEN; ++i)
|
|
||||||
{
|
|
||||||
if (buf[i+1] == ' ')
|
|
||||||
continue;
|
|
||||||
if (buf[i+1] == '\t')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extract parameter value
|
|
||||||
*/
|
*/
|
||||||
j = 0;
|
j = 0;
|
||||||
for (++i; i < MAXLEN; ++i)
|
for (++i; i < MAXLEN; ++i)
|
||||||
if (buf[i] == '\'')
|
if (buff[i] == '\'')
|
||||||
continue;
|
continue;
|
||||||
else if (buf[i] == '#')
|
else if (buff[i] != '\n')
|
||||||
break;
|
value[j++] = buff[i];
|
||||||
else if (buf[i] != '\n')
|
|
||||||
value[j++] = buf[i];
|
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
value[j] = '\0';
|
value[j] = '\0';
|
||||||
@@ -528,7 +358,7 @@ parse_line(char *buf, char *name, char *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
reload_config(t_configuration_options *orig_options)
|
reload_config(char *config_file, t_configuration_options * orig_options)
|
||||||
{
|
{
|
||||||
PGconn *conn;
|
PGconn *conn;
|
||||||
t_configuration_options new_options;
|
t_configuration_options new_options;
|
||||||
@@ -539,7 +369,7 @@ reload_config(t_configuration_options *orig_options)
|
|||||||
*/
|
*/
|
||||||
log_info(_("reloading configuration file and updating repmgr tables\n"));
|
log_info(_("reloading configuration file and updating repmgr tables\n"));
|
||||||
|
|
||||||
parse_config(&new_options);
|
parse_config(config_file, &new_options);
|
||||||
if (new_options.node == -1)
|
if (new_options.node == -1)
|
||||||
{
|
{
|
||||||
log_warning(_("unable to parse new configuration, retaining current configuration\n"));
|
log_warning(_("unable to parse new configuration, retaining current configuration\n"));
|
||||||
@@ -582,13 +412,13 @@ reload_config(t_configuration_options *orig_options)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_options.reconnect_interval < 0)
|
if (new_options.reconnect_intvl < 0)
|
||||||
{
|
{
|
||||||
log_warning(_("new value for 'reconnect_interval' must be zero or greater\n"));
|
log_warning(_("new value for 'reconnect_interval' must be zero or greater\n"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(orig_options->conninfo, new_options.conninfo) != 0)
|
if(strcmp(orig_options->conninfo, new_options.conninfo) != 0)
|
||||||
{
|
{
|
||||||
/* Test conninfo string */
|
/* Test conninfo string */
|
||||||
conn = establish_db_connection(new_options.conninfo, false);
|
conn = establish_db_connection(new_options.conninfo, false);
|
||||||
@@ -608,56 +438,56 @@ reload_config(t_configuration_options *orig_options)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* cluster_name */
|
/* cluster_name */
|
||||||
if (strcmp(orig_options->cluster_name, new_options.cluster_name) != 0)
|
if(strcmp(orig_options->cluster_name, new_options.cluster_name) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->cluster_name, new_options.cluster_name);
|
strcpy(orig_options->cluster_name, new_options.cluster_name);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* conninfo */
|
/* conninfo */
|
||||||
if (strcmp(orig_options->conninfo, new_options.conninfo) != 0)
|
if(strcmp(orig_options->conninfo, new_options.conninfo) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->conninfo, new_options.conninfo);
|
strcpy(orig_options->conninfo, new_options.conninfo);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* node */
|
/* node */
|
||||||
if (orig_options->node != new_options.node)
|
if(orig_options->node != new_options.node)
|
||||||
{
|
{
|
||||||
orig_options->node = new_options.node;
|
orig_options->node = new_options.node;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* failover */
|
/* failover */
|
||||||
if (orig_options->failover != new_options.failover)
|
if(orig_options->failover != new_options.failover)
|
||||||
{
|
{
|
||||||
orig_options->failover = new_options.failover;
|
orig_options->failover = new_options.failover;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* priority */
|
/* priority */
|
||||||
if (orig_options->priority != new_options.priority)
|
if(orig_options->priority != new_options.priority)
|
||||||
{
|
{
|
||||||
orig_options->priority = new_options.priority;
|
orig_options->priority = new_options.priority;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* node_name */
|
/* node_name */
|
||||||
if (strcmp(orig_options->node_name, new_options.node_name) != 0)
|
if(strcmp(orig_options->node_name, new_options.node_name) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->node_name, new_options.node_name);
|
strcpy(orig_options->node_name, new_options.node_name);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* promote_command */
|
/* promote_command */
|
||||||
if (strcmp(orig_options->promote_command, new_options.promote_command) != 0)
|
if(strcmp(orig_options->promote_command, new_options.promote_command) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->promote_command, new_options.promote_command);
|
strcpy(orig_options->promote_command, new_options.promote_command);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* follow_command */
|
/* follow_command */
|
||||||
if (strcmp(orig_options->follow_command, new_options.follow_command) != 0)
|
if(strcmp(orig_options->follow_command, new_options.follow_command) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->follow_command, new_options.follow_command);
|
strcpy(orig_options->follow_command, new_options.follow_command);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
@@ -674,76 +504,76 @@ reload_config(t_configuration_options *orig_options)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* rsync_options */
|
/* rsync_options */
|
||||||
if (strcmp(orig_options->rsync_options, new_options.rsync_options) != 0)
|
if(strcmp(orig_options->rsync_options, new_options.rsync_options) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->rsync_options, new_options.rsync_options);
|
strcpy(orig_options->rsync_options, new_options.rsync_options);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ssh_options */
|
/* ssh_options */
|
||||||
if (strcmp(orig_options->ssh_options, new_options.ssh_options) != 0)
|
if(strcmp(orig_options->ssh_options, new_options.ssh_options) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->ssh_options, new_options.ssh_options);
|
strcpy(orig_options->ssh_options, new_options.ssh_options);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* master_response_timeout */
|
/* master_response_timeout */
|
||||||
if (orig_options->master_response_timeout != new_options.master_response_timeout)
|
if(orig_options->master_response_timeout != new_options.master_response_timeout)
|
||||||
{
|
{
|
||||||
orig_options->master_response_timeout = new_options.master_response_timeout;
|
orig_options->master_response_timeout = new_options.master_response_timeout;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reconnect_attempts */
|
/* reconnect_attempts */
|
||||||
if (orig_options->reconnect_attempts != new_options.reconnect_attempts)
|
if(orig_options->reconnect_attempts != new_options.reconnect_attempts)
|
||||||
{
|
{
|
||||||
orig_options->reconnect_attempts = new_options.reconnect_attempts;
|
orig_options->reconnect_attempts = new_options.reconnect_attempts;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reconnect_interval */
|
/* reconnect_intvl */
|
||||||
if (orig_options->reconnect_interval != new_options.reconnect_interval)
|
if(orig_options->reconnect_intvl != new_options.reconnect_intvl)
|
||||||
{
|
{
|
||||||
orig_options->reconnect_interval = new_options.reconnect_interval;
|
orig_options->reconnect_intvl = new_options.reconnect_intvl;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pg_ctl_options */
|
/* pg_ctl_options */
|
||||||
if (strcmp(orig_options->pg_ctl_options, new_options.pg_ctl_options) != 0)
|
if(strcmp(orig_options->pg_ctl_options, new_options.pg_ctl_options) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->pg_ctl_options, new_options.pg_ctl_options);
|
strcpy(orig_options->pg_ctl_options, new_options.pg_ctl_options);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pg_basebackup_options */
|
/* pg_basebackup_options */
|
||||||
if (strcmp(orig_options->pg_basebackup_options, new_options.pg_basebackup_options) != 0)
|
if(strcmp(orig_options->pg_basebackup_options, new_options.pg_basebackup_options) != 0)
|
||||||
{
|
{
|
||||||
strcpy(orig_options->pg_basebackup_options, new_options.pg_basebackup_options);
|
strcpy(orig_options->pg_basebackup_options, new_options.pg_basebackup_options);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* monitor_interval_secs */
|
/* monitor_interval_secs */
|
||||||
if (orig_options->monitor_interval_secs != new_options.monitor_interval_secs)
|
if(orig_options->monitor_interval_secs != new_options.monitor_interval_secs)
|
||||||
{
|
{
|
||||||
orig_options->monitor_interval_secs = new_options.monitor_interval_secs;
|
orig_options->monitor_interval_secs = new_options.monitor_interval_secs;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retry_promote_interval_secs */
|
/* retry_promote_interval_secs */
|
||||||
if (orig_options->retry_promote_interval_secs != new_options.retry_promote_interval_secs)
|
if(orig_options->retry_promote_interval_secs != new_options.retry_promote_interval_secs)
|
||||||
{
|
{
|
||||||
orig_options->retry_promote_interval_secs = new_options.retry_promote_interval_secs;
|
orig_options->retry_promote_interval_secs = new_options.retry_promote_interval_secs;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use_replication_slots */
|
/* use_replication_slots */
|
||||||
if (orig_options->use_replication_slots != new_options.use_replication_slots)
|
if(orig_options->use_replication_slots != new_options.use_replication_slots)
|
||||||
{
|
{
|
||||||
orig_options->use_replication_slots = new_options.use_replication_slots;
|
orig_options->use_replication_slots = new_options.use_replication_slots;
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_changed == true)
|
if(config_changed == true)
|
||||||
{
|
{
|
||||||
log_debug(_("reload_config(): configuration has changed\n"));
|
log_debug(_("reload_config(): configuration has changed\n"));
|
||||||
}
|
}
|
||||||
@@ -756,96 +586,6 @@ reload_config(t_configuration_options *orig_options)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
error_list_append(ErrorList *error_list, char *error_message)
|
|
||||||
{
|
|
||||||
ErrorListCell *cell;
|
|
||||||
|
|
||||||
cell = (ErrorListCell *) pg_malloc0(sizeof(ErrorListCell));
|
|
||||||
|
|
||||||
if (cell == NULL)
|
|
||||||
{
|
|
||||||
log_err(_("unable to allocate memory; terminating.\n"));
|
|
||||||
exit(ERR_BAD_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
cell->error_message = pg_malloc0(MAXLEN);
|
|
||||||
strncpy(cell->error_message, error_message, MAXLEN);
|
|
||||||
|
|
||||||
if (error_list->tail)
|
|
||||||
{
|
|
||||||
error_list->tail->next = cell;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error_list->head = cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
error_list->tail = cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert provided string to an integer using strtol;
|
|
||||||
* on error, if a callback is provided, pass the error message to that,
|
|
||||||
* otherwise exit
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
repmgr_atoi(const char *value, const char *config_item, ErrorList *error_list)
|
|
||||||
{
|
|
||||||
char *endptr;
|
|
||||||
long longval = 0;
|
|
||||||
char error_message_buf[MAXLEN] = "";
|
|
||||||
|
|
||||||
/* It's possible that some versions of strtol() don't treat an empty
|
|
||||||
* string as an error.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (*value == '\0')
|
|
||||||
{
|
|
||||||
snprintf(error_message_buf,
|
|
||||||
MAXLEN,
|
|
||||||
_("no value provided for \"%s\""),
|
|
||||||
config_item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = 0;
|
|
||||||
longval = strtol(value, &endptr, 10);
|
|
||||||
|
|
||||||
if (value == endptr || errno)
|
|
||||||
{
|
|
||||||
snprintf(error_message_buf,
|
|
||||||
MAXLEN,
|
|
||||||
_("\"%s\": invalid value (provided: \"%s\")"),
|
|
||||||
config_item, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Currently there are no values which could be negative */
|
|
||||||
if (longval < 0)
|
|
||||||
{
|
|
||||||
snprintf(error_message_buf,
|
|
||||||
MAXLEN,
|
|
||||||
_("\"%s\" must be zero or greater (provided: %s)"),
|
|
||||||
config_item, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Error message buffer is set */
|
|
||||||
if (error_message_buf[0] != '\0')
|
|
||||||
{
|
|
||||||
if (error_list == NULL)
|
|
||||||
{
|
|
||||||
log_err("%s\n", error_message_buf);
|
|
||||||
exit(ERR_BAD_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
error_list_append(error_list, error_message_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int32) longval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split argument into old_dir and new_dir and append to tablespace mapping
|
* Split argument into old_dir and new_dir and append to tablespace mapping
|
||||||
@@ -862,7 +602,7 @@ tablespace_list_append(t_configuration_options *options, const char *arg)
|
|||||||
const char *arg_ptr;
|
const char *arg_ptr;
|
||||||
|
|
||||||
cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
|
cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
|
||||||
if (cell == NULL)
|
if(cell == NULL)
|
||||||
{
|
{
|
||||||
log_err(_("unable to allocate memory; terminating\n"));
|
log_err(_("unable to allocate memory; terminating\n"));
|
||||||
exit(ERR_BAD_CONFIG);
|
exit(ERR_BAD_CONFIG);
|
||||||
@@ -930,7 +670,7 @@ parse_event_notifications_list(t_configuration_options *options, const char *arg
|
|||||||
for (arg_ptr = arg; arg_ptr <= (arg + strlen(arg)); arg_ptr++)
|
for (arg_ptr = arg; arg_ptr <= (arg + strlen(arg)); arg_ptr++)
|
||||||
{
|
{
|
||||||
/* ignore whitespace */
|
/* ignore whitespace */
|
||||||
if (*arg_ptr == ' ' || *arg_ptr == '\t')
|
if(*arg_ptr == ' ' || *arg_ptr == '\t')
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -939,13 +679,13 @@ parse_event_notifications_list(t_configuration_options *options, const char *arg
|
|||||||
* comma (or end-of-string) should mark the end of an event type -
|
* comma (or end-of-string) should mark the end of an event type -
|
||||||
* just as long as there was something preceding it
|
* just as long as there was something preceding it
|
||||||
*/
|
*/
|
||||||
if ((*arg_ptr == ',' || *arg_ptr == '\0') && event_type_buf[0] != '\0')
|
if((*arg_ptr == ',' || *arg_ptr == '\0') && event_type_buf[0] != '\0')
|
||||||
{
|
{
|
||||||
EventNotificationListCell *cell;
|
EventNotificationListCell *cell;
|
||||||
|
|
||||||
cell = (EventNotificationListCell *) pg_malloc0(sizeof(EventNotificationListCell));
|
cell = (EventNotificationListCell *) pg_malloc0(sizeof(EventNotificationListCell));
|
||||||
|
|
||||||
if (cell == NULL)
|
if(cell == NULL)
|
||||||
{
|
{
|
||||||
log_err(_("unable to allocate memory; terminating\n"));
|
log_err(_("unable to allocate memory; terminating\n"));
|
||||||
exit(ERR_BAD_CONFIG);
|
exit(ERR_BAD_CONFIG);
|
||||||
@@ -968,7 +708,7 @@ parse_event_notifications_list(t_configuration_options *options, const char *arg
|
|||||||
dst_ptr = event_type_buf;
|
dst_ptr = event_type_buf;
|
||||||
}
|
}
|
||||||
/* ignore duplicated commas */
|
/* ignore duplicated commas */
|
||||||
else if (*arg_ptr == ',')
|
else if(*arg_ptr == ',')
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -978,21 +718,3 @@ parse_event_notifications_list(t_configuration_options *options, const char *arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
exit_with_errors(ErrorList *config_errors)
|
|
||||||
{
|
|
||||||
ErrorListCell *cell;
|
|
||||||
|
|
||||||
log_err(_("%s: following errors were found in the configuration file.\n"), progname());
|
|
||||||
|
|
||||||
for (cell = config_errors->head; cell; cell = cell->next)
|
|
||||||
{
|
|
||||||
log_err("%s\n", cell->error_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(ERR_BAD_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
28
config.h
28
config.h
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* config.h
|
* config.h
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
|
|
||||||
#define CONFIG_FILE_NAME "repmgr.conf"
|
|
||||||
|
|
||||||
typedef struct EventNotificationListCell
|
typedef struct EventNotificationListCell
|
||||||
{
|
{
|
||||||
@@ -68,7 +67,7 @@ typedef struct
|
|||||||
char ssh_options[QUERY_STR_LEN];
|
char ssh_options[QUERY_STR_LEN];
|
||||||
int master_response_timeout;
|
int master_response_timeout;
|
||||||
int reconnect_attempts;
|
int reconnect_attempts;
|
||||||
int reconnect_interval;
|
int reconnect_intvl;
|
||||||
char pg_bindir[MAXLEN];
|
char pg_bindir[MAXLEN];
|
||||||
char pg_ctl_options[MAXLEN];
|
char pg_ctl_options[MAXLEN];
|
||||||
char pg_basebackup_options[MAXLEN];
|
char pg_basebackup_options[MAXLEN];
|
||||||
@@ -83,29 +82,10 @@ typedef struct
|
|||||||
|
|
||||||
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} }
|
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} }
|
||||||
|
|
||||||
typedef struct ErrorListCell
|
|
||||||
{
|
|
||||||
struct ErrorListCell *next;
|
|
||||||
char *error_message;
|
|
||||||
} ErrorListCell;
|
|
||||||
|
|
||||||
typedef struct ErrorList
|
bool parse_config(const char *config_file, t_configuration_options *options);
|
||||||
{
|
|
||||||
ErrorListCell *head;
|
|
||||||
ErrorListCell *tail;
|
|
||||||
} ErrorList;
|
|
||||||
|
|
||||||
void set_progname(const char *argv0);
|
|
||||||
const char * progname(void);
|
|
||||||
|
|
||||||
bool load_config(const char *config_file, bool verbose, t_configuration_options *options, char *argv0);
|
|
||||||
bool reload_config(t_configuration_options *orig_options);
|
|
||||||
bool parse_config(t_configuration_options *options);
|
|
||||||
void parse_line(char *buff, char *name, char *value);
|
void parse_line(char *buff, char *name, char *value);
|
||||||
char *trim(char *s);
|
char *trim(char *s);
|
||||||
void error_list_append(ErrorList *error_list, char *error_message);
|
bool reload_config(char *config_file, t_configuration_options *orig_options);
|
||||||
int repmgr_atoi(const char *s,
|
|
||||||
const char *config_item,
|
|
||||||
ErrorList *error_list);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
471
dbutils.c
471
dbutils.c
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* dbutils.c - Database connection/management functions
|
* dbutils.c - Database connection/management functions
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -82,78 +82,6 @@ establish_db_connection_by_params(const char *keywords[], const char *values[],
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
begin_transaction(PGconn *conn)
|
|
||||||
{
|
|
||||||
PGresult *res;
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "begin_transaction()\n");
|
|
||||||
|
|
||||||
res = PQexec(conn, "BEGIN");
|
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
log_err(_("Unable to begin transaction: %s\n"),
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
commit_transaction(PGconn *conn)
|
|
||||||
{
|
|
||||||
PGresult *res;
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "commit_transaction()\n");
|
|
||||||
|
|
||||||
res = PQexec(conn, "COMMIT");
|
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
log_err(_("Unable to commit transaction: %s\n"),
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
rollback_transaction(PGconn *conn)
|
|
||||||
{
|
|
||||||
PGresult *res;
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "rollback_transaction()\n");
|
|
||||||
|
|
||||||
res = PQexec(conn, "ROLLBACK");
|
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
log_err(_("Unable to rollback transaction: %s\n"),
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
check_cluster_schema(PGconn *conn)
|
check_cluster_schema(PGconn *conn)
|
||||||
{
|
{
|
||||||
@@ -164,8 +92,7 @@ check_cluster_schema(PGconn *conn)
|
|||||||
"SELECT 1 FROM pg_namespace WHERE nspname = '%s'",
|
"SELECT 1 FROM pg_namespace WHERE nspname = '%s'",
|
||||||
get_repmgr_schema());
|
get_repmgr_schema());
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "check_cluster_schema(): %s\n", sqlquery);
|
log_debug(_("check_cluster_schema(): %s\n"), sqlquery);
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
@@ -195,22 +122,17 @@ is_standby(PGconn *conn)
|
|||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
char *sqlquery = "SELECT pg_catalog.pg_is_in_recovery()";
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "is_standby(): %s\n", sqlquery);
|
res = PQexec(conn, "SELECT pg_is_in_recovery()");
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
|
||||||
|
|
||||||
if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err(_("Unable to query server mode: %s\n"),
|
log_err(_("Can't query server mode: %s"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
result = -1;
|
result = -1;
|
||||||
}
|
}
|
||||||
else if (PQntuples(res) == 1 && strcmp(PQgetvalue(res, 0, 0), "t") == 0)
|
else if (PQntuples(res) == 1 && strcmp(PQgetvalue(res, 0, 0), "t") == 0)
|
||||||
{
|
|
||||||
result = 1;
|
result = 1;
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return result;
|
return result;
|
||||||
@@ -297,8 +219,6 @@ get_master_node_id(PGconn *conn, char *cluster)
|
|||||||
get_repmgr_schema_quoted(conn),
|
get_repmgr_schema_quoted(conn),
|
||||||
cluster);
|
cluster);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_master_node_id():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
@@ -340,7 +260,7 @@ get_server_version(PGconn *conn, char *server_version)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_version != NULL)
|
if(server_version != NULL)
|
||||||
strcpy(server_version, PQgetvalue(res, 0, 0));
|
strcpy(server_version, PQgetvalue(res, 0, 0));
|
||||||
|
|
||||||
return atoi(PQgetvalue(res, 0, 0));
|
return atoi(PQgetvalue(res, 0, 0));
|
||||||
@@ -355,17 +275,14 @@ guc_set(PGconn *conn, const char *parameter, const char *op,
|
|||||||
char sqlquery[QUERY_STR_LEN];
|
char sqlquery[QUERY_STR_LEN];
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery,
|
sqlquery_snprintf(sqlquery, "SELECT true FROM pg_settings "
|
||||||
"SELECT true FROM pg_settings "
|
|
||||||
" WHERE name = '%s' AND setting %s '%s'",
|
" WHERE name = '%s' AND setting %s '%s'",
|
||||||
parameter, op, value);
|
parameter, op, value);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "guc_set():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err(_("guc_set(): unable to execute query\n%s\n"),
|
log_err(_("GUC setting check PQexec failed: %s"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
retval = -1;
|
retval = -1;
|
||||||
}
|
}
|
||||||
@@ -396,12 +313,10 @@ guc_set_typed(PGconn *conn, const char *parameter, const char *op,
|
|||||||
" WHERE name = '%s' AND setting::%s %s '%s'::%s",
|
" WHERE name = '%s' AND setting::%s %s '%s'::%s",
|
||||||
parameter, datatype, op, value, datatype);
|
parameter, datatype, op, value, datatype);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "guc_set_typed():n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err(_("guc_set_typed(): unable to execute query\n%s\n"),
|
log_err(_("GUC setting check PQexec failed: %s"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
retval = -1;
|
retval = -1;
|
||||||
}
|
}
|
||||||
@@ -422,16 +337,15 @@ get_cluster_size(PGconn *conn, char *size)
|
|||||||
PGresult *res;
|
PGresult *res;
|
||||||
char sqlquery[QUERY_STR_LEN];
|
char sqlquery[QUERY_STR_LEN];
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery,
|
sqlquery_snprintf(
|
||||||
"SELECT pg_catalog.pg_size_pretty(SUM(pg_catalog.pg_database_size(oid))::bigint) "
|
sqlquery,
|
||||||
|
"SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
|
||||||
" FROM pg_database ");
|
" FROM pg_database ");
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_cluster_size():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err(_("get_cluster_size(): unable to execute query\n%s\n"),
|
log_err(_("get_cluster_size(): PQexec failed: %s"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
@@ -459,7 +373,7 @@ get_pg_setting(PGconn *conn, const char *setting, char *output)
|
|||||||
" FROM pg_settings WHERE name = '%s'",
|
" FROM pg_settings WHERE name = '%s'",
|
||||||
setting);
|
setting);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_pg_setting(): %s\n", sqlquery);
|
log_debug(_("get_pg_setting(): %s\n"), sqlquery);
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
|
|
||||||
@@ -481,14 +395,13 @@ get_pg_setting(PGconn *conn, const char *setting, char *output)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* XXX highly unlikely this would ever happen */
|
log_err(_("unknown parameter: %s"), PQgetvalue(res, i, 0));
|
||||||
log_err(_("get_pg_setting(): unknown parameter \"%s\""), PQgetvalue(res, i, 0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success == true)
|
if(success == true)
|
||||||
{
|
{
|
||||||
log_debug(_("get_pg_setting(): returned value is \"%s\"\n"), output);
|
log_debug(_("get_pg_setting(): returned value is '%s'\n"), output);
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
@@ -533,19 +446,19 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
|
|||||||
cluster,
|
cluster,
|
||||||
node_id);
|
node_id);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_upstream_connection():\n%s\n", sqlquery);
|
log_debug("get_upstream_connection(): %s\n", sqlquery);
|
||||||
|
|
||||||
res = PQexec(standby_conn, sqlquery);
|
res = PQexec(standby_conn, sqlquery);
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err(_("unable to get conninfo for upstream server\n%s\n"),
|
log_err(_("unable to get conninfo for upstream server: %s\n"),
|
||||||
PQerrorMessage(standby_conn));
|
PQerrorMessage(standby_conn));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PQntuples(res))
|
if(!PQntuples(res))
|
||||||
{
|
{
|
||||||
log_notice(_("no record found for upstream server"));
|
log_notice(_("no record found for upstream server"));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
@@ -554,12 +467,12 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
|
|||||||
|
|
||||||
strncpy(upstream_conninfo, PQgetvalue(res, 0, 0), MAXCONNINFO);
|
strncpy(upstream_conninfo, PQgetvalue(res, 0, 0), MAXCONNINFO);
|
||||||
|
|
||||||
if (upstream_node_id_ptr != NULL)
|
if(upstream_node_id_ptr != NULL)
|
||||||
*upstream_node_id_ptr = atoi(PQgetvalue(res, 0, 1));
|
*upstream_node_id_ptr = atoi(PQgetvalue(res, 0, 1));
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_upstream_connection(): conninfo is \"%s\"\n", upstream_conninfo);
|
log_debug("conninfo is: '%s'\n", upstream_conninfo);
|
||||||
upstream_conn = establish_db_connection(upstream_conninfo, false);
|
upstream_conn = establish_db_connection(upstream_conninfo, false);
|
||||||
|
|
||||||
if (PQstatus(upstream_conn) != CONNECTION_OK)
|
if (PQstatus(upstream_conn) != CONNECTION_OK)
|
||||||
@@ -574,103 +487,102 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the node list from the local node and attempt to connect to each node
|
* get a connection to master by reading repl_nodes, creating a connection
|
||||||
* in turn to definitely establish if it's the cluster primary.
|
* to each node (one at a time) and finding if it is a master or a standby
|
||||||
*
|
*
|
||||||
* The node list is returned in the order which makes it likely that the
|
* NB: If master_conninfo_out may be NULL. If it is non-null, it is assumed to
|
||||||
* current primary will be returned first, reducing the number of speculative
|
* point to allocated memory of MAXCONNINFO in length, and the master server
|
||||||
* connections which need to be made to other nodes.
|
* connection string is placed there.
|
||||||
*
|
|
||||||
* If master_conninfo_out points to allocated memory of MAXCONNINFO in length,
|
|
||||||
* the primary server's conninfo string will be copied there.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PGconn *
|
PGconn *
|
||||||
get_master_connection(PGconn *standby_conn, char *cluster,
|
get_master_connection(PGconn *standby_conn, char *cluster,
|
||||||
int *master_id, char *master_conninfo_out)
|
int *master_id, char *master_conninfo_out)
|
||||||
{
|
{
|
||||||
PGconn *remote_conn = NULL;
|
PGconn *master_conn = NULL;
|
||||||
PGresult *res;
|
PGresult *res1;
|
||||||
|
PGresult *res2;
|
||||||
char sqlquery[QUERY_STR_LEN];
|
char sqlquery[QUERY_STR_LEN];
|
||||||
char remote_conninfo_stack[MAXCONNINFO];
|
char master_conninfo_stack[MAXCONNINFO];
|
||||||
char *remote_conninfo = &*remote_conninfo_stack;
|
char *master_conninfo = &*master_conninfo_stack;
|
||||||
|
|
||||||
int i,
|
int i,
|
||||||
node_id;
|
node_id;
|
||||||
|
|
||||||
if (master_id != NULL)
|
if(master_id != NULL)
|
||||||
{
|
{
|
||||||
*master_id = NODE_NOT_FOUND;
|
*master_id = NODE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find all nodes belonging to this cluster */
|
/* find all nodes belonging to this cluster */
|
||||||
log_info(_("retrieving node list for cluster '%s'\n"),
|
log_info(_("finding node list for cluster '%s'\n"),
|
||||||
cluster);
|
cluster);
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery,
|
sqlquery_snprintf(sqlquery,
|
||||||
" SELECT id, conninfo, "
|
"SELECT id, conninfo "
|
||||||
" CASE WHEN type = 'master' THEN 1 ELSE 2 END AS type_priority"
|
" FROM %s.repl_nodes "
|
||||||
" FROM %s.repl_nodes "
|
" WHERE cluster = '%s' "
|
||||||
" WHERE cluster = '%s' "
|
" AND type != 'witness' ",
|
||||||
" AND type != 'witness' "
|
|
||||||
"ORDER BY active DESC, type_priority, priority, id",
|
|
||||||
get_repmgr_schema_quoted(standby_conn),
|
get_repmgr_schema_quoted(standby_conn),
|
||||||
cluster);
|
cluster);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_master_connection():\n%s\n", sqlquery);
|
res1 = PQexec(standby_conn, sqlquery);
|
||||||
|
if (PQresultStatus(res1) != PGRES_TUPLES_OK)
|
||||||
res = PQexec(standby_conn, sqlquery);
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
||||||
{
|
{
|
||||||
log_err(_("unable to retrieve node records: %s\n"),
|
log_err(_("unable to retrieve node records: %s\n"),
|
||||||
PQerrorMessage(standby_conn));
|
PQerrorMessage(standby_conn));
|
||||||
PQclear(res);
|
PQclear(res1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < PQntuples(res); i++)
|
for (i = 0; i < PQntuples(res1); i++)
|
||||||
{
|
{
|
||||||
int is_node_standby;
|
|
||||||
|
|
||||||
/* initialize with the values of the current node being processed */
|
/* initialize with the values of the current node being processed */
|
||||||
node_id = atoi(PQgetvalue(res, i, 0));
|
node_id = atoi(PQgetvalue(res1, i, 0));
|
||||||
strncpy(remote_conninfo, PQgetvalue(res, i, 1), MAXCONNINFO);
|
strncpy(master_conninfo, PQgetvalue(res1, i, 1), MAXCONNINFO);
|
||||||
log_verbose(LOG_INFO,
|
log_info(_("checking role of cluster node '%i'\n"),
|
||||||
_("checking role of cluster node '%i'\n"),
|
node_id);
|
||||||
node_id);
|
master_conn = establish_db_connection(master_conninfo, false);
|
||||||
remote_conn = establish_db_connection(remote_conninfo, false);
|
|
||||||
|
|
||||||
if (PQstatus(remote_conn) != CONNECTION_OK)
|
if (PQstatus(master_conn) != CONNECTION_OK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
is_node_standby = is_standby(remote_conn);
|
/*
|
||||||
|
* Can't use the is_standby() function here because on error that
|
||||||
|
* function closes the connection passed and exits. This still needs
|
||||||
|
* to close master_conn first.
|
||||||
|
*/
|
||||||
|
res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()");
|
||||||
|
|
||||||
if (is_node_standby == -1)
|
if (PQresultStatus(res2) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err(_("unable to retrieve recovery state from node %i:\n%s\n"),
|
log_err(_("unable to retrieve recovery state from this node: %s\n"),
|
||||||
node_id,
|
PQerrorMessage(master_conn));
|
||||||
PQerrorMessage(remote_conn));
|
PQclear(res2);
|
||||||
PQfinish(remote_conn);
|
PQfinish(master_conn);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if is_standby() returns 0, queried node is the master */
|
/* if false, this is the master */
|
||||||
if (is_node_standby == 0)
|
if (strcmp(PQgetvalue(res2, 0, 0), "f") == 0)
|
||||||
{
|
{
|
||||||
PQclear(res);
|
PQclear(res2);
|
||||||
|
PQclear(res1);
|
||||||
log_debug(_("get_master_connection(): current master node is %i\n"), node_id);
|
log_debug(_("get_master_connection(): current master node is %i\n"), node_id);
|
||||||
|
|
||||||
if (master_id != NULL)
|
if(master_id != NULL)
|
||||||
{
|
{
|
||||||
*master_id = node_id;
|
*master_id = node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return remote_conn;
|
return master_conn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* if it is a standby, clear info */
|
||||||
|
PQclear(res2);
|
||||||
|
PQfinish(master_conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* if it is a standby, clear connection info and continue*/
|
|
||||||
PQfinish(remote_conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -681,7 +593,7 @@ get_master_connection(PGconn *standby_conn, char *cluster,
|
|||||||
* Probably we will need to check the error to know if we need to start
|
* Probably we will need to check the error to know if we need to start
|
||||||
* failover procedure or just fix some situation on the standby.
|
* failover procedure or just fix some situation on the standby.
|
||||||
*/
|
*/
|
||||||
PQclear(res);
|
PQclear(res1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +621,7 @@ wait_connection_availability(PGconn *conn, long long timeout)
|
|||||||
{
|
{
|
||||||
if (PQconsumeInput(conn) == 0)
|
if (PQconsumeInput(conn) == 0)
|
||||||
{
|
{
|
||||||
log_warning(_("wait_connection_availability(): could not receive data from connection. %s\n"),
|
log_warning(_("wait_connection_availability: could not receive data from connection. %s\n"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -736,7 +648,7 @@ wait_connection_availability(PGconn *conn, long long timeout)
|
|||||||
if (select(sock, &read_set, NULL, NULL, &tmout) == -1)
|
if (select(sock, &read_set, NULL, NULL, &tmout) == -1)
|
||||||
{
|
{
|
||||||
log_warning(
|
log_warning(
|
||||||
_("wait_connection_availability(): select() returned with error\n%s\n"),
|
_("wait_connection_availability: select() returned with error: %s"),
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -752,7 +664,7 @@ wait_connection_availability(PGconn *conn, long long timeout)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_warning(_("wait_connection_availability(): timeout reached"));
|
log_warning(_("wait_connection_availability: timeout reached"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,12 +699,6 @@ cancel_query(PGconn *conn, int timeout)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return the repmgr schema as an unmodified string
|
|
||||||
* This is useful for displaying the schema name in log messages,
|
|
||||||
* however inclusion in SQL statements, get_repmgr_schema_quoted() should
|
|
||||||
* always be used.
|
|
||||||
*/
|
|
||||||
char *
|
char *
|
||||||
get_repmgr_schema(void)
|
get_repmgr_schema(void)
|
||||||
{
|
{
|
||||||
@@ -803,7 +709,7 @@ get_repmgr_schema(void)
|
|||||||
char *
|
char *
|
||||||
get_repmgr_schema_quoted(PGconn *conn)
|
get_repmgr_schema_quoted(PGconn *conn)
|
||||||
{
|
{
|
||||||
if (strcmp(repmgr_schema_quoted, "") == 0)
|
if(strcmp(repmgr_schema_quoted, "") == 0)
|
||||||
{
|
{
|
||||||
char *identifier = PQescapeIdentifier(conn, repmgr_schema,
|
char *identifier = PQescapeIdentifier(conn, repmgr_schema,
|
||||||
strlen(repmgr_schema));
|
strlen(repmgr_schema));
|
||||||
@@ -834,8 +740,6 @@ create_replication_slot(PGconn *conn, char *slot_name)
|
|||||||
" WHERE slot_name = '%s' ",
|
" WHERE slot_name = '%s' ",
|
||||||
slot_name);
|
slot_name);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "create_replication_slot():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
@@ -845,19 +749,19 @@ create_replication_slot(PGconn *conn, char *slot_name)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PQntuples(res))
|
if(PQntuples(res))
|
||||||
{
|
{
|
||||||
if (strcmp(PQgetvalue(res, 0, 1), "physical") != 0)
|
if(strcmp(PQgetvalue(res, 0, 1), "physical") != 0)
|
||||||
{
|
{
|
||||||
log_err(_("Slot '%s' exists and is not a physical slot\n"),
|
log_err(_("Slot '%s' exists and is not a physical slot\n"),
|
||||||
slot_name);
|
slot_name);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
if (strcmp(PQgetvalue(res, 0, 0), "f") == 0)
|
if(strcmp(PQgetvalue(res, 0, 0), "f") == 0)
|
||||||
{
|
{
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
log_debug("Replication slot '%s' exists but is inactive; reusing\n",
|
log_debug(_("Replication slot '%s' exists but is inactive; reusing\n"),
|
||||||
slot_name);
|
slot_name);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -872,7 +776,6 @@ create_replication_slot(PGconn *conn, char *slot_name)
|
|||||||
slot_name);
|
slot_name);
|
||||||
|
|
||||||
log_debug(_("create_replication_slot(): Creating slot '%s' on primary\n"), slot_name);
|
log_debug(_("create_replication_slot(): Creating slot '%s' on primary\n"), slot_name);
|
||||||
log_verbose(LOG_DEBUG, "create_replication_slot():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
@@ -888,33 +791,6 @@ create_replication_slot(PGconn *conn, char *slot_name)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
drop_replication_slot(PGconn *conn, char *slot_name)
|
|
||||||
{
|
|
||||||
char sqlquery[QUERY_STR_LEN];
|
|
||||||
PGresult *res;
|
|
||||||
sqlquery_snprintf(sqlquery,
|
|
||||||
"SELECT pg_drop_replication_slot('%s')",
|
|
||||||
slot_name);
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "drop_replication_slot():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
|
||||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
log_err(_("unable to drop replication slot \"%s\":\n %s\n"),
|
|
||||||
slot_name,
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "replication slot \"%s\" successfully dropped\n",
|
|
||||||
slot_name);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
||||||
@@ -923,11 +799,11 @@ start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
|||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery,
|
sqlquery_snprintf(sqlquery,
|
||||||
"SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
|
"SELECT pg_xlogfile_name(pg_start_backup('repmgr_standby_clone_%ld', %s))",
|
||||||
time(NULL),
|
time(NULL),
|
||||||
fast_checkpoint ? "TRUE" : "FALSE");
|
fast_checkpoint ? "TRUE" : "FALSE");
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "start_backup():\n%s\n", sqlquery);
|
log_debug(_("standby clone: %s\n"), sqlquery);
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
@@ -942,7 +818,7 @@ start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
|||||||
char *first_wal_seg_pq = PQgetvalue(res, 0, 0);
|
char *first_wal_seg_pq = PQgetvalue(res, 0, 0);
|
||||||
size_t buf_sz = strlen(first_wal_seg_pq);
|
size_t buf_sz = strlen(first_wal_seg_pq);
|
||||||
|
|
||||||
first_wal_segment = pg_malloc0(buf_sz + 1);
|
first_wal_segment = malloc(buf_sz + 1);
|
||||||
xsnprintf(first_wal_segment, buf_sz + 1, "%s", first_wal_seg_pq);
|
xsnprintf(first_wal_segment, buf_sz + 1, "%s", first_wal_seg_pq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,7 +834,7 @@ stop_backup(PGconn *conn, char *last_wal_segment)
|
|||||||
char sqlquery[QUERY_STR_LEN];
|
char sqlquery[QUERY_STR_LEN];
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_stop_backup())");
|
sqlquery_snprintf(sqlquery, "SELECT pg_xlogfile_name(pg_stop_backup())");
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
@@ -973,7 +849,7 @@ stop_backup(PGconn *conn, char *last_wal_segment)
|
|||||||
char *last_wal_seg_pq = PQgetvalue(res, 0, 0);
|
char *last_wal_seg_pq = PQgetvalue(res, 0, 0);
|
||||||
size_t buf_sz = strlen(last_wal_seg_pq);
|
size_t buf_sz = strlen(last_wal_seg_pq);
|
||||||
|
|
||||||
last_wal_segment = pg_malloc0(buf_sz + 1);
|
last_wal_segment = malloc(buf_sz + 1);
|
||||||
xsnprintf(last_wal_segment, buf_sz + 1, "%s", last_wal_seg_pq);
|
xsnprintf(last_wal_segment, buf_sz + 1, "%s", last_wal_seg_pq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,8 +870,6 @@ set_config_bool(PGconn *conn, const char *config_param, bool state)
|
|||||||
config_param,
|
config_param,
|
||||||
state ? "TRUE" : "FALSE");
|
state ? "TRUE" : "FALSE");
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "set_config_bool():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
@@ -1027,13 +901,11 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery, "TRUNCATE TABLE %s.repl_nodes", get_repmgr_schema_quoted(witnessconn));
|
sqlquery_snprintf(sqlquery, "TRUNCATE TABLE %s.repl_nodes", get_repmgr_schema_quoted(witnessconn));
|
||||||
|
log_debug("copy_configuration: %s\n", sqlquery);
|
||||||
log_verbose(LOG_DEBUG, "copy_configuration():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(witnessconn, sqlquery);
|
res = PQexec(witnessconn, sqlquery);
|
||||||
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
{
|
{
|
||||||
log_err(_("Unable to truncate witness servers's repl_nodes table:\n%s\n"),
|
fprintf(stderr, "Cannot clean node details in the witness, %s\n",
|
||||||
PQerrorMessage(witnessconn));
|
PQerrorMessage(witnessconn));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1041,13 +913,10 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
|||||||
sqlquery_snprintf(sqlquery,
|
sqlquery_snprintf(sqlquery,
|
||||||
"SELECT id, type, upstream_node_id, name, conninfo, priority, slot_name FROM %s.repl_nodes",
|
"SELECT id, type, upstream_node_id, name, conninfo, priority, slot_name FROM %s.repl_nodes",
|
||||||
get_repmgr_schema_quoted(masterconn));
|
get_repmgr_schema_quoted(masterconn));
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "copy_configuration():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(masterconn, sqlquery);
|
res = PQexec(masterconn, sqlquery);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_err("Unable to retrieve node records from master:\n%s\n",
|
fprintf(stderr, "Can't get configuration from master: %s\n",
|
||||||
PQerrorMessage(masterconn));
|
PQerrorMessage(masterconn));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return false;
|
return false;
|
||||||
@@ -1056,11 +925,9 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
|||||||
for (i = 0; i < PQntuples(res); i++)
|
for (i = 0; i < PQntuples(res); i++)
|
||||||
{
|
{
|
||||||
bool node_record_created;
|
bool node_record_created;
|
||||||
|
char *witness = PQgetvalue(res, i, 4);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG,
|
log_debug(_("copy_configuration(): %s\n"), witness);
|
||||||
"copy_configuration(): writing node record for node %s (id: %s)\n",
|
|
||||||
PQgetvalue(res, i, 4),
|
|
||||||
PQgetvalue(res, i, 0));
|
|
||||||
|
|
||||||
node_record_created = create_node_record(witnessconn,
|
node_record_created = create_node_record(witnessconn,
|
||||||
"copy_configuration",
|
"copy_configuration",
|
||||||
@@ -1080,9 +947,7 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
|||||||
|
|
||||||
if (node_record_created == false)
|
if (node_record_created == false)
|
||||||
{
|
{
|
||||||
PQclear(res);
|
fprintf(stderr, "Unable to copy node record to witness database: %s\n",
|
||||||
|
|
||||||
log_err("Unable to copy node record to witness database\n%s\n",
|
|
||||||
PQerrorMessage(witnessconn));
|
PQerrorMessage(witnessconn));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1108,13 +973,13 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
|||||||
char slot_name_buf[MAXLEN];
|
char slot_name_buf[MAXLEN];
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
if (upstream_node == NO_UPSTREAM_NODE)
|
if(upstream_node == NO_UPSTREAM_NODE)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* No explicit upstream node id provided for standby - attempt to
|
* No explicit upstream node id provided for standby - attempt to
|
||||||
* get primary node id
|
* get primary node id
|
||||||
*/
|
*/
|
||||||
if (strcmp(type, "standby") == 0)
|
if(strcmp(type, "standby") == 0)
|
||||||
{
|
{
|
||||||
int primary_node_id = get_master_node_id(conn, cluster_name);
|
int primary_node_id = get_master_node_id(conn, cluster_name);
|
||||||
maxlen_snprintf(upstream_node_id, "%i", primary_node_id);
|
maxlen_snprintf(upstream_node_id, "%i", primary_node_id);
|
||||||
@@ -1129,7 +994,7 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
|||||||
maxlen_snprintf(upstream_node_id, "%i", upstream_node);
|
maxlen_snprintf(upstream_node_id, "%i", upstream_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slot_name != NULL && slot_name[0])
|
if(slot_name != NULL && slot_name[0])
|
||||||
{
|
{
|
||||||
maxlen_snprintf(slot_name_buf, "'%s'", slot_name);
|
maxlen_snprintf(slot_name_buf, "'%s'", slot_name);
|
||||||
}
|
}
|
||||||
@@ -1138,7 +1003,6 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
|||||||
maxlen_snprintf(slot_name_buf, "%s", "NULL");
|
maxlen_snprintf(slot_name_buf, "%s", "NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX convert to placeholder query */
|
|
||||||
sqlquery_snprintf(sqlquery,
|
sqlquery_snprintf(sqlquery,
|
||||||
"INSERT INTO %s.repl_nodes "
|
"INSERT INTO %s.repl_nodes "
|
||||||
" (id, type, upstream_node_id, cluster, "
|
" (id, type, upstream_node_id, cluster, "
|
||||||
@@ -1154,18 +1018,16 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
|||||||
slot_name_buf,
|
slot_name_buf,
|
||||||
priority);
|
priority);
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "create_node_record(): %s\n", sqlquery);
|
if(action != NULL)
|
||||||
|
|
||||||
if (action != NULL)
|
|
||||||
{
|
{
|
||||||
log_verbose(LOG_DEBUG, "create_node_record(): action is \"%s\"\n", action);
|
log_debug(_("%s: %s\n"), action, sqlquery);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
{
|
{
|
||||||
log_err(_("Unable to create node record\n%s\n"),
|
log_warning(_("Unable to create node record: %s\n"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1187,18 +1049,15 @@ delete_node_record(PGconn *conn, int node, char *action)
|
|||||||
" WHERE id = %d",
|
" WHERE id = %d",
|
||||||
get_repmgr_schema_quoted(conn),
|
get_repmgr_schema_quoted(conn),
|
||||||
node);
|
node);
|
||||||
|
if(action != NULL)
|
||||||
log_verbose(LOG_DEBUG, "delete_node_record(): %s\n", sqlquery);
|
|
||||||
|
|
||||||
if (action != NULL)
|
|
||||||
{
|
{
|
||||||
log_verbose(LOG_DEBUG, "create_node_record(): action is \"%s\"\n", action);
|
log_debug(_("%s: %s\n"), action, sqlquery);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
res = PQexec(conn, sqlquery);
|
||||||
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
{
|
{
|
||||||
log_err(_("Unable to delete node record: %s\n"),
|
log_warning(_("Unable to delete node record: %s\n"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return false;
|
return false;
|
||||||
@@ -1221,8 +1080,8 @@ delete_node_record(PGconn *conn, int node, char *action)
|
|||||||
*
|
*
|
||||||
* Note this function may be called with `conn` set to NULL in cases where
|
* Note this function may be called with `conn` set to NULL in cases where
|
||||||
* the master node is not available and it's therefore not possible to write
|
* the master node is not available and it's therefore not possible to write
|
||||||
* an event record. In this case, if `event_notification_command` is set, a
|
* an event record. In this case, if `event_notification_command` is set a user-
|
||||||
* user-defined notification to be generated; if not, this function will have
|
* defined notification to be generated; if not, this function will have
|
||||||
* no effect.
|
* no effect.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -1235,12 +1094,12 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
bool success = true;
|
bool success = true;
|
||||||
struct tm ts;
|
struct tm ts;
|
||||||
|
|
||||||
/* Only attempt to write a record if a connection handle was provided.
|
/* Only attempt to write a record if a connection handle was provided/
|
||||||
Also check that the repmgr schema has been properly intialised - if
|
Also check that the repmgr schema has been properly intialised - if
|
||||||
not it means no configuration file was provided, which can happen with
|
not it means no configuration file was provided, which can happen with
|
||||||
e.g. `repmgr standby clone`, and we won't know which schema to write to.
|
e.g. `repmgr standby clone`, and we won't know which schema to write to.
|
||||||
*/
|
*/
|
||||||
if (conn != NULL && strcmp(repmgr_schema, DEFAULT_REPMGR_SCHEMA_PREFIX) != 0)
|
if(conn != NULL && strcmp(repmgr_schema, DEFAULT_REPMGR_SCHEMA_PREFIX) != 0)
|
||||||
{
|
{
|
||||||
int n_node_id = htonl(node_id);
|
int n_node_id = htonl(node_id);
|
||||||
char *t_successful = successful ? "TRUE" : "FALSE";
|
char *t_successful = successful ? "TRUE" : "FALSE";
|
||||||
@@ -1270,8 +1129,6 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
" RETURNING event_timestamp ",
|
" RETURNING event_timestamp ",
|
||||||
get_repmgr_schema_quoted(conn));
|
get_repmgr_schema_quoted(conn));
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "create_event_record():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexecParams(conn,
|
res = PQexecParams(conn,
|
||||||
sqlquery,
|
sqlquery,
|
||||||
4,
|
4,
|
||||||
@@ -1283,6 +1140,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
|
|
||||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
|
|
||||||
log_warning(_("Unable to create event record: %s\n"),
|
log_warning(_("Unable to create event record: %s\n"),
|
||||||
PQerrorMessage(conn));
|
PQerrorMessage(conn));
|
||||||
|
|
||||||
@@ -1293,7 +1151,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
{
|
{
|
||||||
/* Store timestamp to send to the notification command */
|
/* Store timestamp to send to the notification command */
|
||||||
strncpy(event_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
|
strncpy(event_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
|
||||||
log_verbose(LOG_DEBUG, "create_event_record(): Event timestamp is \"%s\"\n", event_timestamp);
|
log_debug(_("Event timestamp is: %s\n"), event_timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
@@ -1304,7 +1162,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
* current timestamp ourselves. This isn't quite the same
|
* current timestamp ourselves. This isn't quite the same
|
||||||
* format as PostgreSQL, but is close enough for diagnostic use.
|
* format as PostgreSQL, but is close enough for diagnostic use.
|
||||||
*/
|
*/
|
||||||
if (!strlen(event_timestamp))
|
if(!strlen(event_timestamp))
|
||||||
{
|
{
|
||||||
time_t now;
|
time_t now;
|
||||||
|
|
||||||
@@ -1314,7 +1172,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* an event notification command was provided - parse and execute it */
|
/* an event notification command was provided - parse and execute it */
|
||||||
if (strlen(options->event_notification_command))
|
if(strlen(options->event_notification_command))
|
||||||
{
|
{
|
||||||
char parsed_command[MAXPGPATH];
|
char parsed_command[MAXPGPATH];
|
||||||
const char *src_ptr;
|
const char *src_ptr;
|
||||||
@@ -1330,14 +1188,14 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
* (If 'event_notifications' was not provided, we assume the script
|
* (If 'event_notifications' was not provided, we assume the script
|
||||||
* should be executed for all events).
|
* should be executed for all events).
|
||||||
*/
|
*/
|
||||||
if (options->event_notifications.head != NULL)
|
if(options->event_notifications.head != NULL)
|
||||||
{
|
{
|
||||||
EventNotificationListCell *cell;
|
EventNotificationListCell *cell;
|
||||||
bool notify_ok = false;
|
bool notify_ok = false;
|
||||||
|
|
||||||
for (cell = options->event_notifications.head; cell; cell = cell->next)
|
for (cell = options->event_notifications.head; cell; cell = cell->next)
|
||||||
{
|
{
|
||||||
if (strcmp(event, cell->event_type) == 0)
|
if(strcmp(event, cell->event_type) == 0)
|
||||||
{
|
{
|
||||||
notify_ok = true;
|
notify_ok = true;
|
||||||
break;
|
break;
|
||||||
@@ -1347,7 +1205,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
/*
|
/*
|
||||||
* Event type not found in the 'event_notifications' list - return early
|
* Event type not found in the 'event_notifications' list - return early
|
||||||
*/
|
*/
|
||||||
if (notify_ok == false)
|
if(notify_ok == false)
|
||||||
{
|
{
|
||||||
log_debug(_("Not executing notification script for event type '%s'\n"), event);
|
log_debug(_("Not executing notification script for event type '%s'\n"), event);
|
||||||
return success;
|
return success;
|
||||||
@@ -1379,7 +1237,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
case 'd':
|
case 'd':
|
||||||
/* %d: details */
|
/* %d: details */
|
||||||
src_ptr++;
|
src_ptr++;
|
||||||
if (details != NULL)
|
if(details != NULL)
|
||||||
{
|
{
|
||||||
strlcpy(dst_ptr, details, end_ptr - dst_ptr);
|
strlcpy(dst_ptr, details, end_ptr - dst_ptr);
|
||||||
dst_ptr += strlen(dst_ptr);
|
dst_ptr += strlen(dst_ptr);
|
||||||
@@ -1413,116 +1271,15 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
|||||||
|
|
||||||
*dst_ptr = '\0';
|
*dst_ptr = '\0';
|
||||||
|
|
||||||
log_debug("create_event_record(): executing\n%s\n", parsed_command);
|
log_debug(_("Executing: %s\n"), parsed_command);
|
||||||
|
|
||||||
r = system(parsed_command);
|
r = system(parsed_command);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
{
|
{
|
||||||
log_warning(_("Unable to execute event notification command\n"));
|
log_warning(_("Unable to execute event notification command\n"));
|
||||||
log_info(_("Parsed event notification command was:\n%s\n"), parsed_command);
|
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Update node record following change of status
|
|
||||||
* (e.g. inactive primary converted to standby)
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
update_node_record_status(PGconn *conn, char *cluster_name, int this_node_id, char *type, int upstream_node_id, bool active)
|
|
||||||
{
|
|
||||||
PGresult *res;
|
|
||||||
char sqlquery[QUERY_STR_LEN];
|
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery,
|
|
||||||
" UPDATE %s.repl_nodes "
|
|
||||||
" SET type = '%s', "
|
|
||||||
" upstream_node_id = %i, "
|
|
||||||
" active = %s "
|
|
||||||
" WHERE cluster = '%s' "
|
|
||||||
" AND id = %i ",
|
|
||||||
get_repmgr_schema_quoted(conn),
|
|
||||||
type,
|
|
||||||
upstream_node_id,
|
|
||||||
active ? "TRUE" : "FALSE",
|
|
||||||
cluster_name,
|
|
||||||
this_node_id);
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "update_node_record_status():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
log_err(_("Unable to update node record: %s\n"),
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
update_node_record_set_upstream(PGconn *conn, char *cluster_name, int this_node_id, int new_upstream_node_id)
|
|
||||||
{
|
|
||||||
PGresult *res;
|
|
||||||
char sqlquery[QUERY_STR_LEN];
|
|
||||||
|
|
||||||
log_debug(_("update_node_record_set_upstream(): Updating node %i's upstream node to %i\n"), this_node_id, new_upstream_node_id);
|
|
||||||
|
|
||||||
sqlquery_snprintf(sqlquery,
|
|
||||||
" UPDATE %s.repl_nodes "
|
|
||||||
" SET upstream_node_id = %i "
|
|
||||||
" WHERE cluster = '%s' "
|
|
||||||
" AND id = %i ",
|
|
||||||
get_repmgr_schema_quoted(conn),
|
|
||||||
new_upstream_node_id,
|
|
||||||
cluster_name,
|
|
||||||
this_node_id);
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "update_node_record_set_upstream():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
res = PQexec(conn, sqlquery);
|
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
log_err(_("Unable to set new upstream node id: %s\n"),
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PGresult *
|
|
||||||
get_node_record(PGconn *conn, char *cluster, int node_id)
|
|
||||||
{
|
|
||||||
char sqlquery[QUERY_STR_LEN];
|
|
||||||
|
|
||||||
sprintf(sqlquery,
|
|
||||||
"SELECT id, upstream_node_id, conninfo, type, slot_name, active "
|
|
||||||
" FROM %s.repl_nodes "
|
|
||||||
" WHERE cluster = '%s' "
|
|
||||||
" AND id = %i",
|
|
||||||
get_repmgr_schema_quoted(conn),
|
|
||||||
cluster,
|
|
||||||
node_id);
|
|
||||||
|
|
||||||
log_verbose(LOG_DEBUG, "get_node_record():\n%s\n", sqlquery);
|
|
||||||
|
|
||||||
return PQexec(conn, sqlquery);
|
|
||||||
}
|
|
||||||
|
|||||||
51
dbutils.h
51
dbutils.h
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* dbutils.h
|
* dbutils.h
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,60 +20,16 @@
|
|||||||
#ifndef _REPMGR_DBUTILS_H_
|
#ifndef _REPMGR_DBUTILS_H_
|
||||||
#define _REPMGR_DBUTILS_H_
|
#define _REPMGR_DBUTILS_H_
|
||||||
|
|
||||||
#include "access/xlogdefs.h"
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
UNKNOWN = 0,
|
|
||||||
MASTER,
|
|
||||||
STANDBY,
|
|
||||||
WITNESS
|
|
||||||
} t_server_type;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Struct to store node information
|
|
||||||
*/
|
|
||||||
typedef struct s_node_info
|
|
||||||
{
|
|
||||||
int node_id;
|
|
||||||
int upstream_node_id;
|
|
||||||
t_server_type type;
|
|
||||||
char name[MAXLEN];
|
|
||||||
char conninfo_str[MAXLEN];
|
|
||||||
char slot_name[MAXLEN];
|
|
||||||
int priority;
|
|
||||||
bool active;
|
|
||||||
bool is_ready;
|
|
||||||
bool is_visible;
|
|
||||||
XLogRecPtr xlog_location;
|
|
||||||
} t_node_info;
|
|
||||||
|
|
||||||
|
|
||||||
#define T_NODE_INFO_INITIALIZER { \
|
|
||||||
NODE_NOT_FOUND, \
|
|
||||||
NO_UPSTREAM_NODE, \
|
|
||||||
UNKNOWN, \
|
|
||||||
"", \
|
|
||||||
"", \
|
|
||||||
"", \
|
|
||||||
DEFAULT_PRIORITY, \
|
|
||||||
true, \
|
|
||||||
false, \
|
|
||||||
false, \
|
|
||||||
InvalidXLogRecPtr \
|
|
||||||
}
|
|
||||||
|
|
||||||
PGconn *establish_db_connection(const char *conninfo,
|
PGconn *establish_db_connection(const char *conninfo,
|
||||||
const bool exit_on_error);
|
const bool exit_on_error);
|
||||||
PGconn *establish_db_connection_by_params(const char *keywords[],
|
PGconn *establish_db_connection_by_params(const char *keywords[],
|
||||||
const char *values[],
|
const char *values[],
|
||||||
const bool exit_on_error);
|
const bool exit_on_error);
|
||||||
bool begin_transaction(PGconn *conn);
|
|
||||||
bool commit_transaction(PGconn *conn);
|
|
||||||
bool rollback_transaction(PGconn *conn);
|
|
||||||
bool check_cluster_schema(PGconn *conn);
|
bool check_cluster_schema(PGconn *conn);
|
||||||
int is_standby(PGconn *conn);
|
int is_standby(PGconn *conn);
|
||||||
bool is_pgup(PGconn *conn, int timeout);
|
bool is_pgup(PGconn *conn, int timeout);
|
||||||
@@ -99,7 +55,6 @@ bool cancel_query(PGconn *conn, int timeout);
|
|||||||
char *get_repmgr_schema(void);
|
char *get_repmgr_schema(void);
|
||||||
char *get_repmgr_schema_quoted(PGconn *conn);
|
char *get_repmgr_schema_quoted(PGconn *conn);
|
||||||
bool create_replication_slot(PGconn *conn, char *slot_name);
|
bool create_replication_slot(PGconn *conn, char *slot_name);
|
||||||
bool drop_replication_slot(PGconn *conn, char *slot_name);
|
|
||||||
|
|
||||||
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint);
|
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint);
|
||||||
bool stop_backup(PGconn *conn, char *last_wal_segment);
|
bool stop_backup(PGconn *conn, char *last_wal_segment);
|
||||||
@@ -108,8 +63,6 @@ bool copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_
|
|||||||
bool create_node_record(PGconn *conn, char *action, int node, char *type, int upstream_node, char *cluster_name, char *node_name, char *conninfo, int priority, char *slot_name);
|
bool create_node_record(PGconn *conn, char *action, int node, char *type, int upstream_node, char *cluster_name, char *node_name, char *conninfo, int priority, char *slot_name);
|
||||||
bool delete_node_record(PGconn *conn, int node, char *action);
|
bool delete_node_record(PGconn *conn, int node, char *action);
|
||||||
bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);
|
bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);
|
||||||
bool update_node_record_status(PGconn *conn, char *cluster_name, int this_node_id, char *type, int upstream_node_id, bool active);
|
|
||||||
bool update_node_record_set_upstream(PGconn *conn, char *cluster_name, int this_node_id, int new_upstream_node_id);
|
|
||||||
PGresult * get_node_record(PGconn *conn, char *cluster, int node_id);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
4
debian/repmgr.repmgrd.default
vendored
4
debian/repmgr.repmgrd.default
vendored
@@ -12,7 +12,7 @@ REPMGRD_ENABLED=no
|
|||||||
#REPMGRD_USER=postgres
|
#REPMGRD_USER=postgres
|
||||||
|
|
||||||
# repmgrd binary
|
# repmgrd binary
|
||||||
#REPMGRD_BIN=/usr/bin/repmgrd
|
#REPMGR_BIN=/usr/bin/repmgr
|
||||||
|
|
||||||
# pid file
|
# pid file
|
||||||
#REPMGRD_PIDFILE=/var/run/repmgrd.pid
|
#REPMGR_PIDFILE=/var/run/repmgrd.pid
|
||||||
|
|||||||
2
debian/repmgr.repmgrd.init
vendored
2
debian/repmgr.repmgrd.init
vendored
@@ -59,7 +59,7 @@ do_stop()
|
|||||||
# 0 if daemon has been stopped
|
# 0 if daemon has been stopped
|
||||||
# 1 if daemon was already stopped
|
# 1 if daemon was already stopped
|
||||||
# other if daemon could not be stopped or a failure occurred
|
# other if daemon could not be stopped or a failure occurred
|
||||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $REPMGRD_PIDFILE --name "$(basename $REPMGRD_BIN)"
|
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $REPMGRD_PIDFILE --exec $REPMGRD_BIN
|
||||||
}
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* errcode.h
|
* errcode.h
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -35,7 +35,5 @@
|
|||||||
#define ERR_BAD_SSH 12
|
#define ERR_BAD_SSH 12
|
||||||
#define ERR_SYS_FAILURE 13
|
#define ERR_SYS_FAILURE 13
|
||||||
#define ERR_BAD_BASEBACKUP 14
|
#define ERR_BAD_BASEBACKUP 14
|
||||||
#define ERR_INTERNAL 15
|
|
||||||
#define ERR_MONITORING_FAIL 16
|
|
||||||
|
|
||||||
#endif /* _ERRCODE_H_ */
|
#endif /* _ERRCODE_H_ */
|
||||||
|
|||||||
154
log.c
154
log.c
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* log.c - Logging methods
|
* log.c - Logging methods
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This module is a set of methods for logging (currently only syslog)
|
* This module is a set of methods for logging (currently only syslog)
|
||||||
*
|
*
|
||||||
@@ -39,37 +39,13 @@
|
|||||||
|
|
||||||
/* #define REPMGR_DEBUG */
|
/* #define REPMGR_DEBUG */
|
||||||
|
|
||||||
static int detect_log_facility(const char *facility);
|
|
||||||
static void _stderr_log_with_level(const char *level_name, int level, const char *fmt, va_list ap);
|
|
||||||
|
|
||||||
int log_type = REPMGR_STDERR;
|
|
||||||
int log_level = LOG_NOTICE;
|
|
||||||
int last_log_level = LOG_NOTICE;
|
|
||||||
int verbose_logging = false;
|
|
||||||
int terse_logging = false;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
stderr_log_with_level(const char *level_name, int level, const char *fmt, ...)
|
stderr_log_with_level(const char *level_name, int level, const char *fmt, ...)
|
||||||
{
|
|
||||||
va_list arglist;
|
|
||||||
|
|
||||||
va_start(arglist, fmt);
|
|
||||||
_stderr_log_with_level(level_name, level, fmt, arglist);
|
|
||||||
va_end(arglist);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_stderr_log_with_level(const char *level_name, int level, const char *fmt, va_list ap)
|
|
||||||
{
|
{
|
||||||
time_t t;
|
time_t t;
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
char buff[100];
|
char buff[100];
|
||||||
|
va_list ap;
|
||||||
/*
|
|
||||||
* Store the requested level so that if there's a subsequent
|
|
||||||
* log_hint(), we can suppress that if appropriate.
|
|
||||||
*/
|
|
||||||
last_log_level = level;
|
|
||||||
|
|
||||||
if (log_level >= level)
|
if (log_level >= level)
|
||||||
{
|
{
|
||||||
@@ -78,74 +54,24 @@ _stderr_log_with_level(const char *level_name, int level, const char *fmt, va_li
|
|||||||
strftime(buff, 100, "[%Y-%m-%d %H:%M:%S]", tm);
|
strftime(buff, 100, "[%Y-%m-%d %H:%M:%S]", tm);
|
||||||
fprintf(stderr, "%s [%s] ", buff, level_name);
|
fprintf(stderr, "%s [%s] ", buff, level_name);
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
log_hint(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
if (terse_logging == false)
|
static int detect_log_level(const char *level);
|
||||||
{
|
static int detect_log_facility(const char *facility);
|
||||||
va_start(ap, fmt);
|
|
||||||
_stderr_log_with_level("HINT", last_log_level, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
log_verbose(int level, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
|
|
||||||
if (verbose_logging == true)
|
|
||||||
{
|
|
||||||
switch(level)
|
|
||||||
{
|
|
||||||
case LOG_EMERG:
|
|
||||||
_stderr_log_with_level("EMERG", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_ALERT:
|
|
||||||
_stderr_log_with_level("ALERT", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_CRIT:
|
|
||||||
_stderr_log_with_level("CRIT", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_ERR:
|
|
||||||
_stderr_log_with_level("ERR", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_WARNING:
|
|
||||||
_stderr_log_with_level("WARNING", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_NOTICE:
|
|
||||||
_stderr_log_with_level("NOTICE", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_INFO:
|
|
||||||
_stderr_log_with_level("INFO", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
case LOG_DEBUG:
|
|
||||||
_stderr_log_with_level("DEBUG", level, fmt, ap);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
int log_type = REPMGR_STDERR;
|
||||||
|
int log_level = LOG_NOTICE;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
logger_init(t_configuration_options * opts, const char *ident)
|
logger_init(t_configuration_options * opts, const char *ident, const char *level, const char *facility)
|
||||||
{
|
{
|
||||||
char *level = opts->loglevel;
|
|
||||||
char *facility = opts->logfacility;
|
|
||||||
|
|
||||||
int l;
|
int l;
|
||||||
int f;
|
int f;
|
||||||
|
|
||||||
@@ -169,10 +95,10 @@ logger_init(t_configuration_options * opts, const char *ident)
|
|||||||
printf("Assigned level for logger: %d\n", l);
|
printf("Assigned level for logger: %d\n", l);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (l >= 0)
|
if (l > 0)
|
||||||
log_level = l;
|
log_level = l;
|
||||||
else
|
else
|
||||||
stderr_log_warning(_("Invalid log level \"%s\" (available values: DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG)\n"), level);
|
stderr_log_warning(_("Cannot detect log level %s (use any of DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG)\n"), level);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (facility && *facility)
|
if (facility && *facility)
|
||||||
@@ -218,38 +144,18 @@ logger_init(t_configuration_options * opts, const char *ident)
|
|||||||
{
|
{
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
|
|
||||||
/* Check if we can write to the specified file before redirecting
|
|
||||||
* stderr - if freopen() fails, stderr output will vanish into
|
|
||||||
* the ether and the user won't know what's going on.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fd = fopen(opts->logfile, "a");
|
|
||||||
if (fd == NULL)
|
|
||||||
{
|
|
||||||
stderr_log_err(_("Unable to open specified logfile '%s' for writing: %s\n"), opts->logfile, strerror(errno));
|
|
||||||
stderr_log_err(_("Terminating\n"));
|
|
||||||
exit(ERR_BAD_CONFIG);
|
|
||||||
}
|
|
||||||
fclose(fd);
|
|
||||||
|
|
||||||
stderr_log_notice(_("Redirecting logging output to '%s'\n"), opts->logfile);
|
|
||||||
fd = freopen(opts->logfile, "a", stderr);
|
fd = freopen(opts->logfile, "a", stderr);
|
||||||
|
|
||||||
/* It's possible freopen() may still fail due to e.g. a race condition;
|
|
||||||
as it's not feasible to restore stderr after a failed freopen(),
|
|
||||||
we'll write to stdout as a last resort.
|
|
||||||
*/
|
|
||||||
if (fd == NULL)
|
if (fd == NULL)
|
||||||
{
|
{
|
||||||
printf(_("Unable to open specified logfile %s for writing: %s\n"), opts->logfile, strerror(errno));
|
fprintf(stderr, "error reopening stderr to '%s': %s",
|
||||||
printf(_("Terminating\n"));
|
opts->logfile, strerror(errno));
|
||||||
exit(ERR_BAD_CONFIG);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
logger_shutdown(void)
|
logger_shutdown(void)
|
||||||
@@ -263,32 +169,17 @@ logger_shutdown(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Indicate whether extra-verbose logging is required. This will
|
* Set a minimum logging level. Intended for command line verbosity
|
||||||
* generate a lot of output, particularly debug logging, and should
|
* options, which might increase requested logging over what's specified
|
||||||
* not be permanently enabled in production.
|
* in the regular configuration file.
|
||||||
*
|
|
||||||
* NOTE: in previous repmgr versions, this option forced the log
|
|
||||||
* level to INFO.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
logger_set_verbose(void)
|
logger_min_verbose(int minimum)
|
||||||
{
|
{
|
||||||
verbose_logging = true;
|
if (log_level < minimum)
|
||||||
|
log_level = minimum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Indicate whether some non-critical log messages can be omitted.
|
|
||||||
* Currently this includes warnings about irrelevant command line
|
|
||||||
* options and hints.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void logger_set_terse(void)
|
|
||||||
{
|
|
||||||
terse_logging = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
detect_log_level(const char *level)
|
detect_log_level(const char *level)
|
||||||
{
|
{
|
||||||
@@ -309,16 +200,17 @@ detect_log_level(const char *level)
|
|||||||
if (!strcmp(level, "EMERG"))
|
if (!strcmp(level, "EMERG"))
|
||||||
return LOG_EMERG;
|
return LOG_EMERG;
|
||||||
|
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
detect_log_facility(const char *facility)
|
detect_log_facility(const char *facility)
|
||||||
{
|
{
|
||||||
int local = 0;
|
int local = 0;
|
||||||
|
|
||||||
if (!strncmp(facility, "LOCAL", 5) && strlen(facility) == 6)
|
if (!strncmp(facility, "LOCAL", 5) && strlen(facility) == 6)
|
||||||
{
|
{
|
||||||
|
|
||||||
local = atoi(&facility[5]);
|
local = atoi(&facility[5]);
|
||||||
|
|
||||||
switch (local)
|
switch (local)
|
||||||
|
|||||||
14
log.h
14
log.h
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* log.h
|
* log.h
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -112,19 +112,13 @@ __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int detect_log_level(const char *level);
|
|
||||||
|
|
||||||
/* Logger initialisation and shutdown */
|
/* Logger initialisation and shutdown */
|
||||||
|
|
||||||
bool logger_init(t_configuration_options * opts, const char *ident);
|
|
||||||
|
|
||||||
bool logger_shutdown(void);
|
bool logger_shutdown(void);
|
||||||
|
|
||||||
void logger_set_verbose(void);
|
bool logger_init(t_configuration_options * opts, const char *ident,
|
||||||
void logger_set_terse(void);
|
const char *level, const char *facility);
|
||||||
|
|
||||||
void log_hint(const char *fmt, ...);
|
void logger_min_verbose(int minimum);
|
||||||
void log_verbose(int level, const char *fmt, ...);
|
|
||||||
|
|
||||||
extern int log_type;
|
extern int log_type;
|
||||||
extern int log_level;
|
extern int log_level;
|
||||||
|
|||||||
@@ -16,15 +16,11 @@ cluster=example_cluster
|
|||||||
# Node ID and name
|
# Node ID and name
|
||||||
# (Note: we recommend to avoid naming nodes after their initial
|
# (Note: we recommend to avoid naming nodes after their initial
|
||||||
# replication funcion, as this will cause confusion when e.g.
|
# replication funcion, as this will cause confusion when e.g.
|
||||||
# "standby2" is promoted to primary)
|
# "standby2" is promoted to master)
|
||||||
node=2 # a unique integer
|
node=2
|
||||||
node_name=node2 # an arbitrary (but unique) string; we recommend using
|
node_name=node2
|
||||||
# the server's hostname or another identifier unambiguously
|
|
||||||
# associated with the server to avoid confusion
|
|
||||||
|
|
||||||
# Database connection information as a conninfo string
|
# Database connection information
|
||||||
# This must be accessible to all servers in the cluster; for details see:
|
|
||||||
# http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
|
||||||
conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
|
conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
|
||||||
|
|
||||||
# Optional configuration items
|
# Optional configuration items
|
||||||
@@ -36,7 +32,7 @@ conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
|
|||||||
# when using cascading replication and a standby is to be connected to an
|
# when using cascading replication and a standby is to be connected to an
|
||||||
# upstream standby, specify that node's ID with 'upstream_node'. The node
|
# upstream standby, specify that node's ID with 'upstream_node'. The node
|
||||||
# must exist before the new standby can be registered. If a standby is
|
# must exist before the new standby can be registered. If a standby is
|
||||||
# to connect directly to a primary node, this parameter is not required.
|
# to connect directly to a master node, this parameter is not required.
|
||||||
#
|
#
|
||||||
# upstream_node=1
|
# upstream_node=1
|
||||||
|
|
||||||
@@ -44,9 +40,7 @@ conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
|
|||||||
# (default: 0)
|
# (default: 0)
|
||||||
#
|
#
|
||||||
# use_replication_slots=0
|
# use_replication_slots=0
|
||||||
#
|
|
||||||
# NOTE: 'max_replication_slots' should be configured for at least the
|
|
||||||
# number of standbys which will connect to the primary.
|
|
||||||
|
|
||||||
# Logging and monitoring settings
|
# Logging and monitoring settings
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
@@ -116,29 +110,28 @@ logfacility=STDERR
|
|||||||
#
|
#
|
||||||
# These settings are only applied when repmgrd is running.
|
# These settings are only applied when repmgrd is running.
|
||||||
|
|
||||||
# Number of seconds to wait for a response from the primary server before
|
# How many seconds we wait for master response before declaring master failure
|
||||||
# deciding it has failed
|
|
||||||
|
|
||||||
master_response_timeout=60
|
master_response_timeout=60
|
||||||
|
|
||||||
# Number of times to try and reconnect to the primary before starting
|
# How many time we try to reconnect to master before starting failover procedure
|
||||||
# the failover procedure
|
|
||||||
reconnect_attempts=6
|
reconnect_attempts=6
|
||||||
reconnect_interval=10
|
reconnect_interval=10
|
||||||
|
|
||||||
# Autofailover options
|
# Autofailover options
|
||||||
failover=automatic # one of 'automatic', 'manual'
|
failover=automatic # one of 'automatic', 'manual'
|
||||||
priority=100 # a value of zero or less prevents the node being promoted to primary
|
priority=100 # a value of zero or less prevents the node being promoted to master
|
||||||
promote_command='repmgr standby promote -f /path/to/repmgr.conf'
|
promote_command='repmgr standby promote -f /path/to/repmgr.conf'
|
||||||
follow_command='repmgr standby follow -f /path/to/repmgr.conf -W'
|
follow_command='repmgr standby follow -f /path/to/repmgr.conf -W'
|
||||||
|
|
||||||
# monitoring interval in seconds; default is 2
|
# monitoring interval; default is 2s
|
||||||
#
|
#
|
||||||
# monitor_interval_secs=2
|
# monitor_interval_secs=2
|
||||||
|
|
||||||
# change wait time for primary; before we bail out and exit when the primary
|
# change wait time for master; before we bail out and exit when the master
|
||||||
# disappears, we wait 'reconnect_attempts' * 'retry_promote_interval_secs'
|
# disappears, we wait 'reconnect_attempts' * 'retry_promote_interval_secs'
|
||||||
# seconds; by default this would be half an hour, as 'retry_promote_interval_secs'
|
# seconds; by default this would be half an hour, as 'retry_promote_interval_secs'
|
||||||
# default value is 300)
|
# default value is 300)
|
||||||
#
|
#
|
||||||
# retry_promote_interval_secs=300
|
# retry_promote_interval_secs=300
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
35
repmgr.h
35
repmgr.h
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* repmgr.h
|
* repmgr.h
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,9 +20,11 @@
|
|||||||
#ifndef _REPMGR_H_
|
#ifndef _REPMGR_H_
|
||||||
#define _REPMGR_H_
|
#define _REPMGR_H_
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
#include "postgres_fe.h"
|
||||||
#include <postgres_fe.h>
|
#include "libpq-fe.h"
|
||||||
#include <getopt_long.h>
|
|
||||||
|
|
||||||
|
#include "getopt_long.h"
|
||||||
|
|
||||||
#include "strutil.h"
|
#include "strutil.h"
|
||||||
#include "dbutils.h"
|
#include "dbutils.h"
|
||||||
@@ -36,6 +38,7 @@
|
|||||||
#define MAXFILENAME 1024
|
#define MAXFILENAME 1024
|
||||||
#define ERRBUFF_SIZE 512
|
#define ERRBUFF_SIZE 512
|
||||||
|
|
||||||
|
#define DEFAULT_CONFIG_FILE "./repmgr.conf"
|
||||||
#define DEFAULT_WAL_KEEP_SEGMENTS "5000"
|
#define DEFAULT_WAL_KEEP_SEGMENTS "5000"
|
||||||
#define DEFAULT_DEST_DIR "."
|
#define DEFAULT_DEST_DIR "."
|
||||||
#define DEFAULT_MASTER_PORT "5432"
|
#define DEFAULT_MASTER_PORT "5432"
|
||||||
@@ -48,7 +51,14 @@
|
|||||||
#define AUTOMATIC_FAILOVER 1
|
#define AUTOMATIC_FAILOVER 1
|
||||||
#define NODE_NOT_FOUND -1
|
#define NODE_NOT_FOUND -1
|
||||||
#define NO_UPSTREAM_NODE -1
|
#define NO_UPSTREAM_NODE -1
|
||||||
#define UNKNOWN_NODE_ID -1
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
MASTER,
|
||||||
|
STANDBY,
|
||||||
|
WITNESS
|
||||||
|
} t_server_type;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +75,6 @@ typedef struct
|
|||||||
char superuser[MAXLEN];
|
char superuser[MAXLEN];
|
||||||
char wal_keep_segments[MAXLEN];
|
char wal_keep_segments[MAXLEN];
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool terse;
|
|
||||||
bool force;
|
bool force;
|
||||||
bool wait_for_master;
|
bool wait_for_master;
|
||||||
bool ignore_rsync_warn;
|
bool ignore_rsync_warn;
|
||||||
@@ -75,7 +84,6 @@ typedef struct
|
|||||||
bool ignore_external_config_files;
|
bool ignore_external_config_files;
|
||||||
char masterport[MAXLEN];
|
char masterport[MAXLEN];
|
||||||
char localport[MAXLEN];
|
char localport[MAXLEN];
|
||||||
char loglevel[MAXLEN];
|
|
||||||
|
|
||||||
/* parameter used by CLUSTER CLEANUP */
|
/* parameter used by CLUSTER CLEANUP */
|
||||||
int keep_history;
|
int keep_history;
|
||||||
@@ -85,9 +93,20 @@ typedef struct
|
|||||||
char recovery_min_apply_delay[MAXLEN];
|
char recovery_min_apply_delay[MAXLEN];
|
||||||
} t_runtime_options;
|
} t_runtime_options;
|
||||||
|
|
||||||
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, "", "", "", 0, "", "" }
|
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, "", "", 0, "", "" }
|
||||||
|
|
||||||
extern char repmgr_schema[MAXLEN];
|
extern char repmgr_schema[MAXLEN];
|
||||||
|
|
||||||
|
typedef struct ErrorListCell
|
||||||
|
{
|
||||||
|
struct ErrorListCell *next;
|
||||||
|
char *error_message;
|
||||||
|
} ErrorListCell;
|
||||||
|
|
||||||
|
typedef struct ErrorList
|
||||||
|
{
|
||||||
|
ErrorListCell *head;
|
||||||
|
ErrorListCell *tail;
|
||||||
|
} ErrorList;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* repmgr.sql
|
* repmgr.sql
|
||||||
*
|
*
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# Makefile
|
# Makefile
|
||||||
#
|
#
|
||||||
# Copyright (c) 2ndQuadrant, 2010-2016
|
# Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
#
|
#
|
||||||
|
|
||||||
MODULE_big = repmgr_funcs
|
MODULE_big = repmgr_funcs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* repmgr_function.sql
|
* repmgr_function.sql
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uninstall_repmgr_funcs.sql
|
* uninstall_repmgr_funcs.sql
|
||||||
* Copyright (c) 2ndQuadrant, 2010-2016
|
* Copyright (c) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* strutil.c
|
* strutil.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* strutil.h
|
* strutil.h
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* uninstall_repmgr.sql
|
* uninstall_repmgr.sql
|
||||||
*
|
*
|
||||||
* Copyright (C) 2ndQuadrant, 2010-2016
|
* Copyright (C) 2ndQuadrant, 2010-2015
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user