mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 15:16:29 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
372f4f7d3d |
@@ -1,29 +0,0 @@
|
||||
License and Contributions
|
||||
=========================
|
||||
|
||||
`repmgr` is licensed under the GPL v3. All of its code and documentation is
|
||||
Copyright 2010-2015, 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.
|
||||
13
FAILOVER.rst
13
FAILOVER.rst
@@ -93,6 +93,7 @@ Create the user and database to manage replication::
|
||||
su - postgres
|
||||
createuser -s repmgr
|
||||
createdb -O repmgr repmgr
|
||||
psql -f /usr/share/postgresql/9.0/contrib/repmgr_funcs.sql repmgr
|
||||
|
||||
Restart the PostgreSQL server::
|
||||
|
||||
@@ -120,7 +121,7 @@ Log in to node2.
|
||||
Clone node1 (the current Master)::
|
||||
|
||||
su - postgres
|
||||
repmgr -d repmgr -U repmgr -h node1 standby clone
|
||||
repmgr -d repmgr -U repmgr -h node1 standby clone
|
||||
|
||||
Start the PostgreSQL server::
|
||||
|
||||
@@ -171,13 +172,11 @@ Register Master and Standby
|
||||
|
||||
Log in to node1.
|
||||
|
||||
Register the node as master::
|
||||
Register the node as Master::
|
||||
|
||||
su - postgres
|
||||
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::
|
||||
|
||||
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
|
||||
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
|
||||
-------------------------
|
||||
|
||||
|
||||
38
FAQ.md
38
FAQ.md
@@ -34,11 +34,6 @@ General
|
||||
replication slots, setting a higher figure will make adding new nodes
|
||||
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`
|
||||
--------
|
||||
@@ -95,32 +90,6 @@ General
|
||||
|
||||
This option is only available when using the `--rsync-only` option.
|
||||
|
||||
- How can I make the witness server use a particular port?
|
||||
|
||||
By default the witness server is configured to use port 5499; this
|
||||
is intended to support running the witness server as a separate
|
||||
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
|
||||
in the `conninfo` string in `repmgr.conf`
|
||||
(repmgr 3.0.1 and earlier: use the `-l/--local-port` option)
|
||||
|
||||
- Do I need to include `shared_preload_libraries = 'repmgr_funcs'`
|
||||
in `postgresql.conf` if I'm not using `repmgrd`?
|
||||
|
||||
No, the `repmgr_funcs` library is only needed when running `repmgrd`.
|
||||
If you later decide to run `repmgrd`, you just need to add
|
||||
`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`
|
||||
---------
|
||||
|
||||
@@ -133,7 +102,7 @@ General
|
||||
|
||||
- How can I prevent a node from ever being promoted to master?
|
||||
|
||||
In `repmgr.conf`, set its priority to a value of 0 or less.
|
||||
In `rempgr.conf`, set its priority to a value of 0 or less.
|
||||
|
||||
- Does `repmgrd` support delayed standbys?
|
||||
|
||||
@@ -148,8 +117,3 @@ General
|
||||
|
||||
Note that after registering a delayed standby, `repmgrd` will only start
|
||||
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
|
||||
|
||||
49
HISTORY
49
HISTORY
@@ -1,46 +1,4 @@
|
||||
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
|
||||
Prevent repmgrd from looping infinitely if node was not registered (Ian)
|
||||
When promoting a standby, have repmgr (not repmgrd) handle metadata updates (Ian)
|
||||
Re-use replication slot if it already exists (Ian)
|
||||
Prevent a test SSH connection being made when not needed (Ian)
|
||||
Correct monitoring table column names (Ian)
|
||||
|
||||
3.0 2015-03-27
|
||||
3.0
|
||||
Require PostgreSQL 9.3 or later (Ian)
|
||||
Use `pg_basebackup` by default (instead of `rsync`) to clone standby servers (Ian)
|
||||
Use `pg_ctl promote` to promote a standby to primary
|
||||
@@ -53,11 +11,6 @@
|
||||
General usability and logging message improvements (Ian)
|
||||
Code consolidation and cleanup (Ian)
|
||||
|
||||
2.0.3 2015-04-16
|
||||
Add -S/--superuser option for witness database creation Ian)
|
||||
Add -c/--fast-checkpoint option for cloning (Christoph)
|
||||
Add option "--initdb-no-pwprompt" (Ian)
|
||||
|
||||
2.0.2 2015-02-17
|
||||
Add "--checksum" in rsync when using "--force" (Jaime)
|
||||
Use createdb/createuser instead of psql (Jaime)
|
||||
|
||||
96
PACKAGES.md
96
PACKAGES.md
@@ -4,10 +4,10 @@ Packaging
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -15,61 +15,57 @@ type of system.
|
||||
|
||||
When building repmgr against a RPM packaged build, you may discover that some
|
||||
development packages are needed as well. The following build errors can
|
||||
occur:
|
||||
occur::
|
||||
|
||||
/usr/bin/ld: cannot find -lxslt
|
||||
/usr/bin/ld: cannot find -lpam
|
||||
/usr/bin/ld: cannot find -lxslt
|
||||
/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 pam-devel
|
||||
yum install libxslt-devel
|
||||
yum install pam-devel
|
||||
|
||||
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
|
||||
path either. The following recipe should work:
|
||||
|
||||
sudo PATH="/usr/pgsql-9.0/bin:$PATH" make USE_PGXS=1 install
|
||||
directories using sudo, the syntax is hard. ``pg_config`` won't be in root's
|
||||
path either. The following recipe should work::
|
||||
|
||||
sudo PATH="/usr/pgsql-9.0/bin:$PATH" make USE_PGXS=1 install
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
to be allowed.
|
||||
|
||||
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
|
||||
..
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
@@ -77,14 +73,14 @@ those, and if the repmgr build finds the wrong postgresql90-devel these
|
||||
"skipping incompatible" messages appear.
|
||||
|
||||
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
|
||||
yum install postgresql90-devel-9.0.2-2PGDG.rhel5.x86_64
|
||||
rpm -e postgresql90-devel --allmatches
|
||||
yum install postgresql90-devel-9.0.2-2PGDG.rhel5.x86_64
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -93,19 +89,19 @@ to get rid of leftover files from the wrong architecture.
|
||||
Notes on Ubuntu, Debian or other Debian-based Builds
|
||||
----------------------------------------------------
|
||||
|
||||
The Debian packages of PostgreSQL put `pg_config` into the development package
|
||||
called `postgresql-server-dev-$version`.
|
||||
The Debian packages of PostgreSQL put ``pg_config`` into the development package
|
||||
called ``postgresql-server-dev-$version``.
|
||||
|
||||
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
|
||||
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
|
||||
USE_PGXS option you also need to install the corresponding development package:
|
||||
If your using Debian packages for PostgreSQL and are building repmgr with the
|
||||
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
|
||||
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.
|
||||
|
||||
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/repmgrd repmgrd /usr/lib/postgresql/9.0/bin/repmgrd 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
|
||||
|
||||
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
|
||||
same directory that you have your repmgr/ directory in.
|
||||
|
||||
@@ -21,8 +21,7 @@ Master setup
|
||||
CREATE DATABASE repmgr_db OWNER repmgr_usr;
|
||||
```
|
||||
|
||||
- configure `postgresql.conf` for replication (see README.md for sample
|
||||
settings)
|
||||
- configure `postgresql.conf` for replication (see above)
|
||||
|
||||
- update `pg_hba.conf`, e.g.:
|
||||
|
||||
@@ -72,10 +71,7 @@ Standby setup
|
||||
[2015-03-03 18:18:23] [NOTICE] HINT: You can now start your postgresql server
|
||||
[2015-03-03 18:18:23] [NOTICE] for example : pg_ctl -D /path/to/standby/data start
|
||||
|
||||
Note that the `repmgr.conf` file is not required when cloning a standby.
|
||||
However we recommend providing a valid `repmgr.conf` if you wish to use
|
||||
replication slots, or want `repmgr` to log the clone event to the
|
||||
`repl_events` table.
|
||||
Note that at this point it does not matter if the `repmgr.conf` file is not found.
|
||||
|
||||
This will clone the PostgreSQL database files from the master, including its
|
||||
`postgresql.conf` and `pg_hba.conf` files, and additionally automatically create
|
||||
@@ -111,8 +107,8 @@ This concludes the basic `repmgr` setup of master and standby. The records
|
||||
created in the `repl_nodes` table should look something like this:
|
||||
|
||||
repmgr_db=# SELECT * from repmgr_test.repl_nodes;
|
||||
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
|
||||
2 | standby | 1 | test | node2 | host=repmgr_node2 user=repmgr_usr dbname=repmgr_db | | 0 | t
|
||||
id | type | upstream_node_id | cluster | name | conninfo | slot_name | priority | active
|
||||
----+---------+------------------+---------+-------+-------------------------------------------------+-----------+----------+--------
|
||||
1 | primary | | test | node1 | host=localhost 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)
|
||||
|
||||
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
|
||||
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
|
||||
replication slots and cascading replication, doesn't require a restart
|
||||
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
|
||||
`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
|
||||
--------
|
||||
|
||||
@@ -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
|
||||
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
|
||||
nodes in order to clone standby servers using `rsync`. `repmgr 3` can
|
||||
@@ -101,8 +98,8 @@ for details.
|
||||
|
||||
### PostgreSQL configuration
|
||||
|
||||
The primary server needs to be configured for replication with settings
|
||||
like the following in `postgresql.conf`:
|
||||
The primary server needs to be configured for replication with the
|
||||
following settings in `postgresql.conf`:
|
||||
|
||||
# Allow read-only queries on standby servers. The number of WAL
|
||||
# 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
|
||||
# disconnected standby to catch up again. The larger this is, the
|
||||
# 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
|
||||
|
||||
@@ -124,18 +121,13 @@ like the following in `postgresql.conf`:
|
||||
archive_mode = on
|
||||
archive_command = 'cd .'
|
||||
|
||||
# If you plan to use repmgrd, ensure that shared_preload_libraries
|
||||
# is configured to load 'repmgr_funcs'
|
||||
|
||||
shared_preload_libraries = 'repmgr_funcs'
|
||||
# You can also set additional replication parameters here, such as
|
||||
# hot_standby_feedback or synchronous_standby_names.
|
||||
|
||||
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
|
||||
"Replication slots" below for more details.
|
||||
|
||||
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.
|
||||
the value of wal_keep_segments need no longer be set. With 9.3, `repmgr`
|
||||
expects it 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
|
||||
store monitoring and replication data are required. Create them by
|
||||
@@ -144,14 +136,10 @@ running the following commands:
|
||||
createuser -s repmgr
|
||||
createdb repmgr -O repmgr
|
||||
|
||||
We recommend using the name `repmgr` for both user and database, but you
|
||||
can use whatever name you like (and you need to set the names you chose
|
||||
in the `conninfo` string in `repmgr.conf`; see below). We also recommend
|
||||
that you set the `repmgr` user's search path to include the `repmgr` schema
|
||||
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.
|
||||
We recommend using the name `repmgr` for both, but you can use whatever
|
||||
name you like (and you need to set the names you chose in the `conninfo`
|
||||
string in `repmgr.conf`; see below). `repmgr` will create the schema and
|
||||
objects it needs when it connects to the server.
|
||||
|
||||
### 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 `--force` option is required to update the existing record:
|
||||
|
||||
repmgr -f /etc/repmgr/repmgr.conf \
|
||||
repmgr -f /etc/repmgr/repmgr.conf
|
||||
--force \
|
||||
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] 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
|
||||
--------------
|
||||
@@ -371,12 +345,10 @@ Following event types currently exist:
|
||||
|
||||
master_register
|
||||
standby_register
|
||||
standby_unregister
|
||||
standby_clone
|
||||
standby_promote
|
||||
witness_create
|
||||
repmgrd_start
|
||||
repmgrd_monitor
|
||||
repmgrd_failover_promote
|
||||
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
|
||||
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:
|
||||
* http://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION-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
|
||||
standby nodes are registered.
|
||||
|
||||
`primary register` can be used as an alias for `master register`.
|
||||
|
||||
* `standby register`
|
||||
|
||||
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.
|
||||
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]`
|
||||
|
||||
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
|
||||
exit:
|
||||
|
||||
* SUCCESS (0) Program ran successfully.
|
||||
* 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_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_QUERY (7) Error while executing a database query
|
||||
* 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_STR_OVERFLOW (10) String overflow error
|
||||
* ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only)
|
||||
* ERR_BAD_SSH (12) Error when connecting to remote host via SSH
|
||||
* ERR_SYS_FAILURE (13) Error when forking (repmgrd only)
|
||||
* ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup
|
||||
* ERR_MONITORING_FAIL (16) Unrecoverable error encountered during monitoring (repmgrd only)
|
||||
* SUCCESS (0) Program ran successfully.
|
||||
* 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_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_QUERY (7) Error while executing a database query
|
||||
* 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_STR_OVERFLOW (10) String overflow error
|
||||
* ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only)
|
||||
* ERR_BAD_SSH (12) Error when connecting to remote host via SSH
|
||||
* ERR_SYS_FAILURE (13) Error when forking (repmgrd only)
|
||||
* ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup
|
||||
|
||||
|
||||
Support and Assistance
|
||||
----------------------
|
||||
|
||||
@@ -1,114 +1,89 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
#
|
||||
# chkconfig: - 75 16
|
||||
# description: Enable repmgrd replication management and monitoring daemon for PostgreSQL
|
||||
# processname: repmgrd
|
||||
# pidfile="/var/run/${NAME}.pid"
|
||||
# repmgrd Start up the repmgrd daemon
|
||||
# repmrgd (replication manager daemon)
|
||||
#
|
||||
# chkconfig: - 75 16
|
||||
# description: repmgrd is the repliation manager daemon \
|
||||
# The repmgrd replication management and monitoring daemon for PostgreSQL.
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: repmgrd
|
||||
# Required-Start: $local_fs $remote_fs $network $syslog postgresql
|
||||
# Required-Stop: $local_fs $remote_fs $network $syslog postgresql
|
||||
# Should-Start: $syslog postgresql-9.3
|
||||
# Should-Stop: $syslog postgresql-9.3
|
||||
# Short-Description: start and stop repmrgd
|
||||
# Description: Enable repmgrd replication management and monitoring daemon for PostgreSQL
|
||||
# this is used to monitor a postgresql cluster.
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
INITD=/etc/rc.d/init.d
|
||||
. $INITD/functions
|
||||
. /etc/init.d/functions
|
||||
|
||||
# Get function listing for cross-distribution logic.
|
||||
TYPESET=`typeset -f|grep "declare"`
|
||||
|
||||
# Get network config.
|
||||
# Source networking configuration.
|
||||
. /etc/sysconfig/network
|
||||
|
||||
DESC="PostgreSQL replication management and monitoring daemon"
|
||||
NAME=repmgrd
|
||||
|
||||
REPMGRD_ENABLED=no
|
||||
prog=repmgrd
|
||||
REPMGRD_ENABLED=yes
|
||||
REPMGRD_OPTS=
|
||||
REPMGRD_USER=postgres
|
||||
REPMGRD_BIN=/usr/pgsql-9.3/bin/repmgrd
|
||||
REPMGRD_PIDFILE=/var/run/repmgrd.pid
|
||||
REPMGRD_LOCK=/var/lock/subsys/${NAME}
|
||||
REPMGRD_LOG=/var/lib/pgsql/9.3/data/pg_log/repmgrd.log
|
||||
DAEMONIZE="-d"
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
|
||||
# pull in sysconfig settings
|
||||
[ -f /etc/sysconfig/repmgrd ] && . /etc/sysconfig/repmgrd
|
||||
|
||||
# For SELinux we need to use 'runuser' not 'su'
|
||||
if [ -x /sbin/runuser ]
|
||||
then
|
||||
SU=runuser
|
||||
else
|
||||
SU=su
|
||||
fi
|
||||
|
||||
test -x $REPMGRD_BIN || exit 0
|
||||
LOCKFILE=/var/lock/subsys/$prog
|
||||
RETVAL=0
|
||||
|
||||
case "$REPMGRD_ENABLED" in
|
||||
[Yy]*)
|
||||
break
|
||||
#nothing to do here
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
if [ -z "${REPMGRD_OPTS}" ]
|
||||
if [ -z "$REPMGRD_OPTS" ]
|
||||
then
|
||||
echo "Not starting ${NAME}, REPMGRD_OPTS not set in /etc/sysconfig/${NAME}"
|
||||
exit 0
|
||||
echo "Not starting $prog, REPMGRD_OPTS not set in /etc/sysconfig/$prog"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
start()
|
||||
{
|
||||
REPMGRD_START=$"Starting ${NAME} service: "
|
||||
start() {
|
||||
[ "$EUID" != "0" ] && exit 4
|
||||
[ "$NETWORKING" = "no" ] && exit 1
|
||||
|
||||
# Make sure startup-time log file is valid
|
||||
if [ ! -e "${REPMGRD_LOG}" -a ! -h "${REPMGRD_LOG}" ]
|
||||
then
|
||||
touch "${REPMGRD_LOG}" || exit 1
|
||||
chown ${REPMGRD_USER}:postgres "${REPMGRD_LOG}"
|
||||
chmod go-rwx "${REPMGRD_LOG}"
|
||||
[ -x /sbin/restorecon ] && /sbin/restorecon "${REPMGRD_LOG}"
|
||||
fi
|
||||
|
||||
echo -n "${REPMGRD_START}"
|
||||
$SU -l $REPMGRD_USER -c "${REPMGRD_BIN} ${REPMGRD_OPTS} -p ${REPMGRD_PIDFILE} &" >> "${REPMGRD_LOG}" 2>&1 < /dev/null
|
||||
sleep 2
|
||||
pid=`head -n 1 "${REPMGRD_PIDFILE}" 2>/dev/null`
|
||||
if [ "x${pid}" != "x" ]
|
||||
then
|
||||
success "${REPMGRD_START}"
|
||||
touch "${REPMGRD_LOCK}"
|
||||
echo $pid > "${REPMGRD_PIDFILE}"
|
||||
# Start daemons.
|
||||
echo -n $"Starting $prog: "
|
||||
daemon --user $REPMGRD_USER $prog $DAEMONIZE $REPMGRD_OPTS
|
||||
RETVAL=$?
|
||||
echo
|
||||
else
|
||||
failure "${REPMGRD_START}"
|
||||
echo
|
||||
script_result=1
|
||||
fi
|
||||
[ $RETVAL -eq 0 ] && touch $LOCKFILE
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
echo -n $"Stopping ${NAME} service: "
|
||||
if [ -e "${REPMGRD_LOCK}" ]
|
||||
then
|
||||
killproc ${NAME}
|
||||
ret=$?
|
||||
if [ $ret -eq 0 ]
|
||||
then
|
||||
echo_success
|
||||
rm -f "${REPMGRD_PIDFILE}"
|
||||
rm -f "${REPMGRD_LOCK}"
|
||||
stop() {
|
||||
[ "$EUID" != "0" ] && exit 4
|
||||
echo -n $"Shutting down $prog: "
|
||||
killproc $prog
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && rm -f $LOCKFILE
|
||||
return $RETVAL
|
||||
}
|
||||
status() {
|
||||
if [ -f "$LOCKFILE" ]; then
|
||||
echo "$prog is running"
|
||||
else
|
||||
echo_failure
|
||||
script_result=1
|
||||
RETVAL=3
|
||||
echo "$prog is stopped"
|
||||
fi
|
||||
else
|
||||
# not running; per LSB standards this is "ok"
|
||||
echo_success
|
||||
fi
|
||||
echo
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start)
|
||||
@@ -118,16 +93,22 @@ case "$1" in
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status -p $REPMGRD_PIDFILE $NAME
|
||||
script_result=$?
|
||||
status $prog
|
||||
;;
|
||||
restart)
|
||||
restart|force-reload)
|
||||
stop
|
||||
start
|
||||
start
|
||||
;;
|
||||
try-restart|condrestart)
|
||||
if status $prog > /dev/null; then
|
||||
stop
|
||||
start
|
||||
fi
|
||||
;;
|
||||
reload)
|
||||
exit 3
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|status|restart}"
|
||||
echo $"Usage: $0 {start|stop|status|restart|try-restart|force-reload}"
|
||||
exit 2
|
||||
esac
|
||||
|
||||
exit $script_result
|
||||
|
||||
@@ -1,21 +1,4 @@
|
||||
# default settings for repmgrd. This file is source by /bin/sh from
|
||||
# /etc/init.d/repmgrd
|
||||
#default sysconfig file for repmrgd
|
||||
#custom overrides can be placed here
|
||||
|
||||
# disable repmgrd by default so it won't get started upon installation
|
||||
# valid values: yes/no
|
||||
REPMGRD_ENABLED=no
|
||||
|
||||
# Options for repmgrd (required)
|
||||
#REPMGRD_OPTS="--verbose -d -f /var/lib/pgsql/repmgr/repmgr.conf"
|
||||
|
||||
# User to run repmgrd as
|
||||
#REPMGRD_USER=postgres
|
||||
|
||||
# repmgrd binary
|
||||
#REPMGRD_BIN=/usr/bin/repmgrd
|
||||
|
||||
# pid file
|
||||
#REPMGRD_PIDFILE=/var/lib/pgsql/repmgr/repmgrd.pid
|
||||
|
||||
# log file
|
||||
#REPMGRD_LOG=/var/lib/pgsql/repmgr/repmgrd.log
|
||||
REPMGRD_OPTS="-f /etc/repmgr/repmgr.conf"
|
||||
|
||||
49
SSH-RSYNC.md
49
SSH-RSYNC.md
@@ -1,36 +1,35 @@
|
||||
Set up trusted copy between postgres accounts
|
||||
---------------------------------------------
|
||||
|
||||
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
|
||||
If you need to use rsync to clone standby servers, the postgres account
|
||||
on your master and standby servers must be each able to access the other
|
||||
using SSH without a password.
|
||||
|
||||
First generate an ssh key, using an empty passphrase, and copy the resulting
|
||||
keys and a matching authorization file to a privileged user account on the other
|
||||
system:
|
||||
First generate a ssh key, using an empty passphrase, and copy the resulting
|
||||
keys and a maching authorization file to a privledged user on the other system::
|
||||
|
||||
[postgres@node1]$ ssh-keygen -t rsa
|
||||
Generating public/private rsa key pair.
|
||||
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_rsa):
|
||||
Enter passphrase (empty for no passphrase):
|
||||
Enter same passphrase again:
|
||||
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.
|
||||
The key fingerprint is:
|
||||
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]$ chmod go-rwx ~/.ssh/*
|
||||
[postgres@node1]$ cd ~/.ssh
|
||||
[postgres@node1]$ scp id_rsa.pub id_rsa authorized_keys user@node2:
|
||||
[postgres@node1]$ ssh-keygen -t rsa
|
||||
Generating public/private rsa key pair.
|
||||
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_rsa):
|
||||
Enter passphrase (empty for no passphrase):
|
||||
Enter same passphrase again:
|
||||
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.
|
||||
The key fingerprint is:
|
||||
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]$ chmod go-rwx ~/.ssh/*
|
||||
[postgres@node1]$ cd ~/.ssh
|
||||
[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`
|
||||
user's account:
|
||||
Login as a user on the other system, and install the files into the postgres
|
||||
user's account::
|
||||
|
||||
[user@node2 ~]$ sudo chown postgres.postgres authorized_keys id_rsa.pub id_rsa
|
||||
[user@node2 ~]$ sudo mkdir -p ~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 chmod -R go-rwx ~postgres/.ssh
|
||||
[user@node2 ~]$ sudo chown postgres.postgres authorized_keys id_rsa.pub id_rsa
|
||||
[user@node2 ~]$ sudo mkdir -p ~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 chmod -R go-rwx ~postgres/.ssh
|
||||
|
||||
Now test that ssh in both directions works. You may have to accept some new
|
||||
known hosts in the process.
|
||||
|
||||
60
TODO
60
TODO
@@ -5,16 +5,9 @@ Known issues in repmgr
|
||||
the database server using the ``pg_ctl`` command may accidentally
|
||||
terminate after their associated ssh session ends.
|
||||
|
||||
* PGPASSFILE may not be passed to pg_basebackup
|
||||
|
||||
|
||||
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
|
||||
|
||||
* Make the fact that a standby may be delayed a factor in the voting
|
||||
@@ -25,53 +18,8 @@ Planned feature improvements
|
||||
* Create the repmgr user/database on "master register".
|
||||
|
||||
* 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
|
||||
(currently: current directory and `pg_config --sysconfdir`); if
|
||||
possible this should include the location provided by the package,
|
||||
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).
|
||||
* Use pg_basebackup -X s
|
||||
NOTE: this can be used by including `-X s` in the configuration parameter
|
||||
`pg_basebackup_options`
|
||||
61
check_dir.c
61
check_dir.c
@@ -23,19 +23,14 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ftw.h>
|
||||
|
||||
/* 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 "strutil.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
|
||||
* we use this function to check the new data directory and
|
||||
@@ -248,19 +243,6 @@ is_pg_dir(char *dir)
|
||||
|
||||
bool
|
||||
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;
|
||||
|
||||
@@ -297,33 +279,21 @@ _create_pg_dir(char *dir, bool force, bool for_witness)
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
break;
|
||||
}
|
||||
else if (pg_dir && !force)
|
||||
{
|
||||
log_hint(_("This looks like a PostgreSQL directory.\n"
|
||||
"If you are sure you want to clone here, "
|
||||
"please check there is no PostgreSQL server "
|
||||
"running and use the -F/--force option\n"));
|
||||
log_warning(_("\nThis looks like a PostgreSQL directory.\n"
|
||||
"If you are sure you want to clone here, "
|
||||
"please check there is no PostgreSQL server "
|
||||
"running and use the --force option\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -336,14 +306,3 @@ _create_pg_dir(char *dir, bool force, bool for_witness)
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,5 @@ bool create_dir(char *dir);
|
||||
bool set_dir_permissions(char *dir);
|
||||
bool is_pg_dir(char *dir);
|
||||
bool create_pg_dir(char *dir, bool force);
|
||||
bool create_witness_pg_dir(char *dir, bool force);
|
||||
|
||||
#endif
|
||||
|
||||
544
config.c
544
config.c
@@ -26,28 +26,10 @@
|
||||
|
||||
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 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
|
||||
* file.
|
||||
@@ -56,161 +38,83 @@ progname(void)
|
||||
*
|
||||
* Any configuration options changed in this function must also be changed in
|
||||
* 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
|
||||
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
|
||||
* emit an error and terminate. We assume that if a user explicitly
|
||||
* provides a configuration file, they'll want to make sure it's
|
||||
* used and not fall back to any of the defaults.
|
||||
* emit an error
|
||||
*/
|
||||
if (config_file[0])
|
||||
{
|
||||
strncpy(config_file_path, config_file, MAXPGPATH);
|
||||
canonicalize_path(config_file_path);
|
||||
struct stat config;
|
||||
|
||||
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,
|
||||
strerror(errno)
|
||||
);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (verbose == true)
|
||||
{
|
||||
log_notice(_("using configuration file \"%s\"\n"), config_file);
|
||||
}
|
||||
|
||||
config_file_provided = true;
|
||||
config_file_found = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no configuration file was provided, attempt to find a default file
|
||||
* in this order:
|
||||
* - 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 no configuration file was provided, set to a default file
|
||||
* which `parse_config()` will attempt to read if it exists
|
||||
*/
|
||||
if (config_file_provided == false)
|
||||
else
|
||||
{
|
||||
char my_exec_path[MAXPGPATH];
|
||||
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"));
|
||||
}
|
||||
}
|
||||
strncpy(config_file_buf, DEFAULT_CONFIG_FILE, MAXLEN);
|
||||
}
|
||||
|
||||
return parse_config(options);
|
||||
}
|
||||
|
||||
fp = fopen(config_file_buf, "r");
|
||||
|
||||
/*
|
||||
* Parse configuration file; if any errors are encountered,
|
||||
* list them and exit.
|
||||
*
|
||||
* Ensure any default values set here are synced with repmgr.conf.sample
|
||||
* and any other documentation.
|
||||
*/
|
||||
bool
|
||||
parse_config(t_configuration_options *options)
|
||||
{
|
||||
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
|
||||
/*
|
||||
* Since some commands don't require a config file at all, not having one
|
||||
* isn't necessarily a problem.
|
||||
*
|
||||
* If the user explictly provided a configuration file and we can't
|
||||
* read it we'll raise an error.
|
||||
*
|
||||
* If no configuration file was provided, we'll try and read the default\
|
||||
* file if it exists and is readable, but won't worry if it's not.
|
||||
*/
|
||||
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));
|
||||
options->node = -1;
|
||||
options->upstream_node = NO_UPSTREAM_NODE;
|
||||
options->use_replication_slots = 0;
|
||||
memset(options->conninfo, 0, sizeof(options->conninfo));
|
||||
options->failover = MANUAL_FAILOVER;
|
||||
options->priority = DEFAULT_PRIORITY;
|
||||
@@ -228,7 +132,7 @@ parse_config(t_configuration_options *options)
|
||||
|
||||
/* default to 6 reconnection attempts at intervals of 10 seconds */
|
||||
options->reconnect_attempts = 6;
|
||||
options->reconnect_interval = 10;
|
||||
options->reconnect_intvl = 10;
|
||||
|
||||
options->monitor_interval_secs = 2;
|
||||
options->retry_promote_interval_secs = 300;
|
||||
@@ -238,61 +142,27 @@ parse_config(t_configuration_options *options)
|
||||
options->tablespace_mapping.head = 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");
|
||||
|
||||
/*
|
||||
* A configuration file has been found, either provided by the user
|
||||
* 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)
|
||||
/* Read next line */
|
||||
while ((s = fgets(buff, sizeof buff, fp)) != NULL)
|
||||
{
|
||||
bool known_parameter = true;
|
||||
|
||||
/* Skip blank lines and comments */
|
||||
if (buff[0] == '\n' || buff[0] == '#')
|
||||
continue;
|
||||
|
||||
/* Parse name/value pair from line */
|
||||
parse_line(buf, name, value);
|
||||
|
||||
/* Skip blank lines */
|
||||
if (!strlen(name))
|
||||
continue;
|
||||
|
||||
/* Skip comments */
|
||||
if (name[0] == '#')
|
||||
continue;
|
||||
parse_line(buff, name, value);
|
||||
|
||||
/* Copy into correct entry in parameters struct */
|
||||
if (strcmp(name, "cluster") == 0)
|
||||
strncpy(options->cluster_name, value, MAXLEN);
|
||||
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)
|
||||
options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors);
|
||||
options->upstream_node = atoi(value);
|
||||
else if (strcmp(name, "conninfo") == 0)
|
||||
strncpy(options->conninfo, value, MAXLEN);
|
||||
else if (strcmp(name, "rsync_options") == 0)
|
||||
@@ -319,11 +189,12 @@ parse_config(t_configuration_options *options)
|
||||
}
|
||||
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)
|
||||
options->priority = repmgr_atoi(value, "priority", &config_errors);
|
||||
options->priority = atoi(value);
|
||||
else if (strcmp(name, "node_name") == 0)
|
||||
strncpy(options->node_name, value, MAXLEN);
|
||||
else if (strcmp(name, "promote_command") == 0)
|
||||
@@ -331,16 +202,11 @@ parse_config(t_configuration_options *options)
|
||||
else if (strcmp(name, "follow_command") == 0)
|
||||
strncpy(options->follow_command, value, MAXLEN);
|
||||
else if (strcmp(name, "master_response_timeout") == 0)
|
||||
options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors);
|
||||
/* '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);
|
||||
options->master_response_timeout = atoi(value);
|
||||
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)
|
||||
options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", &config_errors);
|
||||
options->reconnect_intvl = atoi(value);
|
||||
else if (strcmp(name, "pg_bindir") == 0)
|
||||
strncpy(options->pg_bindir, value, MAXLEN);
|
||||
else if (strcmp(name, "pg_ctl_options") == 0)
|
||||
@@ -350,12 +216,11 @@ parse_config(t_configuration_options *options)
|
||||
else if (strcmp(name, "logfile") == 0)
|
||||
strncpy(options->logfile, value, MAXLEN);
|
||||
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)
|
||||
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)
|
||||
/* XXX we should have a dedicated boolean argument format */
|
||||
options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", &config_errors);
|
||||
options->use_replication_slots = atoi(value);
|
||||
else if (strcmp(name, "event_notification_command") == 0)
|
||||
strncpy(options->event_notification_command, value, MAXLEN);
|
||||
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,
|
||||
* as currently e.g. an empty `node` value will be converted to '0'.
|
||||
*/
|
||||
if (known_parameter == true && !strlen(value)) {
|
||||
char error_message_buf[MAXLEN] = "";
|
||||
snprintf(error_message_buf,
|
||||
MAXLEN,
|
||||
_("no value provided for parameter \"%s\""),
|
||||
name);
|
||||
|
||||
error_list_append(&config_errors, error_message_buf);
|
||||
if(known_parameter == true && !strlen(value)) {
|
||||
log_err(_("no value provided for parameter '%s'\n"), name);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,49 +252,45 @@ parse_config(t_configuration_options *options)
|
||||
/* The following checks are for the presence of the parameter */
|
||||
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)
|
||||
{
|
||||
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')
|
||||
{
|
||||
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')
|
||||
{
|
||||
error_list_append(&config_errors, _("\"conninfo\": parameter was not found\n"));
|
||||
}
|
||||
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);
|
||||
log_err(_("required parameter 'conninfo' was not found\n"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -449,7 +305,7 @@ trim(char *s)
|
||||
*s2 = &s[strlen(s) - 1];
|
||||
|
||||
/* If string is empty, no action needed */
|
||||
if (s2 < s1)
|
||||
if(s2 < s1)
|
||||
return s;
|
||||
|
||||
/* Trim and delimit right side */
|
||||
@@ -469,58 +325,32 @@ trim(char *s)
|
||||
}
|
||||
|
||||
void
|
||||
parse_line(char *buf, char *name, char *value)
|
||||
parse_line(char *buff, char *name, char *value)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
/*
|
||||
* Extract parameter name, if present
|
||||
* first we find the name of the parameter
|
||||
*/
|
||||
for (; i < MAXLEN; ++i)
|
||||
{
|
||||
|
||||
if (buf[i] == '=')
|
||||
if (buff[i] != '=')
|
||||
name[j++] = buff[i];
|
||||
else
|
||||
break;
|
||||
|
||||
switch(buf[i])
|
||||
{
|
||||
/* Ignore whitespace */
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
continue;
|
||||
default:
|
||||
name[j++] = buf[i];
|
||||
}
|
||||
}
|
||||
name[j] = '\0';
|
||||
|
||||
/*
|
||||
* Ignore any whitespace following the '=' sign
|
||||
*/
|
||||
for (; i < MAXLEN; ++i)
|
||||
{
|
||||
if (buf[i+1] == ' ')
|
||||
continue;
|
||||
if (buf[i+1] == '\t')
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract parameter value
|
||||
* Now the value
|
||||
*/
|
||||
j = 0;
|
||||
for (++i; i < MAXLEN; ++i)
|
||||
if (buf[i] == '\'')
|
||||
if (buff[i] == '\'')
|
||||
continue;
|
||||
else if (buf[i] == '#')
|
||||
break;
|
||||
else if (buf[i] != '\n')
|
||||
value[j++] = buf[i];
|
||||
else if (buff[i] != '\n')
|
||||
value[j++] = buff[i];
|
||||
else
|
||||
break;
|
||||
value[j] = '\0';
|
||||
@@ -528,7 +358,7 @@ parse_line(char *buf, char *name, char *value)
|
||||
}
|
||||
|
||||
bool
|
||||
reload_config(t_configuration_options *orig_options)
|
||||
reload_config(char *config_file, t_configuration_options * orig_options)
|
||||
{
|
||||
PGconn *conn;
|
||||
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"));
|
||||
|
||||
parse_config(&new_options);
|
||||
parse_config(config_file, &new_options);
|
||||
if (new_options.node == -1)
|
||||
{
|
||||
log_warning(_("unable to parse new configuration, retaining current configuration\n"));
|
||||
@@ -582,13 +412,13 @@ reload_config(t_configuration_options *orig_options)
|
||||
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"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(orig_options->conninfo, new_options.conninfo) != 0)
|
||||
if(strcmp(orig_options->conninfo, new_options.conninfo) != 0)
|
||||
{
|
||||
/* Test conninfo string */
|
||||
conn = establish_db_connection(new_options.conninfo, false);
|
||||
@@ -608,56 +438,56 @@ reload_config(t_configuration_options *orig_options)
|
||||
*/
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* node */
|
||||
if (orig_options->node != new_options.node)
|
||||
if(orig_options->node != new_options.node)
|
||||
{
|
||||
orig_options->node = new_options.node;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* failover */
|
||||
if (orig_options->failover != new_options.failover)
|
||||
if(orig_options->failover != new_options.failover)
|
||||
{
|
||||
orig_options->failover = new_options.failover;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* priority */
|
||||
if (orig_options->priority != new_options.priority)
|
||||
if(orig_options->priority != new_options.priority)
|
||||
{
|
||||
orig_options->priority = new_options.priority;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
@@ -674,76 +504,76 @@ reload_config(t_configuration_options *orig_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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* reconnect_interval */
|
||||
if (orig_options->reconnect_interval != new_options.reconnect_interval)
|
||||
/* reconnect_intvl */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
if (config_changed == true)
|
||||
if(config_changed == true)
|
||||
{
|
||||
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
|
||||
@@ -862,7 +602,7 @@ tablespace_list_append(t_configuration_options *options, const char *arg)
|
||||
const char *arg_ptr;
|
||||
|
||||
cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
|
||||
if (cell == NULL)
|
||||
if(cell == NULL)
|
||||
{
|
||||
log_err(_("unable to allocate memory; terminating\n"));
|
||||
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++)
|
||||
{
|
||||
/* ignore whitespace */
|
||||
if (*arg_ptr == ' ' || *arg_ptr == '\t')
|
||||
if(*arg_ptr == ' ' || *arg_ptr == '\t')
|
||||
{
|
||||
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 -
|
||||
* 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;
|
||||
|
||||
cell = (EventNotificationListCell *) pg_malloc0(sizeof(EventNotificationListCell));
|
||||
|
||||
if (cell == NULL)
|
||||
if(cell == NULL)
|
||||
{
|
||||
log_err(_("unable to allocate memory; terminating\n"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
@@ -968,7 +708,7 @@ parse_event_notifications_list(t_configuration_options *options, const char *arg
|
||||
dst_ptr = event_type_buf;
|
||||
}
|
||||
/* ignore duplicated commas */
|
||||
else if (*arg_ptr == ',')
|
||||
else if(*arg_ptr == ',')
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
26
config.h
26
config.h
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "strutil.h"
|
||||
|
||||
#define CONFIG_FILE_NAME "repmgr.conf"
|
||||
|
||||
typedef struct EventNotificationListCell
|
||||
{
|
||||
@@ -68,7 +67,7 @@ typedef struct
|
||||
char ssh_options[QUERY_STR_LEN];
|
||||
int master_response_timeout;
|
||||
int reconnect_attempts;
|
||||
int reconnect_interval;
|
||||
int reconnect_intvl;
|
||||
char pg_bindir[MAXLEN];
|
||||
char pg_ctl_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} }
|
||||
|
||||
typedef struct ErrorListCell
|
||||
{
|
||||
struct ErrorListCell *next;
|
||||
char *error_message;
|
||||
} ErrorListCell;
|
||||
|
||||
typedef struct ErrorList
|
||||
{
|
||||
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);
|
||||
bool parse_config(const char *config_file, t_configuration_options *options);
|
||||
void parse_line(char *buff, char *name, char *value);
|
||||
char *trim(char *s);
|
||||
void error_list_append(ErrorList *error_list, char *error_message);
|
||||
int repmgr_atoi(const char *s,
|
||||
const char *config_item,
|
||||
ErrorList *error_list);
|
||||
bool reload_config(char *config_file, t_configuration_options *orig_options);
|
||||
|
||||
#endif
|
||||
|
||||
513
dbutils.c
513
dbutils.c
@@ -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
|
||||
check_cluster_schema(PGconn *conn)
|
||||
{
|
||||
@@ -164,8 +92,7 @@ check_cluster_schema(PGconn *conn)
|
||||
"SELECT 1 FROM pg_namespace WHERE nspname = '%s'",
|
||||
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);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
@@ -195,22 +122,17 @@ is_standby(PGconn *conn)
|
||||
{
|
||||
PGresult *res;
|
||||
int result = 0;
|
||||
char *sqlquery = "SELECT pg_catalog.pg_is_in_recovery()";
|
||||
|
||||
log_verbose(LOG_DEBUG, "is_standby(): %s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
res = PQexec(conn, "SELECT pg_is_in_recovery()");
|
||||
|
||||
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));
|
||||
result = -1;
|
||||
}
|
||||
else if (PQntuples(res) == 1 && strcmp(PQgetvalue(res, 0, 0), "t") == 0)
|
||||
{
|
||||
result = 1;
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
return result;
|
||||
@@ -275,7 +197,7 @@ is_pgup(PGconn *conn, int timeout)
|
||||
|
||||
|
||||
/*
|
||||
* Return the id of the active master node, or NODE_NOT_FOUND if no
|
||||
* Return the id of the active master node, or -1 if no
|
||||
* record available.
|
||||
*
|
||||
* This reports the value stored in the database only and
|
||||
@@ -297,19 +219,17 @@ get_master_node_id(PGconn *conn, char *cluster)
|
||||
get_repmgr_schema_quoted(conn),
|
||||
cluster);
|
||||
|
||||
log_verbose(LOG_DEBUG, "get_master_node_id():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_err(_("get_master_node_id(): query failed\n%s\n"),
|
||||
PQerrorMessage(conn));
|
||||
retval = NODE_NOT_FOUND;
|
||||
retval = -1;
|
||||
}
|
||||
else if (PQntuples(res) == 0)
|
||||
{
|
||||
log_warning(_("get_master_node_id(): no active primary found\n"));
|
||||
retval = NODE_NOT_FOUND;
|
||||
retval = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -340,7 +260,7 @@ get_server_version(PGconn *conn, char *server_version)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (server_version != NULL)
|
||||
if(server_version != NULL)
|
||||
strcpy(server_version, 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];
|
||||
int retval = 1;
|
||||
|
||||
sqlquery_snprintf(sqlquery,
|
||||
"SELECT true FROM pg_settings "
|
||||
sqlquery_snprintf(sqlquery, "SELECT true FROM pg_settings "
|
||||
" WHERE name = '%s' AND setting %s '%s'",
|
||||
parameter, op, value);
|
||||
|
||||
log_verbose(LOG_DEBUG, "guc_set():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
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));
|
||||
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",
|
||||
parameter, datatype, op, value, datatype);
|
||||
|
||||
log_verbose(LOG_DEBUG, "guc_set_typed():n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
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));
|
||||
retval = -1;
|
||||
}
|
||||
@@ -422,16 +337,15 @@ get_cluster_size(PGconn *conn, char *size)
|
||||
PGresult *res;
|
||||
char sqlquery[QUERY_STR_LEN];
|
||||
|
||||
sqlquery_snprintf(sqlquery,
|
||||
"SELECT pg_catalog.pg_size_pretty(SUM(pg_catalog.pg_database_size(oid))::bigint) "
|
||||
sqlquery_snprintf(
|
||||
sqlquery,
|
||||
"SELECT pg_size_pretty(SUM(pg_database_size(oid))::bigint) "
|
||||
" FROM pg_database ");
|
||||
|
||||
log_verbose(LOG_DEBUG, "get_cluster_size():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
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));
|
||||
|
||||
PQclear(res);
|
||||
@@ -459,7 +373,7 @@ get_pg_setting(PGconn *conn, const char *setting, char *output)
|
||||
" FROM pg_settings WHERE name = '%s'",
|
||||
setting);
|
||||
|
||||
log_verbose(LOG_DEBUG, "get_pg_setting(): %s\n", sqlquery);
|
||||
log_debug(_("get_pg_setting(): %s\n"), sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
|
||||
@@ -481,14 +395,13 @@ get_pg_setting(PGconn *conn, const char *setting, char *output)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* XXX highly unlikely this would ever happen */
|
||||
log_err(_("get_pg_setting(): unknown parameter \"%s\""), PQgetvalue(res, i, 0));
|
||||
log_err(_("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);
|
||||
@@ -533,19 +446,19 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
|
||||
cluster,
|
||||
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);
|
||||
|
||||
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));
|
||||
PQclear(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!PQntuples(res))
|
||||
if(!PQntuples(res))
|
||||
{
|
||||
log_notice(_("no record found for upstream server"));
|
||||
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);
|
||||
|
||||
if (upstream_node_id_ptr != NULL)
|
||||
if(upstream_node_id_ptr != NULL)
|
||||
*upstream_node_id_ptr = atoi(PQgetvalue(res, 0, 1));
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
* in turn to definitely establish if it's the cluster primary.
|
||||
* get a connection to master by reading repl_nodes, creating a connection
|
||||
* 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
|
||||
* current primary will be returned first, reducing the number of speculative
|
||||
* connections which need to be made to other nodes.
|
||||
*
|
||||
* If master_conninfo_out points to allocated memory of MAXCONNINFO in length,
|
||||
* the primary server's conninfo string will be copied there.
|
||||
* NB: If master_conninfo_out may be NULL. If it is non-null, it is assumed to
|
||||
* point to allocated memory of MAXCONNINFO in length, and the master server
|
||||
* connection string is placed there.
|
||||
*/
|
||||
|
||||
PGconn *
|
||||
get_master_connection(PGconn *standby_conn, char *cluster,
|
||||
int *master_id, char *master_conninfo_out)
|
||||
{
|
||||
PGconn *remote_conn = NULL;
|
||||
PGresult *res;
|
||||
PGconn *master_conn = NULL;
|
||||
PGresult *res1;
|
||||
PGresult *res2;
|
||||
char sqlquery[QUERY_STR_LEN];
|
||||
char remote_conninfo_stack[MAXCONNINFO];
|
||||
char *remote_conninfo = &*remote_conninfo_stack;
|
||||
char master_conninfo_stack[MAXCONNINFO];
|
||||
char *master_conninfo = &*master_conninfo_stack;
|
||||
|
||||
int i,
|
||||
node_id;
|
||||
|
||||
if (master_id != NULL)
|
||||
if(master_id != NULL)
|
||||
{
|
||||
*master_id = NODE_NOT_FOUND;
|
||||
*master_id = -1;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
sqlquery_snprintf(sqlquery,
|
||||
" SELECT id, conninfo, "
|
||||
" CASE WHEN type = 'master' THEN 1 ELSE 2 END AS type_priority"
|
||||
" FROM %s.repl_nodes "
|
||||
" WHERE cluster = '%s' "
|
||||
" AND type != 'witness' "
|
||||
"ORDER BY active DESC, type_priority, priority, id",
|
||||
"SELECT id, conninfo "
|
||||
" FROM %s.repl_nodes "
|
||||
" WHERE cluster = '%s' "
|
||||
" AND type != 'witness' ",
|
||||
get_repmgr_schema_quoted(standby_conn),
|
||||
cluster);
|
||||
|
||||
log_verbose(LOG_DEBUG, "get_master_connection():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(standby_conn, sqlquery);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
res1 = PQexec(standby_conn, sqlquery);
|
||||
if (PQresultStatus(res1) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_err(_("unable to retrieve node records: %s\n"),
|
||||
PQerrorMessage(standby_conn));
|
||||
PQclear(res);
|
||||
PQclear(res1);
|
||||
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 */
|
||||
node_id = atoi(PQgetvalue(res, i, 0));
|
||||
strncpy(remote_conninfo, PQgetvalue(res, i, 1), MAXCONNINFO);
|
||||
log_verbose(LOG_INFO,
|
||||
_("checking role of cluster node '%i'\n"),
|
||||
node_id);
|
||||
remote_conn = establish_db_connection(remote_conninfo, false);
|
||||
node_id = atoi(PQgetvalue(res1, i, 0));
|
||||
strncpy(master_conninfo, PQgetvalue(res1, i, 1), MAXCONNINFO);
|
||||
log_info(_("checking role of cluster node '%i'\n"),
|
||||
node_id);
|
||||
master_conn = establish_db_connection(master_conninfo, false);
|
||||
|
||||
if (PQstatus(remote_conn) != CONNECTION_OK)
|
||||
if (PQstatus(master_conn) != CONNECTION_OK)
|
||||
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"),
|
||||
node_id,
|
||||
PQerrorMessage(remote_conn));
|
||||
PQfinish(remote_conn);
|
||||
log_err(_("unable to retrieve recovery state from this node: %s\n"),
|
||||
PQerrorMessage(master_conn));
|
||||
PQclear(res2);
|
||||
PQfinish(master_conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if is_standby() returns 0, queried node is the master */
|
||||
if (is_node_standby == 0)
|
||||
/* if false, this is the master */
|
||||
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);
|
||||
|
||||
if (master_id != NULL)
|
||||
if(master_id != NULL)
|
||||
{
|
||||
*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
|
||||
* failover procedure or just fix some situation on the standby.
|
||||
*/
|
||||
PQclear(res);
|
||||
PQclear(res1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -709,7 +621,7 @@ wait_connection_availability(PGconn *conn, long long timeout)
|
||||
{
|
||||
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));
|
||||
return 0;
|
||||
}
|
||||
@@ -736,7 +648,7 @@ wait_connection_availability(PGconn *conn, long long timeout)
|
||||
if (select(sock, &read_set, NULL, NULL, &tmout) == -1)
|
||||
{
|
||||
log_warning(
|
||||
_("wait_connection_availability(): select() returned with error\n%s\n"),
|
||||
_("wait_connection_availability: select() returned with error: %s"),
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
@@ -752,7 +664,7 @@ wait_connection_availability(PGconn *conn, long long timeout)
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_warning(_("wait_connection_availability(): timeout reached"));
|
||||
log_warning(_("wait_connection_availability: timeout reached"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -787,12 +699,6 @@ cancel_query(PGconn *conn, int timeout)
|
||||
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 *
|
||||
get_repmgr_schema(void)
|
||||
{
|
||||
@@ -803,7 +709,7 @@ get_repmgr_schema(void)
|
||||
char *
|
||||
get_repmgr_schema_quoted(PGconn *conn)
|
||||
{
|
||||
if (strcmp(repmgr_schema_quoted, "") == 0)
|
||||
if(strcmp(repmgr_schema_quoted, "") == 0)
|
||||
{
|
||||
char *identifier = PQescapeIdentifier(conn, repmgr_schema,
|
||||
strlen(repmgr_schema));
|
||||
@@ -822,57 +728,11 @@ create_replication_slot(PGconn *conn, char *slot_name)
|
||||
char sqlquery[QUERY_STR_LEN];
|
||||
PGresult *res;
|
||||
|
||||
/*
|
||||
* Check whether slot exists already; if it exists and is active, that
|
||||
* means another active standby is using it, which creates an error situation;
|
||||
* if not we can reuse it as-is
|
||||
*/
|
||||
|
||||
sqlquery_snprintf(sqlquery,
|
||||
"SELECT active, slot_type "
|
||||
" FROM pg_replication_slots "
|
||||
" WHERE slot_name = '%s' ",
|
||||
slot_name);
|
||||
|
||||
log_verbose(LOG_DEBUG, "create_replication_slot():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_err(_("unable to query pg_replication_slots: %s\n"),
|
||||
PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PQntuples(res))
|
||||
{
|
||||
if (strcmp(PQgetvalue(res, 0, 1), "physical") != 0)
|
||||
{
|
||||
log_err(_("Slot '%s' exists and is not a physical slot\n"),
|
||||
slot_name);
|
||||
PQclear(res);
|
||||
}
|
||||
if (strcmp(PQgetvalue(res, 0, 0), "f") == 0)
|
||||
{
|
||||
PQclear(res);
|
||||
log_debug("Replication slot '%s' exists but is inactive; reusing\n",
|
||||
slot_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
PQclear(res);
|
||||
log_err(_("Slot '%s' already exists as an active slot\n"),
|
||||
slot_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlquery_snprintf(sqlquery,
|
||||
"SELECT * FROM pg_create_physical_replication_slot('%s')",
|
||||
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);
|
||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
@@ -888,33 +748,6 @@ create_replication_slot(PGconn *conn, char *slot_name)
|
||||
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
|
||||
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
||||
@@ -923,11 +756,11 @@ start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
||||
PGresult *res;
|
||||
|
||||
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),
|
||||
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);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
@@ -942,7 +775,7 @@ start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
|
||||
char *first_wal_seg_pq = PQgetvalue(res, 0, 0);
|
||||
size_t buf_sz = strlen(first_wal_seg_pq);
|
||||
|
||||
first_wal_segment = pg_malloc0(buf_sz + 1);
|
||||
first_wal_segment = malloc(buf_sz + 1);
|
||||
xsnprintf(first_wal_segment, buf_sz + 1, "%s", first_wal_seg_pq);
|
||||
}
|
||||
|
||||
@@ -958,7 +791,7 @@ stop_backup(PGconn *conn, char *last_wal_segment)
|
||||
char sqlquery[QUERY_STR_LEN];
|
||||
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);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
@@ -973,7 +806,7 @@ stop_backup(PGconn *conn, char *last_wal_segment)
|
||||
char *last_wal_seg_pq = PQgetvalue(res, 0, 0);
|
||||
size_t buf_sz = strlen(last_wal_seg_pq);
|
||||
|
||||
last_wal_segment = pg_malloc0(buf_sz + 1);
|
||||
last_wal_segment = malloc(buf_sz + 1);
|
||||
xsnprintf(last_wal_segment, buf_sz + 1, "%s", last_wal_seg_pq);
|
||||
}
|
||||
|
||||
@@ -994,8 +827,6 @@ set_config_bool(PGconn *conn, const char *config_param, bool state)
|
||||
config_param,
|
||||
state ? "TRUE" : "FALSE");
|
||||
|
||||
log_verbose(LOG_DEBUG, "set_config_bool():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(conn, sqlquery);
|
||||
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
@@ -1027,13 +858,11 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
||||
int i;
|
||||
|
||||
sqlquery_snprintf(sqlquery, "TRUNCATE TABLE %s.repl_nodes", get_repmgr_schema_quoted(witnessconn));
|
||||
|
||||
log_verbose(LOG_DEBUG, "copy_configuration():\n%s\n", sqlquery);
|
||||
|
||||
log_debug("copy_configuration: %s\n", sqlquery);
|
||||
res = PQexec(witnessconn, sqlquery);
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
@@ -1041,13 +870,10 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
||||
sqlquery_snprintf(sqlquery,
|
||||
"SELECT id, type, upstream_node_id, name, conninfo, priority, slot_name FROM %s.repl_nodes",
|
||||
get_repmgr_schema_quoted(masterconn));
|
||||
|
||||
log_verbose(LOG_DEBUG, "copy_configuration():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexec(masterconn, sqlquery);
|
||||
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));
|
||||
PQclear(res);
|
||||
return false;
|
||||
@@ -1056,11 +882,9 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
||||
for (i = 0; i < PQntuples(res); i++)
|
||||
{
|
||||
bool node_record_created;
|
||||
char *witness = PQgetvalue(res, i, 4);
|
||||
|
||||
log_verbose(LOG_DEBUG,
|
||||
"copy_configuration(): writing node record for node %s (id: %s)\n",
|
||||
PQgetvalue(res, i, 4),
|
||||
PQgetvalue(res, i, 0));
|
||||
log_debug(_("copy_configuration(): %s\n"), witness);
|
||||
|
||||
node_record_created = create_node_record(witnessconn,
|
||||
"copy_configuration",
|
||||
@@ -1080,9 +904,7 @@ copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
|
||||
|
||||
if (node_record_created == false)
|
||||
{
|
||||
PQclear(res);
|
||||
|
||||
log_err("Unable to copy node record to witness database\n%s\n",
|
||||
fprintf(stderr, "Unable to copy node record to witness database: %s\n",
|
||||
PQerrorMessage(witnessconn));
|
||||
return false;
|
||||
}
|
||||
@@ -1108,13 +930,13 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
||||
char slot_name_buf[MAXLEN];
|
||||
PGresult *res;
|
||||
|
||||
if (upstream_node == NO_UPSTREAM_NODE)
|
||||
if(upstream_node == NO_UPSTREAM_NODE)
|
||||
{
|
||||
/*
|
||||
* No explicit upstream node id provided for standby - attempt to
|
||||
* 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);
|
||||
maxlen_snprintf(upstream_node_id, "%i", primary_node_id);
|
||||
@@ -1129,7 +951,7 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
||||
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);
|
||||
}
|
||||
@@ -1138,7 +960,6 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
||||
maxlen_snprintf(slot_name_buf, "%s", "NULL");
|
||||
}
|
||||
|
||||
/* XXX convert to placeholder query */
|
||||
sqlquery_snprintf(sqlquery,
|
||||
"INSERT INTO %s.repl_nodes "
|
||||
" (id, type, upstream_node_id, cluster, "
|
||||
@@ -1154,18 +975,16 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
|
||||
slot_name_buf,
|
||||
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);
|
||||
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
log_err(_("Unable to create node record\n%s\n"),
|
||||
PQerrorMessage(conn));
|
||||
log_warning(_("Unable to create node record: %s\n"),
|
||||
PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
return false;
|
||||
}
|
||||
@@ -1187,18 +1006,15 @@ delete_node_record(PGconn *conn, int node, char *action)
|
||||
" WHERE id = %d",
|
||||
get_repmgr_schema_quoted(conn),
|
||||
node);
|
||||
|
||||
log_verbose(LOG_DEBUG, "delete_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);
|
||||
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));
|
||||
PQclear(res);
|
||||
return false;
|
||||
@@ -1221,8 +1037,8 @@ delete_node_record(PGconn *conn, int node, char *action)
|
||||
*
|
||||
* 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
|
||||
* an event record. In this case, if `event_notification_command` is set, a
|
||||
* user-defined notification to be generated; if not, this function will have
|
||||
* an event record. In this case, if `event_notification_command` is set a user-
|
||||
* defined notification to be generated; if not, this function will have
|
||||
* no effect.
|
||||
*/
|
||||
|
||||
@@ -1235,12 +1051,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
bool success = true;
|
||||
struct tm ts;
|
||||
|
||||
/* Only attempt to write a record if a connection handle was provided.
|
||||
Also check that the repmgr schema has been properly intialised - if
|
||||
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.
|
||||
*/
|
||||
if (conn != NULL && strcmp(repmgr_schema, DEFAULT_REPMGR_SCHEMA_PREFIX) != 0)
|
||||
if(conn != NULL)
|
||||
{
|
||||
int n_node_id = htonl(node_id);
|
||||
char *t_successful = successful ? "TRUE" : "FALSE";
|
||||
@@ -1270,8 +1081,6 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
" RETURNING event_timestamp ",
|
||||
get_repmgr_schema_quoted(conn));
|
||||
|
||||
log_verbose(LOG_DEBUG, "create_event_record():\n%s\n", sqlquery);
|
||||
|
||||
res = PQexecParams(conn,
|
||||
sqlquery,
|
||||
4,
|
||||
@@ -1283,6 +1092,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
|
||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
|
||||
log_warning(_("Unable to create event record: %s\n"),
|
||||
PQerrorMessage(conn));
|
||||
|
||||
@@ -1293,7 +1103,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
{
|
||||
/* Store timestamp to send to the notification command */
|
||||
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);
|
||||
@@ -1304,7 +1114,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
* current timestamp ourselves. This isn't quite the same
|
||||
* format as PostgreSQL, but is close enough for diagnostic use.
|
||||
*/
|
||||
if (!strlen(event_timestamp))
|
||||
if(!strlen(event_timestamp))
|
||||
{
|
||||
time_t now;
|
||||
|
||||
@@ -1314,7 +1124,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
}
|
||||
|
||||
/* 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];
|
||||
const char *src_ptr;
|
||||
@@ -1330,14 +1140,14 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
* (If 'event_notifications' was not provided, we assume the script
|
||||
* should be executed for all events).
|
||||
*/
|
||||
if (options->event_notifications.head != NULL)
|
||||
if(options->event_notifications.head != NULL)
|
||||
{
|
||||
EventNotificationListCell *cell;
|
||||
bool notify_ok = false;
|
||||
|
||||
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;
|
||||
break;
|
||||
@@ -1347,7 +1157,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
/*
|
||||
* 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);
|
||||
return success;
|
||||
@@ -1379,7 +1189,7 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
case 'd':
|
||||
/* %d: details */
|
||||
src_ptr++;
|
||||
if (details != NULL)
|
||||
if(details != NULL)
|
||||
{
|
||||
strlcpy(dst_ptr, details, end_ptr - dst_ptr);
|
||||
dst_ptr += strlen(dst_ptr);
|
||||
@@ -1413,116 +1223,15 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
|
||||
|
||||
*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);
|
||||
if (r != 0)
|
||||
{
|
||||
log_warning(_("Unable to execute event notification command\n"));
|
||||
log_info(_("Parsed event notification command was:\n%s\n"), parsed_command);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
49
dbutils.h
49
dbutils.h
@@ -20,60 +20,16 @@
|
||||
#ifndef _REPMGR_DBUTILS_H_
|
||||
#define _REPMGR_DBUTILS_H_
|
||||
|
||||
#include "access/xlogdefs.h"
|
||||
|
||||
#include "config.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,
|
||||
const bool exit_on_error);
|
||||
PGconn *establish_db_connection_by_params(const char *keywords[],
|
||||
const char *values[],
|
||||
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);
|
||||
int is_standby(PGconn *conn);
|
||||
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_quoted(PGconn *conn);
|
||||
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 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 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 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
|
||||
|
||||
4
debian/repmgr.repmgrd.default
vendored
4
debian/repmgr.repmgrd.default
vendored
@@ -12,7 +12,7 @@ REPMGRD_ENABLED=no
|
||||
#REPMGRD_USER=postgres
|
||||
|
||||
# repmgrd binary
|
||||
#REPMGRD_BIN=/usr/bin/repmgrd
|
||||
#REPMGR_BIN=/usr/bin/repmgr
|
||||
|
||||
# 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
|
||||
# 1 if daemon was already stopped
|
||||
# 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
|
||||
|
||||
@@ -35,7 +35,5 @@
|
||||
#define ERR_BAD_SSH 12
|
||||
#define ERR_SYS_FAILURE 13
|
||||
#define ERR_BAD_BASEBACKUP 14
|
||||
#define ERR_INTERNAL 15
|
||||
#define ERR_MONITORING_FAIL 16
|
||||
|
||||
#endif /* _ERRCODE_H_ */
|
||||
|
||||
152
log.c
152
log.c
@@ -39,37 +39,13 @@
|
||||
|
||||
/* #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
|
||||
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;
|
||||
struct tm *tm;
|
||||
char buff[100];
|
||||
|
||||
/*
|
||||
* Store the requested level so that if there's a subsequent
|
||||
* log_hint(), we can suppress that if appropriate.
|
||||
*/
|
||||
last_log_level = level;
|
||||
va_list ap;
|
||||
|
||||
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);
|
||||
fprintf(stderr, "%s [%s] ", buff, level_name);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_hint(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (terse_logging == false)
|
||||
{
|
||||
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);
|
||||
}
|
||||
static int detect_log_level(const char *level);
|
||||
static int detect_log_facility(const char *facility);
|
||||
|
||||
int log_type = REPMGR_STDERR;
|
||||
int log_level = LOG_NOTICE;
|
||||
|
||||
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 f;
|
||||
|
||||
@@ -169,10 +95,10 @@ logger_init(t_configuration_options * opts, const char *ident)
|
||||
printf("Assigned level for logger: %d\n", l);
|
||||
#endif
|
||||
|
||||
if (l >= 0)
|
||||
if (l > 0)
|
||||
log_level = l;
|
||||
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)
|
||||
@@ -218,38 +144,18 @@ logger_init(t_configuration_options * opts, const char *ident)
|
||||
{
|
||||
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);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
printf(_("Unable to open specified logfile %s for writing: %s\n"), opts->logfile, strerror(errno));
|
||||
printf(_("Terminating\n"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
fprintf(stderr, "error reopening stderr to '%s': %s",
|
||||
opts->logfile, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
logger_shutdown(void)
|
||||
@@ -263,32 +169,17 @@ logger_shutdown(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate whether extra-verbose logging is required. This will
|
||||
* generate a lot of output, particularly debug logging, and should
|
||||
* not be permanently enabled in production.
|
||||
*
|
||||
* NOTE: in previous repmgr versions, this option forced the log
|
||||
* level to INFO.
|
||||
* Set a minimum logging level. Intended for command line verbosity
|
||||
* options, which might increase requested logging over what's specified
|
||||
* in the regular configuration file.
|
||||
*/
|
||||
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
|
||||
detect_log_level(const char *level)
|
||||
{
|
||||
@@ -309,16 +200,17 @@ detect_log_level(const char *level)
|
||||
if (!strcmp(level, "EMERG"))
|
||||
return LOG_EMERG;
|
||||
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
detect_log_facility(const char *facility)
|
||||
{
|
||||
int local = 0;
|
||||
|
||||
if (!strncmp(facility, "LOCAL", 5) && strlen(facility) == 6)
|
||||
{
|
||||
|
||||
local = atoi(&facility[5]);
|
||||
|
||||
switch (local)
|
||||
|
||||
12
log.h
12
log.h
@@ -112,19 +112,13 @@ __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
|
||||
#endif
|
||||
|
||||
|
||||
int detect_log_level(const char *level);
|
||||
|
||||
/* Logger initialisation and shutdown */
|
||||
|
||||
bool logger_init(t_configuration_options * opts, const char *ident);
|
||||
|
||||
bool logger_shutdown(void);
|
||||
|
||||
void logger_set_verbose(void);
|
||||
void logger_set_terse(void);
|
||||
bool logger_init(t_configuration_options * opts, const char *ident,
|
||||
const char *level, const char *facility);
|
||||
|
||||
void log_hint(const char *fmt, ...);
|
||||
void log_verbose(int level, const char *fmt, ...);
|
||||
void logger_min_verbose(int minimum);
|
||||
|
||||
extern int log_type;
|
||||
extern int log_level;
|
||||
|
||||
@@ -7,24 +7,17 @@
|
||||
#
|
||||
# repmgr and repmgrd require these items to be configured:
|
||||
|
||||
# Cluster name - this will be used by repmgr to generate its internal
|
||||
# schema (pattern: "repmgr_{cluster}"); while this name will be quoted
|
||||
# to preserve case, we recommend using lower case and avoiding whitespace
|
||||
# to facilitate easier querying of the repmgr views and tables.
|
||||
cluster=example_cluster
|
||||
# Cluster name
|
||||
cluster=test
|
||||
|
||||
# Node ID and name
|
||||
# (Note: we recommend to avoid naming nodes after their initial
|
||||
# replication funcion, as this will cause confusion when e.g.
|
||||
# "standby2" is promoted to primary)
|
||||
node=2 # a unique integer
|
||||
node_name=node2 # an arbitrary (but unique) string; we recommend using
|
||||
# the server's hostname or another identifier unambiguously
|
||||
# associated with the server to avoid confusion
|
||||
# "standby2" is promoted to master)
|
||||
node=2
|
||||
node_name=node2
|
||||
|
||||
# Database connection information as a conninfo string
|
||||
# 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
|
||||
# Database connection information
|
||||
conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
|
||||
|
||||
# Optional configuration items
|
||||
@@ -36,7 +29,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
|
||||
# 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
|
||||
# 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
|
||||
|
||||
@@ -44,9 +37,7 @@ conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
|
||||
# (default: 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
|
||||
# -------------------------------
|
||||
@@ -116,29 +107,28 @@ logfacility=STDERR
|
||||
#
|
||||
# These settings are only applied when repmgrd is running.
|
||||
|
||||
# Number of seconds to wait for a response from the primary server before
|
||||
# deciding it has failed
|
||||
|
||||
# How many seconds we wait for master response before declaring master failure
|
||||
master_response_timeout=60
|
||||
|
||||
# Number of times to try and reconnect to the primary before starting
|
||||
# the failover procedure
|
||||
# How many time we try to reconnect to master before starting failover procedure
|
||||
reconnect_attempts=6
|
||||
reconnect_interval=10
|
||||
|
||||
# Autofailover options
|
||||
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'
|
||||
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
|
||||
|
||||
# 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'
|
||||
# seconds; by default this would be half an hour, as 'retry_promote_interval_secs'
|
||||
# default value is 300)
|
||||
#
|
||||
# retry_promote_interval_secs=300
|
||||
|
||||
|
||||
|
||||
34
repmgr.h
34
repmgr.h
@@ -20,9 +20,11 @@
|
||||
#ifndef _REPMGR_H_
|
||||
#define _REPMGR_H_
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <postgres_fe.h>
|
||||
#include <getopt_long.h>
|
||||
#include "postgres_fe.h"
|
||||
#include "libpq-fe.h"
|
||||
|
||||
|
||||
#include "getopt_long.h"
|
||||
|
||||
#include "strutil.h"
|
||||
#include "dbutils.h"
|
||||
@@ -36,6 +38,7 @@
|
||||
#define MAXFILENAME 1024
|
||||
#define ERRBUFF_SIZE 512
|
||||
|
||||
#define DEFAULT_CONFIG_FILE "./repmgr.conf"
|
||||
#define DEFAULT_WAL_KEEP_SEGMENTS "5000"
|
||||
#define DEFAULT_DEST_DIR "."
|
||||
#define DEFAULT_MASTER_PORT "5432"
|
||||
@@ -46,9 +49,15 @@
|
||||
|
||||
#define MANUAL_FAILOVER 0
|
||||
#define AUTOMATIC_FAILOVER 1
|
||||
#define NODE_NOT_FOUND -1
|
||||
#define NO_UPSTREAM_NODE -1
|
||||
#define UNKNOWN_NODE_ID -1
|
||||
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN = 0,
|
||||
MASTER,
|
||||
STANDBY,
|
||||
WITNESS
|
||||
} t_server_type;
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +74,6 @@ typedef struct
|
||||
char superuser[MAXLEN];
|
||||
char wal_keep_segments[MAXLEN];
|
||||
bool verbose;
|
||||
bool terse;
|
||||
bool force;
|
||||
bool wait_for_master;
|
||||
bool ignore_rsync_warn;
|
||||
@@ -75,7 +83,6 @@ typedef struct
|
||||
bool ignore_external_config_files;
|
||||
char masterport[MAXLEN];
|
||||
char localport[MAXLEN];
|
||||
char loglevel[MAXLEN];
|
||||
|
||||
/* parameter used by CLUSTER CLEANUP */
|
||||
int keep_history;
|
||||
@@ -85,9 +92,20 @@ typedef struct
|
||||
char recovery_min_apply_delay[MAXLEN];
|
||||
} 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];
|
||||
|
||||
typedef struct ErrorListCell
|
||||
{
|
||||
struct ErrorListCell *next;
|
||||
char *error_message;
|
||||
} ErrorListCell;
|
||||
|
||||
typedef struct ErrorList
|
||||
{
|
||||
ErrorListCell *head;
|
||||
ErrorListCell *tail;
|
||||
} ErrorList;
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user