Compare commits

..

3 Commits

Author SHA1 Message Date
Christian Kruse
0a71123920 Merge branch 'master' into REL2_0_STABLE 2014-03-03 09:25:08 +01:00
Christian Kruse
a72c2296e9 Merge branch 'master' into REL2_0_STABLE 2014-02-11 09:28:40 +01:00
Christian Kruse
9c3d79147b now version.h contains the right version 2014-02-07 21:47:39 +01:00
40 changed files with 3818 additions and 7454 deletions

View File

@@ -1,4 +1,4 @@
Copyright (c) 2010-2015, 2ndQuadrant Limited Copyright (c) 2010-2014, 2ndQuadrant Limited
All rights reserved. All rights reserved.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify

136
FAQ.md
View File

@@ -1,136 +0,0 @@
FAQ - Frequently Asked Questions about repmgr
=============================================
This FAQ applies to `repmgr` 3.0 and later.
General
-------
- What's the difference between the repmgr versions?
repmgr 3.x builds on the improved replication facilities added
in PostgreSQL 9.3, as well as improved automated failover support
via `repmgrd`, and is not compatible with PostgreSQL 9.2 and earlier.
repmgr 2.x supports PostgreSQL 9.0 onwards. While it is compatible
with PostgreSQL 9.3 and later, we recommend repmgr v3.
- What's the advantage of using replication slots?
Replication slots, introduced in PostgreSQL 9.4, ensure that the
master server will retain WAL files until they have been consumed
by all standby servers. This makes WAL file management much easier,
and if used `repmgr` will no longer insist on a fixed number (default: 5000)
of WAL files being preserved.
(However this does mean that if a standby is no longer connected to the
master, the master will retain WAL files indefinitely).
- How many replication slots should I define in `max_replication_slots`?
Normally at least same number as the number of standbys which will connect
to the node. Note that changes to `max_replication_slots` require a server
restart to take effect, and as there is no particular penalty for unused
replication slots, setting a higher figure will make adding new nodes
easier.
`repmgr`
--------
- When should I use the --rsync-only option?
By default, `repmgr` uses `pg_basebackup` to clone a standby from
a master. However, `pg_basebackup` copies the entire data directory, which
can take some time depending on installation size. If you have an
existing but "stale" standby, `repmgr` can use `rsync` instead,
which means only changed or added files need to be copied.
- Can I register an existing master/standby?
Yes, this is no problem.
- How can a failed master be re-added as a standby?
This is a two-stage process. First, the failed master's data directory
must be re-synced with the current master; secondly the failed master
needs to be re-registered as a standby. The section "Converting a failed
master to a standby" in the `README.md` file contains more detailed
information on this process.
- Is there an easy way to check my master server is correctly configured
for use with `repmgr`?
Yes - execute `repmgr` with the `--check-upstream-config` option, and it
will let you know which items in `postgresql.conf` need to be modified.
- Even though I specified custom `rsync` options, `repmgr` appends
the `--checksum` - why?
When syncing a stale data directory from an active server, it's
essential that `rsync` compares the content of files rather than
just timestamp and size, to ensure that all changed files are
copied and prevent corruption.
- When cloning a standby, how can I prevent `repmgr` from copying
`postgresql.conf` and `pg_hba.conf` from the PostgreSQL configuration
directory in `/etc`?
Use the command line option `--ignore-external-config-files`
- How can I prevent `repmgr` from copying local configuration files
in the data directory?
If you're updating an existing but stale data directory which
contains e.g. configuration files you don't want to be overwritten
with the same file from the master, specify the files in the
`rsync_options` configuration option, e.g.
rsync_options=--exclude=postgresql.local.conf
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 a port for the witness server, supply the port number to
repmgr with the `-l/--local-port` command line 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.
`repmgrd`
---------
- Do I need a witness server?
Not necessarily. However if you have an uneven number of nodes spread
over more than one network segment, a witness server will enable
better handling of a 'split brain' situation by providing a "casting
vote" on the preferred network segment.
- 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.
- Does `repmgrd` support delayed standbys?
`repmgrd` can monitor delayed standbys - those set up with
`recovery_min_apply_delay` set to a non-zero value in `recovery.conf` -
but as it's not currently possible to directly examine the value
applied to the standby, `repmgrd` may not be able to properly evaluate
the node as a promotion candidate.
We recommend that delayed standbys are explicitly excluded from promotion
by setting `priority` to 0 in `repmgr.conf`.
Note that after registering a delayed standby, `repmgrd` will only start
once the metadata added in the master node has been replicated.

50
HISTORY
View File

@@ -1,43 +1,3 @@
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
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
Enable tablespace remapping using `pg_basebackup` (in PostgreSQL 9.3 with `rsync`) (Ian)
Support cascaded standbys (Ian)
"pg_bindir" no longer required as a configuration parameter (Ian)
Enable replication slots to be used (PostgreSQL 9.4 and later (Ian)
Command line option "--check-upstream-config" (Ian)
Add event logging table and option to execute an external program when an event occurs (Ian)
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)
Fixes to witness creation and monitoring (wamonite)
Use default master port if none supplied (Martín)
Documentation fixes and improvements (Ian)
2.0.1 2014-07-16
Documentation fixes and new QUICKSTART file (Ian)
Explicitly specify directories to ignore when cloning (Ian)
Fix log level for some log messages (Ian)
RHEL/CentOS specfile, init script and Makefile fixes (Nathan Van Overloop)
Debian init script and config file documentation fixes (József Kószó)
Typo fixes (Riegie Godwin Jeyaranchen, PriceChild)
2.0stable 2014-01-30 2.0stable 2014-01-30
Documentation fixes (Christian) Documentation fixes (Christian)
General refactoring, code quality improvements and stabilization work (Christian) General refactoring, code quality improvements and stabilization work (Christian)
@@ -65,15 +25,15 @@
Make the monitoring optional and turned off by default, it can be turned on with --monitoring-history switch (Jaime) Make the monitoring optional and turned off by default, it can be turned on with --monitoring-history switch (Jaime)
Add tunables to specify number of retries to reconnect to master and the time between them (Jaime) Add tunables to specify number of retries to reconnect to master and the time between them (Jaime)
1.2.0 2012-07-27 1.2.0 2012-07-27
Test ssh connection before trying to rsync (Cédric) Test ssh connection before trying to rsync (Cédric)
Add CLUSTER SHOW command (Carlo) Add CLUSTER SHOW command (Carlo)
Add CLUSTER CLEANUP command (Jaime) Add CLUSTER CLEANUP command (Jaime)
Add function write_primary_conninfo (Marco) Add function write_primary_conninfo (Marco)
Teach repmgr how to get tablespace's location in different pg version (Jaime) Teach repmgr how to get tablespace's location in different pg version (Jaime)
Improve version message (Carlo) Improve version message (Carlo)
1.1.1 2012-04-18 1.1.1 2012-04-18
Add --ignore-rsync-warning (Cédric) Add --ignore-rsync-warning (Cédric)
Add strnlen for compatibility with OS X (Greg) Add strnlen for compatibility with OS X (Greg)
Improve performance of the repl_status view (Jaime) Improve performance of the repl_status view (Jaime)
@@ -84,7 +44,7 @@
1.1.0 2011-03-09 1.1.0 2011-03-09
Make options -U, -R and -p not mandatory (Jaime) Make options -U, -R and -p not mandatory (Jaime)
1.1.0b1 2011-02-24 1.1.0b1 2011-02-24
Fix missing "--force" option in help (Greg Smith) Fix missing "--force" option in help (Greg Smith)
Correct warning message for wal_keep_segments (Bas van Oostveen) Correct warning message for wal_keep_segments (Bas van Oostveen)
Add Debian build/usage docs (Bas, Hannu Krosing, Cedric Villemain) Add Debian build/usage docs (Bas, Hannu Krosing, Cedric Villemain)

View File

@@ -1,6 +1,6 @@
# #
# Makefile # Makefile
# Copyright (c) 2ndQuadrant, 2010-2015 # Copyright (c) 2ndQuadrant, 2010-2014
repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o
@@ -36,24 +36,12 @@ endif
install: install_prog install_ext install: install_prog install_ext
install_prog: install_prog:
mkdir -p '$(DESTDIR)$(bindir)' $(INSTALL_PROGRAM) repmgrd$(X) '$(DESTDIR)$(bindir)'
$(INSTALL_PROGRAM) repmgrd$(X) '$(DESTDIR)$(bindir)/' $(INSTALL_PROGRAM) repmgr$(X) '$(DESTDIR)$(bindir)'
$(INSTALL_PROGRAM) repmgr$(X) '$(DESTDIR)$(bindir)/'
install_ext: install_ext:
$(MAKE) -C sql install $(MAKE) -C sql install
install_rhel:
mkdir -p '$(DESTDIR)/etc/init.d/'
$(INSTALL_PROGRAM) RHEL/repmgrd.init '$(DESTDIR)/etc/init.d/repmgrd'
mkdir -p '$(DESTDIR)/etc/sysconfig/'
$(INSTALL_PROGRAM) RHEL/repmgrd.sysconfig '$(DESTDIR)/etc/sysconfig/repmgrd'
mkdir -p '$(DESTDIR)/etc/repmgr/'
$(INSTALL_PROGRAM) repmgr.conf.sample '$(DESTDIR)/etc/repmgr/'
mkdir -p '$(DESTDIR)/usr/bin/'
$(INSTALL_PROGRAM) repmgrd$(X) '$(DESTDIR)/usr/bin/'
$(INSTALL_PROGRAM) repmgr$(X) '$(DESTDIR)/usr/bin/'
ifneq (,$(DATA)$(DATA_built)) ifneq (,$(DATA)$(DATA_built))
@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \ @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \ echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
@@ -79,4 +67,3 @@ deb: repmgrd repmgr
mv debian.deb ../postgresql-repmgr-9.0_1.0.0.deb mv debian.deb ../postgresql-repmgr-9.0_1.0.0.deb
rm -rf ./debian/usr rm -rf ./debian/usr

View File

@@ -1,123 +0,0 @@
Packaging
=========
Notes on RedHat Linux, Fedora, and CentOS Builds
------------------------------------------------
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``
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
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::
/usr/bin/ld: cannot find -lxslt
/usr/bin/ld: cannot find -lpam
Install the following packages to correct those::
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
Issues with 32 and 64 bit RPMs
------------------------------
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::
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::
# 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
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
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::
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::
make USE_PGXS=1 clean
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``.
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::
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::
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
the default path. The reason behind this is that Ubuntu/Debian systems manage
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::
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::
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.

View File

@@ -1,117 +0,0 @@
repmgr quickstart guide
=======================
This quickstart guide provides some annotated examples on basic
`repmgr` setup. It assumes you are familiar with PostgreSQL replication
concepts setup and Linux/UNIX system administration.
For the purposes of this guide, we'll assume the database user will be
`repmgr_usr` and the database will be `repmgr_db`.
Master setup
------------
1. Configure PostgreSQL
- create user and database:
```
CREATE ROLE repmgr_usr LOGIN SUPERUSER;
CREATE DATABASE repmgr_db OWNER repmgr_usr;
```
- configure `postgresql.conf` for replication (see above)
- update `pg_hba.conf`, e.g.:
```
host repmgr_db repmgr_usr 192.168.1.0/24 trust
host replication repmgr_usr 192.168.1.0/24 trust
```
Restart the PostgreSQL server after making these changes.
2. Create the `repmgr` configuration file:
$ cat /path/to/repmgr/node1/repmgr.conf
cluster=test
node=1
node_name=node1
conninfo='host=repmgr_node1 user=repmgr_usr dbname=repmgr_db'
pg_bindir=/path/to/postgres/bin
(For an annotated `repmgr.conf` file, see `repmgr.conf.sample` in the
repository's root directory).
3. Register the master node with `repmgr`:
$ repmgr -f /path/to/repmgr/node1/repmgr.conf --verbose master register
[2015-03-03 17:45:53] [INFO] repmgr connecting to master database
[2015-03-03 17:45:53] [INFO] repmgr connected to master, checking its state
[2015-03-03 17:45:53] [INFO] master register: creating database objects inside the repmgr_test schema
[2015-03-03 17:45:53] [NOTICE] Master node correctly registered for cluster test with id 1 (conninfo: host=localhost user=repmgr_usr dbname=repmgr_db)
Standby setup
-------------
1. Use `repmgr standby clone` to clone a standby from the master:
repmgr -D /path/to/standby/data -d repmgr_db -U repmgr_usr --verbose standby clone 192.168.1.2
[2015-03-03 18:18:21] [NOTICE] No configuration file provided and default file './repmgr.conf' not found - continuing with default values
[2015-03-03 18:18:21] [NOTICE] repmgr Destination directory ' /path/to/standby/data' provided
[2015-03-03 18:18:21] [INFO] repmgr connecting to upstream node
[2015-03-03 18:18:21] [INFO] repmgr connected to upstream node, checking its state
[2015-03-03 18:18:21] [INFO] Successfully connected to upstream node. Current installation size is 27 MB
[2015-03-03 18:18:21] [NOTICE] Starting backup...
[2015-03-03 18:18:21] [INFO] creating directory " /path/to/standby/data"...
[2015-03-03 18:18:21] [INFO] Executing: 'pg_basebackup -l "repmgr base backup" -h localhost -p 9595 -U repmgr_usr -D /path/to/standby/data '
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
[2015-03-03 18:18:23] [NOTICE] repmgr standby clone (using pg_basebackup) complete
[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.
This will clone the PostgreSQL database files from the master, including its
`postgresql.conf` and `pg_hba.conf` files, and additionally automatically create
the `recovery.conf` file containing the correct parameters to start streaming
from the primary node.
2. Start the PostgreSQL server
3. Create the `repmgr` configuration file:
$ cat /path/node2/repmgr/repmgr.conf
cluster=test
node=2
node_name=node2
conninfo='host=repmgr_node2 user=repmgr_usr dbname=repmgr_db'
pg_bindir=/path/to/postgres/bin
4. Register the standby node with `repmgr`:
$ repmgr -f /path/to/repmgr/node2/repmgr.conf --verbose standby register
[2015-03-03 18:24:34] [NOTICE] Opening configuration file: /path/to/repmgr/node2/repmgr.conf
[2015-03-03 18:24:34] [INFO] repmgr connecting to standby database
[2015-03-03 18:24:34] [INFO] repmgr connecting to master database
[2015-03-03 18:24:34] [INFO] finding node list for cluster 'test'
[2015-03-03 18:24:34] [INFO] checking role of cluster node '1'
[2015-03-03 18:24:34] [INFO] repmgr connected to master, checking its state
[2015-03-03 18:24:34] [INFO] repmgr registering the standby
[2015-03-03 18:24:34] [INFO] repmgr registering the standby complete
[2015-03-03 18:24:34] [NOTICE] Standby node correctly registered for cluster test with id 2 (conninfo: host=localhost user=repmgr_usr dbname=repmgr_db)
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=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)

619
README.md
View File

@@ -1,619 +0,0 @@
repmgr: Replication Manager for PostgreSQL
==========================================
`repmgr` is an open-source tool to manage replication and failover
between multiple PostgreSQL servers. It enhances PostgreSQL's built-in
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.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 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.
Overview
--------
The `repmgr` command-line tool is used to perform administrative tasks,
and the `repmgrd` daemon is used to optionally monitor replication and
manage automatic failover.
To get started, each PostgreSQL node in your cluster must have a
`repmgr.conf` file. The current master node must be registered using
`repmgr master register`. Existing standby servers can be registered
using `repmgr standby register`. A new standby server can be created
using `repmgr standby clone` followed by `repmgr standby register`.
See the `QUICKSTART.md` file for examples of how to use these commands.
Once the cluster is in operation, run `repmgr cluster show` to see the
status of the registered primary and standby nodes. Any standby can be
manually promoted using `repmgr standby promote`. Other standby nodes
can be told to follow the new master using `repmgr standby follow`. We
show examples of these commands below.
Next, for detailed monitoring, you must run `repmgrd` (with the same
configuration file) on all your nodes. Replication status information is
stored in a custom schema along with information about registered nodes.
You also need `repmgrd` to configure automatic failover in your cluster.
See the `FAILOVER.rst` file for an explanation of how to set up
automatic failover.
Requirements
------------
`repmgr` is developed and tested on Linux and OS X, but it should work
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 9.4.
Earlier versions of `repmgr` needed password-less SSH access between
nodes in order to clone standby servers using `rsync`. `repmgr 3` can
use `pg_basebackup` instead in most circumstances; ssh is not required.
You will need to use rsync only if your PostgreSQL configuration files
are outside your data directory (as on Debian) and you wish these to
be copied by `repmgr`. See the `SSH-RSYNC.md` file for details on
configuring password-less SSH between your nodes.
Installation
------------
`repmgr` must be installed on each PostgreSQL server node.
* Packages
- PGDG publishes RPM packages for RedHat-based distributions
- Debian/Ubuntu provide .deb packages.
- See `PACKAGES.md` for details on building .deb and .rpm packages
from the `repmgr` source code.
* Source installation
- `git clone https://github.com/2ndQuadrant/repmgr`
- Or download tar.gz files from
https://github.com/2ndQuadrant/repmgr/releases
- To install from source, run `sudo make USE_PGXS=1 install`
After installation, you should be able to run `repmgr --version` and
`repmgrd --version`. These binaries should be installed in the same
directory as other PostgreSQL binaries, such as `psql`.
Configuration
-------------
### Server configuration
By default, `repmgr` uses PostgreSQL's built-in replication protocol to
clone a primary and create a standby server. If your configuration files
live outside your data directory, however, you will still need to set up
password-less SSH so that rsync can be used. See the `SSH-RSYNC.md` file
for details.
### PostgreSQL configuration
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.
hot_standby = on
wal_level = 'hot_standby'
max_wal_senders = 10
# 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; in 9.4, replication slots can be used instead (see below).
wal_keep_segments = 5000
# Enable archiving, but leave it unconfigured (so that it can be
# configured without a restart later). Recommended, not required.
archive_mode = on
archive_command = 'cd .'
# 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. 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
running the following commands:
createuser -s repmgr
createdb repmgr -O repmgr
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
Create a `repmgr.conf` file on each server. Here's a minimal sample:
cluster=test
node=1
node_name=node1
conninfo='host=repmgr_node1 user=repmgr dbname=repmgr'
The `cluster` name must be the same on all nodes. The `node` (an
integer) and `node_name` must be unique to each node.
The `conninfo` string must point to repmgr's database *on this node*.
The host must be an IP or a name that all the nodes in the cluster can
resolve (not `localhost`!). All nodes must use the same username and
database name, but other parameters, such as the port, can vary between
nodes.
Your `repmgr.conf` should not be stored inside the PostgreSQL data
directory. We recommend `/etc/repmgr/repmgr.conf`, but you can place it
anywhere and use the `-f /path/to/repmgr.conf` option to tell `repmgr`
where it is. If not specified, `repmgr` will search for `repmgr.conf` in
the current working directory.
If your PostgreSQL binaries (`pg_ctl`, `pg_basebackup`) are not in your
`PATH`, you can specify an alternate location in `repmgr.conf`:
pg_bindir=/path/to/postgres/bin
See `repmgr.conf.sample` for an example configuration file with all
available configuration settings annotated.
### Starting up
The master node must be registered first using `repmgr master register`,
and standby servers must be registered using `repmgr standby register`;
this inserts details about each node into the control database. Use
`repmgr cluster show` to see the result.
See the `QUICKSTART.md` file for examples of how to use these commands.
Failover
--------
To promote a standby to master, on the standby execute e.g.:
repmgr -f /etc/repmgr/repmgr.conf --verbose standby promote
`repmgr` will attempt to connect to the current master to verify that it
is not available (if it is, `repmgr` will not promote the standby).
Other standby servers need to be told to follow the new master with e.g.:
repmgr -f /etc/repmgr/repmgr.conf --verbose standby follow
See file `FAILOVER.rst` for details on setting up automated failover.
Converting a failed master to a standby
---------------------------------------
Often it's desirable to bring a failed master back into replication
as a standby. First, ensure that the master's PostgreSQL server is
no longer running; then use `repmgr standby clone` to re-sync its
data directory with the current master, e.g.:
repmgr -f /etc/repmgr/repmgr.conf \
--force --rsync-only \
-h node2 -d repmgr -U repmgr --verbose \
standby clone
Here it's essential to use the command line options `--force`, to
ensure `repmgr` will re-use the existing data directory, and
`--rsync-only`, which causes `repmgr` to use `rsync` rather than
`pg_basebackup`, as the latter can only be used to clone a fresh
standby.
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
--force \
standby register
Replication management with repmgrd
-----------------------------------
`repmgrd` is a management and monitoring daemon which runs on standby nodes
and which can automate actions such as failover and updating standbys to
follow the new master.`repmgrd` can be started simply with e.g.:
repmgrd -f /etc/repmgr/repmgr.conf --verbose > $HOME/repmgr/repmgr.log 2>&1
or alternatively:
repmgrd -f /etc/repmgr/repmgr.conf --verbose --monitoring-history > $HOME/repmgr/repmgrd.log 2>&1
which will track replication advance or lag on all registered standbys.
For permanent operation, we recommend using the options `-d/--daemonize` to
detach the `repmgrd` process, and `-p/--pid-file` to write the process PID
to a file.
Example log output (at default log level):
[2015-03-11 13:15:40] [INFO] checking cluster configuration with schema 'repmgr_test'
[2015-03-11 13:15:40] [INFO] checking node 2 in cluster 'test'
[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
Witness server
--------------
In a situation caused e.g. by a network interruption between two
data centres, it's important to avoid a "split-brain" situation where
both sides of the network assume they are the active segment and the
side without an active master unilaterally promotes one of its standbys.
To prevent this situation happening, it's essential to ensure that one
network segment has a "voting majority", so other segments will know
they're in the minority and not attempt to promote a new master. Where
an odd number of servers exists, this is not an issue. However, if each
network has an even number of nodes, it's necessary to provide some way
of ensuring a majority, which is where the witness server becomes useful.
This is not a fully-fledged standby node and is not integrated into
replication, but it effectively represents the "casting vote" when
deciding which network segment has a majority. A witness server can
be set up using `repmgr witness create` (see below for details) and
can run on a dedicated server or an existing node. Note that it only
makes sense to create a witness server in conjunction with running
`repmgrd`; the witness server will require its own `repmgrd` instance.
Monitoring
----------
When `repmgrd` is running with the option `-m/--monitoring-history`, it will
constantly write node status information to the `repl_monitor` table, which can
be queried easily using the view `repl_status`:
repmgr=# SELECT * FROM repmgr_test.repl_status;
-[ RECORD 1 ]-------------+-----------------------------
primary_node | 1
standby_node | 2
standby_name | node2
node_type | standby
active | t
last_monitor_time | 2015-03-11 14:02:34.51713+09
last_wal_primary_location | 0/3012AF0
last_wal_standby_location | 0/3012AF0
replication_lag | 0 bytes
replication_time_lag | 00:00:03.463085
apply_lag | 0 bytes
communication_time_lag | 00:00:00.955385
Event logging and notifications
-------------------------------
To help understand what significant events (e.g. failure of a node) happened
when and for what reason, `repmgr` logs such events into the `repl_events`
table, e.g.:
repmgr_db=# SELECT * from repmgr_test.repl_events ;
node_id | event | successful | event_timestamp | details
---------+------------------+------------+-------------------------------+-----------------------------------------------------------------------------------
1 | master_register | t | 2015-03-16 17:36:21.711796+09 |
2 | standby_clone | t | 2015-03-16 17:36:31.286934+09 | Cloned from host 'localhost', port 5500; backup method: pg_basebackup; --force: N
2 | standby_register | t | 2015-03-16 17:36:32.391567+09 |
(3 rows)
Additionally `repmgr` can execute an external program each time an event is
logged. This program is defined with the configuration variable
`event_notification_command`; the command string can contain the following
placeholders, which will be replaced with the same content which is
written to the `repl_events` table:
%n - node id
%e - event type
%s - success (1 or 0)
%t - timestamp
%d - description
Example:
event_notification_command=/path/to/some-script %n %e %s "%t" "%d"
By default the program defined with `event_notification_command` will be
executed for every event; to restrict execution to certain events, list
these in the parameter `event_notifications`
event_notifications=master_register,standby_register
Following event types currently exist:
master_register
standby_register
standby_clone
standby_promote
witness_create
repmgrd_start
repmgrd_failover_promote
repmgrd_failover_follow
Cascading replication
---------------------
Cascading replication - where a standby can connect to an upstream node and not
the master server itself - was introduced in PostgreSQL 9.2. `repmgr` and
`repmgrd` support cascading replication by keeping track of the relationship
between standby servers - each node record is stored with the node id of its
upstream ("parent") server (except of course the master server).
In a failover situation where the master node fails and a top-level standby
is promoted, a standby connected to another standby will not be affected
and continue working as normal (even if the upstream standby it's connected
to becomes the master node). If however the node's direct upstream fails,
the "cascaded standby" will attempt to reconnect to that node's parent.
To configure standby servers for cascading replication, add the parameter
`upstream_node` to `repmgr.conf` and set it to the id of the node it should
connect to, e.g.:
cluster=test
node=2
node_name=node2
upstream_node=1
Replication slots
-----------------
Replication slots were introduced with PostgreSQL 9.4 and enable standbys to
notify the master of their WAL consumption, ensuring that the master will
not remove any WAL files until they have been received by all standbys.
This mitigates the requirement to manage WAL file retention using
`wal_keep_segments` etc., with the caveat that if a standby fails, no WAL
files will be removed until the standby's replication slot is deleted.
To enable replication slots, set the boolean parameter `use_replication_slots`
in `repmgr.conf`:
use_replication_slots=1
`repmgr` will automatically generate an appropriate slot name, which is
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.
Further reading:
* http://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION-SLOTS
* http://blog.2ndquadrant.com/postgresql-9-4-slots/
Upgrading from repmgr 2
-----------------------
`repmgr 3` is largely compatible with `repmgr 2`; the only step required
to upgrade is to update the `repl_nodes` table to the definition needed
by `repmgr 3`. See the file `sql/repmgr2_repmgr3.sql` for details on how
to do this.
`repmgrd` must *not* be running while `repl_nodes` is being updated.
Existing `repmgr.conf` files can be retained as-is.
---------------------------------------
Reference
---------
### repmgr command reference
Not all of these commands need the ``repmgr.conf`` file, but they need to be able to
connect to the remote and local databases.
You can teach it which is the remote database by using the -h parameter or
as a last parameter in standby clone and standby follow. If you need to specify
a port different then the default 5432 you can specify a -p parameter.
Standby is always considered as localhost and a second -p parameter will indicate
its port if is different from the default one.
* `master register`
Registers a master in a cluster. This command needs to be executed before any
standby nodes are registered.
* `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 clone [node to be cloned]`
Clones a new standby node from the data directory of the master (or
an upstream cascading standby) using `pg_basebackup` or `rsync`.
Additionally it will create the `recovery.conf` file required to
start the server as a standby. This command does not require
`repmgr.conf` to be provided, but does require connection details
of the master or upstream server as command line parameters.
Provide the `-D/--data-dir` option to specify the destination data
directory; if not, the same directory path as on the source server
will be used. By default, `pg_basebackup` will be used to copy data
from the master or upstream node but this can only be used for
bootstrapping new installations. To update an existing but 'stale'
data directory (for example belonging to a failed master), `rsync`
must be used by specifying `--rsync-only`. In this case,
password-less SSH connections between servers are required.
* `standby promote`
Promotes a standby to a master if the current master has failed. This
command requires a valid `repmgr.conf` file for the standby, either
specified explicitly with `-f/--config-file` or located in the current
working directory; no additional arguments are required.
If the standby promotion succeeds, the server will not need to be
restarted. However any other standbys will need to follow the new server,
by using `standby follow` (see below); if `repmgrd` is active, it will
handle this.
This command will not function if the current master is still running.
* `witness create`
Creates a witness server as a separate PostgreSQL instance. This instance
can be on a separate server or a server running an existing node. The
witness server contain a copy of the repmgr metadata tables but will not
be set up as a standby; instead it will update its metadata copy each
time a failover occurs.
Note that it only makes sense to create a witness server if `repmgrd`
is in use; see section "witness server" above.
By default the witness server will use port 5499 to facilitate easier setup
on a server running an existing node.
* `standby follow`
Attaches the standby to a new master. This command requires a valid
`repmgr.conf` file for the standby, either specified explicitly with
`-f/--config-file` or located in the current working directory; no
additional arguments are required.
This command will force a restart of the standby server. It can only be used
to attach a standby to a new master node.
* `cluster show`
Displays information about each node in the replication cluster. This
command polls each registered server and shows its role (master / standby /
witness) or "FAILED" if the node doesn't respond. It polls each server
directly and can be run on any node in the cluster; this is also useful
when analyzing connectivity from a particular node.
This command requires a valid `repmgr.conf` file for the node on which it is
executed, either specified explicitly with `-f/--config-file` or located in
the current working directory; no additional arguments are required.
Example:
repmgr -f /path/to/repmgr.conf cluster show
Role | Connection String
* master | host=node1 dbname=repmgr user=repmgr
standby | host=node2 dbname=repmgr user=repmgr
standby | host=node3 dbname=repmgr user=repmgr
* `cluster cleanup`
Purges monitoring history from the `repl_monitor` table to prevent excessive
table growth. Use the `-k/--keep-history` to specify the number of days of
monitoring history to retain. This command can be used manually or as a
cronjob.
This command requires a valid `repmgr.conf` file for the node on which it is
executed, either specified explicitly with `-f/--config-file` or located in
the current working directory; no additional arguments are required.
### repmgr configuration file
See `repmgr.conf.sample` for an example configuration file with available
configuration settings annotated.
### repmgr database schema
`repmgr` creates a small schema for its own use in the database specified in
each node's `conninfo` configuration parameter. This database can in principle
be any database. The schema name is the global `cluster` name prefixed
with `repmgr_`, so for the example setup above the schema name is
`repmgr_test`.
The schema contains two tables:
* `repl_nodes`
stores information about all registered servers in the cluster
* `repl_monitor`
stores monitoring information about each node (generated by `repmgrd` with
`-m/--monitoring-history` option enabled)
and one view:
* `repl_status`
summarizes the latest monitoring information for each node (generated by `repmgrd` with
`-m/--monitoring-history` option enabled)
### Error codes
`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
Support and Assistance
----------------------
2ndQuadrant provides 24x7 production support for repmgr, including
configuration assistance, installation verification and training for
running a robust replication cluster. For further details see:
* http://2ndquadrant.com/en/support/
There is a mailing list/forum to discuss contributions or issues
http://groups.google.com/group/repmgr
The IRC channel #repmgr is registered with freenode.
Further information is available at http://www.repmgr.org/
We'd love to hear from you about how you use repmgr. Case studies and
news are always welcome. Send us an email at info@2ndQuadrant.com, or
send a postcard to
repmgr
c/o 2ndQuadrant
7200 The Quorum
Oxford Business Park North
Oxford
OX4 2JZ
United Kingdom
Thanks from the repmgr core team.
* Ian Barwick
* Jaime Casanova
* Abhijit Menon-Sen
* Simon Riggs
* Cedric Villemain
Further reading
---------------
* http://blog.2ndquadrant.com/announcing-repmgr-2-0/
* http://blog.2ndquadrant.com/managing-useful-clusters-repmgr/
* http://blog.2ndquadrant.com/easier_postgresql_90_clusters/

1144
README.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +0,0 @@
Summary: repmgr
Name: repmgr
Version: 3.0
Release: 1
License: GPLv3
Group: System Environment/Daemons
URL: http://repmgr.org
Packager: Ian Barwick <ian@2ndquadrant.com>
Vendor: 2ndQuadrant Limited
Distribution: centos
Source0: %{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
%description
repmgr is a utility suite which greatly simplifies
the process of setting up and managing replication
using streaming replication within a cluster of
PostgreSQL servers.
%prep
%setup
%build
export PATH=$PATH:/usr/pgsql-9.3/bin/
%{__make} USE_PGXS=1
%install
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
export PATH=$PATH:/usr/pgsql-9.3/bin/
%{__make} USE_PGXS=1 install DESTDIR=%{buildroot} INSTALL="install -p"
%{__make} USE_PGXS=1 install_prog DESTDIR=%{buildroot} INSTALL="install -p"
%{__make} USE_PGXS=1 install_rhel DESTDIR=%{buildroot} INSTALL="install -p"
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
%files
%defattr(-,root,root)
/usr/bin/repmgr
/usr/bin/repmgrd
/usr/pgsql-9.3/bin/repmgr
/usr/pgsql-9.3/bin/repmgrd
/usr/pgsql-9.3/lib/repmgr_funcs.so
/usr/pgsql-9.3/share/contrib/repmgr.sql
/usr/pgsql-9.3/share/contrib/repmgr_funcs.sql
/usr/pgsql-9.3/share/contrib/uninstall_repmgr.sql
/usr/pgsql-9.3/share/contrib/uninstall_repmgr_funcs.sql
%attr(0755,root,root)/etc/init.d/repmgrd
%attr(0644,root,root)/etc/sysconfig/repmgrd
%attr(0644,root,root)/etc/repmgr/repmgr.conf.sample
%changelog
* Tue Mar 10 2015 Ian Barwick ian@2ndquadrant.com>
- build for repmgr 3.0
* Thu Jun 05 2014 Nathan Van Overloop <nathan.van.overloop@nexperteam.be> 2.0.2
- fix witness creation to create db and user if needed
* Fri Apr 04 2014 Nathan Van Overloop <nathan.van.overloop@nexperteam.be> 2.0.1
- initial build for RHEL6

View File

@@ -1,133 +0,0 @@
#!/bin/sh
#
# chkconfig: - 75 16
# description: Enable repmgrd replication management and monitoring daemon for PostgreSQL
# processname: repmgrd
# pidfile="/var/run/${NAME}.pid"
# Source function library.
INITD=/etc/rc.d/init.d
. $INITD/functions
# Get function listing for cross-distribution logic.
TYPESET=`typeset -f|grep "declare"`
# Get network config.
. /etc/sysconfig/network
DESC="PostgreSQL replication management and monitoring daemon"
NAME=repmgrd
REPMGRD_ENABLED=no
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
# Read configuration variable file if it is present
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
# 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
case "$REPMGRD_ENABLED" in
[Yy]*)
break
;;
*)
exit 0
;;
esac
if [ -z "${REPMGRD_OPTS}" ]
then
echo "Not starting ${NAME}, REPMGRD_OPTS not set in /etc/sysconfig/${NAME}"
exit 0
fi
start()
{
REPMGRD_START=$"Starting ${NAME} service: "
# 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}"
echo
else
failure "${REPMGRD_START}"
echo
script_result=1
fi
}
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}"
else
echo_failure
script_result=1
fi
else
# not running; per LSB standards this is "ok"
echo_success
fi
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status -p $REPMGRD_PIDFILE $NAME
script_result=$?
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 2
esac
exit $script_result

View File

@@ -1,21 +0,0 @@
# default settings for repmgrd. This file is source by /bin/sh from
# /etc/init.d/repmgrd
# 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/repmgr
# pid file
#REPMGRD_PIDFILE=/var/lib/pgsql/repmgr/repmgrd.pid
# log file
#REPMGRD_LOG=/var/lib/pgsql/repmgr/repmgrd.log

View File

@@ -1,35 +0,0 @@
Set up trusted copy between postgres accounts
---------------------------------------------
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 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:
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
Now test that ssh in both directions works. You may have to accept some new
known hosts in the process.

13
TODO
View File

@@ -5,23 +5,14 @@ Known issues in repmgr
the database server using the ``pg_ctl`` command may accidentally the database server using the ``pg_ctl`` command may accidentally
terminate after their associated ssh session ends. terminate after their associated ssh session ends.
* PGPASSFILE may not be passed to pg_basebackup
Planned feature improvements Planned feature improvements
============================ ============================
* Timeline increases when promoting a standby
* A better check which standby did receive most of the data * A better check which standby did receive most of the data
* Make the fact that a standby may be delayed a factor in the voting * Make the fact that a standby may be delayed a factor in the voting
algorithm algorithm
* include support for delayed standbys * include support for delayed standbys
* Create the repmgr user/database on "master register".
* Use pg_basebackup for the data directory, and ALSO rsync for the
configuration files.
* Use pg_basebackup -X s
NOTE: this can be used by including `-X s` in the configuration parameter
`pg_basebackup_options`

View File

@@ -1,11 +1,11 @@
==================================================== =====================================================
PostgreSQL Automatic Failover - User Documentation PostgreSQL Automatic Fail-Over - User Documentation
==================================================== =====================================================
Automatic Failover Automatic Failover
================== ==================
repmgr allows for automatic failover when it detects the failure of the master node. repmgr allows setups for automatic failover when it detects the failure of the master node.
Following is a quick setup for this. Following is a quick setup for this.
Installation Installation
@@ -14,33 +14,34 @@ Installation
For convenience, we define: For convenience, we define:
**node1** **node1**
is the fully qualified domain name of the Master server, IP 192.168.1.10 is the hostname fully qualified of the Master server, IP 192.168.1.10
**node2** **node2**
is the fully qualified domain name of the Standby server, IP 192.168.1.11 is the hostname fully qualified of the Standby server, IP 192.168.1.11
**witness** **witness**
is the fully qualified domain name of the server used as a witness, IP 192.168.1.12 is the hostname fully qualified of the server used for witness, IP 192.168.1.12
**Note:** We don't recommend using names with the status of a server like «masterserver», **Note:** It is not recommanded to use name defining status of a server like «masterserver»,
because it would be confusing once a failover takes place and the Master is this is a name leading to confusion once a failover take place and the Master is
now on the «standbyserver». now on the «standbyserver».
Summary Summary
------- -------
2 PostgreSQL servers are involved in the replication. Automatic failover needs 2 PostgreSQL servers are involved in the replication. Automatic fail-over need
a vote to decide what server it should promote, so an odd number is required. to vote to decide what server it should promote, thus an odd number is required
A witness-repmgrd is installed in a third server where it uses a PostgreSQL and a witness-repmgrd is installed in a third server where it uses a PostgreSQL
cluster to communicate with other repmgrd daemons. cluster to communicate with other repmgrd daemons.
1. Install PostgreSQL in all the servers involved (including the witness server) 1. Install PostgreSQL in all the servers involved (including the server used for
witness)
2. Install repmgr in all the servers involved (including the witness server) 2. Install repmgr in all the servers involved (including the server used for witness)
3. Configure the Master PostreSQL 3. Configure the Master PostreSQL
4. Clone the Master to the Standby using "repmgr standby clone" command 4. Clone the Master to the Standby using "repmgr standby clone" command
5. Configure repmgr in all the servers involved (including the witness server) 5. Configure repmgr in all the servers involved (including the server used for witness)
6. Register Master and Standby nodes 6. Register Master and Standby nodes
@@ -48,31 +49,31 @@ cluster to communicate with other repmgrd daemons.
8. Start the repmgrd daemons in all nodes 8. Start the repmgrd daemons in all nodes
**Note** A complete High-Availability design needs at least 3 servers to still have **Note** A complete Hight-Availability design need at least 3 servers to still have
a backup node after a first failure. a backup node after a first failure.
Install PostgreSQL Install PostgreSQL
------------------ ------------------
You can install PostgreSQL using any of the recommended methods. You should ensure You can install PostgreSQL using any of the recommended methods. You should ensure
it's 9.0 or later. it's 9.0 or superior.
Install repmgr Install repmgr
-------------- --------------
Install repmgr following the steps in the README file. Install repmgr following the steps in the README.
Configure PostreSQL Configure PostreSQL
------------------- -------------------
Log in to node1. Log in node1.
Edit the file postgresql.conf and modify the parameters:: Edit the file postgresql.conf and modify the parameters::
listen_addresses='*' listen_addresses='*'
wal_level = 'hot_standby' wal_level = 'hot_standby'
archive_mode = on archive_mode = on
archive_command = 'cd .' # we can also use exit 0, anything that archive_command = 'cd .' # we can also use exit 0, anything that
# just does nothing # just does nothing
max_wal_senders = 10 max_wal_senders = 10
wal_keep_segments = 5000 # 80 GB required on pg_xlog wal_keep_segments = 5000 # 80 GB required on pg_xlog
@@ -116,9 +117,9 @@ Create the ssh-key for the postgres user and copy it to other servers::
Clone Master Clone Master
------------ ------------
Log in to node2. Log in node2.
Clone node1 (the current Master):: Clone the node1 (the current Master)::
su - postgres su - postgres
repmgr -d repmgr -U repmgr -h node1 standby clone repmgr -d repmgr -U repmgr -h node1 standby clone
@@ -132,7 +133,7 @@ And check everything is fine in the server log.
Configure repmgr Configure repmgr
---------------- ----------------
Log in to each server and configure repmgr by editing the file Log in each server and configure repmgr by editing the file
/etc/repmgr/repmgr.conf:: /etc/repmgr/repmgr.conf::
cluster=my_cluster cluster=my_cluster
@@ -153,13 +154,13 @@ Log in to each server and configure repmgr by editing the file
**node_name** **node_name**
is an identifier for every node. is an identifier for every node.
**conninfo** **conninfo**
is used to connect to the local PostgreSQL server (where the configuration file is) from any node. In the witness server configuration you need to add a 'port=5499' to the conninfo. is used to connect to the local PostgreSQL server (where the configuration file is) from any node. In the witness server configuration it is needed to add a 'port=5499' to the conninfo.
**master_response_timeout** **master_response_timeout**
is the maximum amount of time we are going to wait before deciding the master has died and start the failover procedure. is the maximum amount of time we are going to wait before deciding the master has died and start failover procedure.
**reconnect_attempts** **reconnect_attempts**
is the number of times we will try to reconnect to master after a failure has been detected and before start the failover procedure. is the number of times we will try to reconnect to master after a failure has been detected and before start failover procedure.
**reconnect_interval** **reconnect_interval**
is the amount of time between retries to reconnect to master after a failure has been detected and before start the failover procedure. is the amount of time between retries to reconnect to master after a failure has been detected and before start failover procedure.
**failover** **failover**
configure behavior: *manual* or *automatic*. configure behavior: *manual* or *automatic*.
**promote_command** **promote_command**
@@ -170,14 +171,14 @@ Log in to each server and configure repmgr by editing the file
Register Master and Standby Register Master and Standby
--------------------------- ---------------------------
Log in to node1. Log in node1.
Register the node as Master:: Register the node as Master::
su - postgres su - postgres
repmgr -f /etc/repmgr/repmgr.conf master register repmgr -f /etc/repmgr/repmgr.conf master register
Log in to node2. Register it as a standby:: Log in node2. Register it as a standby::
su - postgres su - postgres
repmgr -f /etc/repmgr/repmgr.conf standby register repmgr -f /etc/repmgr/repmgr.conf standby register
@@ -185,45 +186,38 @@ Log in to node2. Register it as a standby::
Initialize witness server Initialize witness server
------------------------- -------------------------
Log in to witness. Log in witness.
Initialize the witness server:: Initialize the witness server::
su - postgres su - postgres
repmgr -d repmgr -U repmgr -h 192.168.1.10 -D $WITNESS_PGDATA -f /etc/repmgr/repmgr.conf witness create repmgr -d repmgr -U repmgr -h 192.168.1.10 -D $WITNESS_PGDATA -f /etc/repmgr/repmgr.conf witness create
The witness server needs the following information from the command It needs information to connect to the master to copy the configuration of the cluster, also it needs to know where it should initialize it's own $PGDATA.
line: As part of the procees it also ask for the superuser password so it can connect when needed.
* Connection details for the current master, to copy the cluster
configuration.
* A location for initializing its own $PGDATA.
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).
Start the repmgrd daemons Start the repmgrd daemons
------------------------- -------------------------
Log in to node2 and witness:: Log in node2 and witness.
su - postgres su - postgres
repmgrd -f /etc/repmgr/repmgr.conf --daemonize -> /var/log/postgresql/repmgr.log 2>&1 repmgrd -f /etc/repmgr/repmgr.conf > /var/log/postgresql/repmgr.log 2>&1
**Note:** The Master does not need a repmgrd daemon. **Note:** The Master does not need a repmgrd daemon.
Suspend Automatic behavior Suspend Automatic behavior
========================== ==========================
Edit the repmgr.conf of the node to remove from automatic processing and change:: Edit the repmgr.conf of the node to remove from automatic processing and change::
failover=manual failover=manual
Then, signal repmgrd daemon:: Then, signal repmgrd daemon::
su - postgres su - postgres
kill -HUP $(pidof repmgrd) kill -HUP `pidoff repmgrd`
Usage Usage
===== =====

View File

@@ -1,6 +1,6 @@
/* /*
* check_dir.c - Directories management functions * check_dir.c - Directories management functions
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -44,9 +44,9 @@
int int
check_dir(char *dir) check_dir(char *dir)
{ {
DIR *chkdir; DIR *chkdir;
struct dirent *file; struct dirent *file;
int result = 1; int result = 1;
errno = 0; errno = 0;
@@ -58,7 +58,7 @@ check_dir(char *dir)
while ((file = readdir(chkdir)) != NULL) while ((file = readdir(chkdir)) != NULL)
{ {
if (strcmp(".", file->d_name) == 0 || if (strcmp(".", file->d_name) == 0 ||
strcmp("..", file->d_name) == 0) strcmp("..", file->d_name) == 0)
{ {
/* skip this and parent directory */ /* skip this and parent directory */
continue; continue;
@@ -71,7 +71,6 @@ check_dir(char *dir)
} }
#ifdef WIN32 #ifdef WIN32
/* /*
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
* released version * released version
@@ -83,29 +82,29 @@ check_dir(char *dir)
closedir(chkdir); closedir(chkdir);
if (errno != 0) if (errno != 0)
return -1; /* some kind of I/O error? */ return -1; /* some kind of I/O error? */
return result; return result;
} }
/* /*
* Create directory with error log message when failing * Create directory
*/ */
bool bool
create_dir(char *dir) create_directory(char *dir)
{ {
if (mkdir_p(dir, 0700) == 0) if (mkdir_p(dir, 0700) == 0)
return true; return true;
log_err(_("unable to create directory \"%s\": %s\n"), log_err(_("Could not create directory \"%s\": %s\n"),
dir, strerror(errno)); dir, strerror(errno));
return false; return false;
} }
bool bool
set_dir_permissions(char *dir) set_directory_permissions(char *dir)
{ {
return (chmod(dir, 0700) != 0) ? false : true; return (chmod(dir, 0700) != 0) ? false : true;
} }
@@ -128,10 +127,10 @@ mkdir_p(char *path, mode_t omode)
{ {
struct stat sb; struct stat sb;
mode_t numask, mode_t numask,
oumask; oumask;
int first, int first,
last, last,
retval; retval;
char *p; char *p;
p = path; p = path;
@@ -150,8 +149,8 @@ mkdir_p(char *path, mode_t omode)
return 1; return 1;
} }
else if (p[1] == ':' && else if (p[1] == ':' &&
((p[0] >= 'a' && p[0] <= 'z') || ((p[0] >= 'a' && p[0] <= 'z') ||
(p[0] >= 'A' && p[0] <= 'Z'))) (p[0] >= 'A' && p[0] <= 'Z')))
{ {
/* local drive */ /* local drive */
p += 2; p += 2;
@@ -222,9 +221,9 @@ bool
is_pg_dir(char *dir) is_pg_dir(char *dir)
{ {
const size_t buf_sz = 8192; const size_t buf_sz = 8192;
char path[buf_sz]; char path[buf_sz];
struct stat sb; struct stat sb;
int r; int r;
/* test pgdata */ /* test pgdata */
xsnprintf(path, buf_sz, "%s/PG_VERSION", dir); xsnprintf(path, buf_sz, "%s/PG_VERSION", dir);
@@ -242,67 +241,67 @@ is_pg_dir(char *dir)
bool bool
create_pg_dir(char *dir, bool force) create_pgdir(char *dir, bool force)
{ {
bool pg_dir = false; bool pg_dir = false;
/* Check this directory could be used as a PGDATA dir */ /* Check this directory could be used as a PGDATA dir */
switch (check_dir(dir)) switch (check_dir(dir))
{ {
case 0: case 0:
/* dir not there, must create it */ /* dir not there, must create it */
log_info(_("creating directory \"%s\"...\n"), dir); log_info(_("creating directory \"%s\"...\n"), dir);
if (!create_dir(dir))
{
log_err(_("unable to create directory \"%s\"...\n"),
dir);
return false;
}
break;
case 1:
/* Present but empty, fix permissions and use it */
log_info(_("checking and correcting permissions on existing directory %s ...\n"),
dir);
if (!set_dir_permissions(dir))
{
log_err(_("unable to change permissions of directory \"%s\": %s\n"),
dir, strerror(errno));
return false;
}
break;
case 2:
/* Present and not empty */
log_warning(_("directory \"%s\" exists but is not empty\n"),
dir);
pg_dir = is_pg_dir(dir);
/*
* we use force to reduce the time needed to restore a node which
* turn async after a failover or anything else
*/
if (pg_dir && force)
{
/* Let it continue */
break;
}
else if (pg_dir && !force)
{
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;
}
if (!create_directory(dir))
{
log_err(_("couldn't create directory \"%s\"...\n"),
dir);
return false; return false;
default: }
/* Trouble accessing directory */ break;
log_err(_("could not access directory \"%s\": %s\n"), case 1:
dir, strerror(errno)); /* Present but empty, fix permissions and use it */
log_info(_("checking and correcting permissions on existing directory %s ...\n"),
dir);
if (!set_directory_permissions(dir))
{
log_err(_("could not change permissions of directory \"%s\": %s\n"),
dir, strerror(errno));
return false; return false;
}
break;
case 2:
/* Present and not empty */
log_warning(_("directory \"%s\" exists but is not empty\n"),
dir);
pg_dir = is_pg_dir(dir);
/*
* we use force to reduce the time needed to restore a node which
* turn async after a failover or anything else
*/
if (pg_dir && force)
{
/* Let it continue */
break;
}
else if (pg_dir && !force)
{
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;
}
return false;
default:
/* Trouble accessing directory */
log_err(_("could not access directory \"%s\": %s\n"),
dir, strerror(errno));
return false;
} }
return true; return true;
} }

View File

@@ -1,6 +1,6 @@
/* /*
* check_dir.h * check_dir.h
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -20,11 +20,11 @@
#ifndef _REPMGR_CHECK_DIR_H_ #ifndef _REPMGR_CHECK_DIR_H_
#define _REPMGR_CHECK_DIR_H_ #define _REPMGR_CHECK_DIR_H_
int mkdir_p(char *path, mode_t omode); int mkdir_p(char *path, mode_t omode);
int check_dir(char *dir); int check_dir(char *dir);
bool create_dir(char *dir); bool create_directory(char *dir);
bool set_dir_permissions(char *dir); bool set_directory_permissions(char *dir);
bool is_pg_dir(char *dir); bool is_pg_dir(char *dir);
bool create_pg_dir(char *dir, bool force); bool create_pgdir(char *dir, bool force);
#endif #endif

584
config.c
View File

@@ -1,6 +1,6 @@
/* /*
* config.c - Functions to parse the config file * config.c - Functions to parse the config file
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -17,138 +17,57 @@
* *
*/ */
#include <sys/stat.h> /* for stat() */
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include "strutil.h" #include "strutil.h"
#include "repmgr.h" #include "repmgr.h"
static void parse_event_notifications_list(t_configuration_options *options, const char *arg); void
static void tablespace_list_append(t_configuration_options *options, const char *arg);
/*
* parse_config()
*
* Set default options and overwrite with values from provided configuration
* file.
*
* Returns true if a configuration file could be parsed, otherwise false.
*
* Any configuration options changed in this function must also be changed in
* reload_config()
*/
bool
parse_config(const char *config_file, t_configuration_options *options) parse_config(const char *config_file, t_configuration_options *options)
{ {
char *s, char *s, buff[MAXLINELENGTH];
buff[MAXLINELENGTH]; char name[MAXLEN];
char config_file_buf[MAXLEN]; char value[MAXLEN];
char name[MAXLEN];
char value[MAXLEN];
bool config_file_provided = false;
FILE *fp;
/* Sanity checks */ FILE *fp = fopen (config_file, "r");
/* /* Initialize */
* If a configuration file was provided, check it exists, otherwise
* emit an error
*/
if (config_file[0])
{
struct stat config;
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"),
config_file,
strerror(errno)
);
exit(ERR_BAD_CONFIG);
}
config_file_provided = true;
}
/*
* If no configuration file was provided, set to a default file
* which `parse_config()` will attempt to read if it exists
*/
else
{
strncpy(config_file_buf, DEFAULT_CONFIG_FILE, MAXLEN);
}
fp = fopen(config_file_buf, "r");
/*
* 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)); memset(options->cluster_name, 0, sizeof(options->cluster_name));
options->node = -1; options->node = -1;
options->upstream_node = NO_UPSTREAM_NODE;
memset(options->conninfo, 0, sizeof(options->conninfo)); memset(options->conninfo, 0, sizeof(options->conninfo));
options->failover = MANUAL_FAILOVER; options->failover = MANUAL_FAILOVER;
options->priority = DEFAULT_PRIORITY; options->priority = 0;
memset(options->node_name, 0, sizeof(options->node_name)); memset(options->node_name, 0, sizeof(options->node_name));
memset(options->promote_command, 0, sizeof(options->promote_command)); memset(options->promote_command, 0, sizeof(options->promote_command));
memset(options->follow_command, 0, sizeof(options->follow_command)); memset(options->follow_command, 0, sizeof(options->follow_command));
memset(options->rsync_options, 0, sizeof(options->rsync_options)); memset(options->rsync_options, 0, sizeof(options->rsync_options));
memset(options->ssh_options, 0, sizeof(options->ssh_options)); memset(options->ssh_options, 0, sizeof(options->ssh_options));
memset(options->pg_bindir, 0, sizeof(options->pg_bindir)); memset(options->pg_bindir, 0, sizeof(options->pg_bindir));
memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options)); memset(options->pgctl_options, 0, sizeof(options->pgctl_options));
memset(options->pg_basebackup_options, 0, sizeof(options->pg_basebackup_options));
/* default master_response_timeout is 60 seconds */ /* if nothing has been provided defaults to 60 */
options->master_response_timeout = 60; options->master_response_timeout = 60;
/* default to 6 reconnection attempts at intervals of 10 seconds */ /* it defaults to 6 retries with a time between retries of 10s */
options->reconnect_attempts = 6; options->reconnect_attempts = 6;
options->reconnect_intvl = 10; options->reconnect_intvl = 10;
options->monitor_interval_secs = 2; options->monitor_interval_secs = 2;
options->retry_promote_interval_secs = 300; options->retry_promote_interval_secs = 300;
memset(options->event_notification_command, 0, sizeof(options->event_notification_command)); /*
* Since some commands don't require a config file at all, not
options->tablespace_mapping.head = NULL; * having one isn't necessarily a problem.
options->tablespace_mapping.tail = NULL; */
if (fp == NULL)
{
log_err(_("Did not find the configuration file '%s', continuing\n"), config_file);
return;
}
/* Read next line */ /* Read next line */
while ((s = fgets(buff, sizeof buff, fp)) != NULL) while ((s = fgets (buff, sizeof buff, fp)) != NULL)
{ {
bool known_parameter = true;
/* Skip blank lines and comments */ /* Skip blank lines and comments */
if (buff[0] == '\n' || buff[0] == '#') if (buff[0] == '\n' || buff[0] == '#')
continue; continue;
@@ -158,39 +77,32 @@ parse_config(const char *config_file, t_configuration_options *options)
/* Copy into correct entry in parameters struct */ /* Copy into correct entry in parameters struct */
if (strcmp(name, "cluster") == 0) if (strcmp(name, "cluster") == 0)
strncpy(options->cluster_name, value, MAXLEN); strncpy (options->cluster_name, value, MAXLEN);
else if (strcmp(name, "node") == 0) else if (strcmp(name, "node") == 0)
options->node = atoi(value); options->node = atoi(value);
else if (strcmp(name, "upstream_node") == 0)
options->upstream_node = atoi(value);
else if (strcmp(name, "conninfo") == 0) else if (strcmp(name, "conninfo") == 0)
strncpy(options->conninfo, value, MAXLEN); strncpy (options->conninfo, value, MAXLEN);
else if (strcmp(name, "rsync_options") == 0) else if (strcmp(name, "rsync_options") == 0)
strncpy(options->rsync_options, value, QUERY_STR_LEN); strncpy (options->rsync_options, value, QUERY_STR_LEN);
else if (strcmp(name, "ssh_options") == 0) else if (strcmp(name, "ssh_options") == 0)
strncpy(options->ssh_options, value, QUERY_STR_LEN); strncpy (options->ssh_options, value, QUERY_STR_LEN);
else if (strcmp(name, "loglevel") == 0) else if (strcmp(name, "loglevel") == 0)
strncpy(options->loglevel, value, MAXLEN); strncpy (options->loglevel, value, MAXLEN);
else if (strcmp(name, "logfacility") == 0) else if (strcmp(name, "logfacility") == 0)
strncpy(options->logfacility, value, MAXLEN); strncpy (options->logfacility, value, MAXLEN);
else if (strcmp(name, "failover") == 0) else if (strcmp(name, "failover") == 0)
{ {
char failoverstr[MAXLEN]; char failoverstr[MAXLEN];
strncpy(failoverstr, value, MAXLEN); strncpy(failoverstr, value, MAXLEN);
if (strcmp(failoverstr, "manual") == 0) if (strcmp(failoverstr, "manual") == 0)
{
options->failover = MANUAL_FAILOVER; options->failover = MANUAL_FAILOVER;
}
else if (strcmp(failoverstr, "automatic") == 0) else if (strcmp(failoverstr, "automatic") == 0)
{
options->failover = AUTOMATIC_FAILOVER; options->failover = AUTOMATIC_FAILOVER;
}
else else
{ {
log_err(_("value for 'failover' must be 'automatic' or 'manual'\n")); log_warning(_("value for failover option is incorrect, it should be automatic or manual. Defaulting to manual.\n"));
exit(ERR_BAD_CONFIG); options->failover = MANUAL_FAILOVER;
} }
} }
else if (strcmp(name, "priority") == 0) else if (strcmp(name, "priority") == 0)
@@ -208,117 +120,78 @@ parse_config(const char *config_file, t_configuration_options *options)
else if (strcmp(name, "reconnect_interval") == 0) else if (strcmp(name, "reconnect_interval") == 0)
options->reconnect_intvl = atoi(value); options->reconnect_intvl = atoi(value);
else if (strcmp(name, "pg_bindir") == 0) else if (strcmp(name, "pg_bindir") == 0)
strncpy(options->pg_bindir, value, MAXLEN); strncpy (options->pg_bindir, value, MAXLEN);
else if (strcmp(name, "pg_ctl_options") == 0) else if (strcmp(name, "pg_ctl_options") == 0)
strncpy(options->pg_ctl_options, value, MAXLEN); strncpy (options->pgctl_options, value, MAXLEN);
else if (strcmp(name, "pg_basebackup_options") == 0)
strncpy(options->pg_basebackup_options, value, MAXLEN);
else if (strcmp(name, "logfile") == 0) else if (strcmp(name, "logfile") == 0)
strncpy(options->logfile, value, MAXLEN); strncpy(options->logfile, value, MAXLEN);
else if (strcmp(name, "monitor_interval_secs") == 0) else if (strcmp(name, "monitor_interval_secs") == 0)
options->monitor_interval_secs = atoi(value); options->monitor_interval_secs = atoi(value);
else if (strcmp(name, "retry_promote_interval_secs") == 0) else if (strcmp(name, "retry_promote_interval_secs") == 0)
options->retry_promote_interval_secs = atoi(value); options->retry_promote_interval_secs = atoi(value);
else if (strcmp(name, "use_replication_slots") == 0)
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)
parse_event_notifications_list(options, value);
else if (strcmp(name, "tablespace_mapping") == 0)
tablespace_list_append(options, value);
else else
{ log_warning(_("%s/%s: Unknown name/value pair!\n"), name, value);
known_parameter = false;
log_warning(_("%s/%s: unknown name/value pair provided; ignoring\n"), name, value);
}
/*
* Raise an error if a known parameter is provided with an empty value.
* Currently there's no reason why empty parameters are needed; if
* we want to accept those, we'd need to add stricter default checking,
* as currently e.g. an empty `node` value will be converted to '0'.
*/
if(known_parameter == true && !strlen(value)) {
log_err(_("no value provided for parameter '%s'\n"), name);
exit(ERR_BAD_CONFIG);
}
} }
fclose(fp); /* Close file */
fclose (fp);
/* Check config settings */ /* Check config settings */
if (strnlen(options->cluster_name, MAXLEN)==0)
/* The following checks are for the presence of the parameter */
if (*options->cluster_name == '\0')
{ {
log_err(_("required parameter 'cluster' was not found\n")); log_err(_("Cluster name is missing. Check the configuration file.\n"));
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
if (options->node == -1) if (options->node == -1)
{ {
log_err(_("required parameter 'node' was not found\n")); log_err(_("Node information is missing. Check the configuration file.\n"));
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
if (*options->node_name == '\0')
{
log_err(_("required parameter 'node_name' was not found\n"));
exit(ERR_BAD_CONFIG);
}
if (*options->conninfo == '\0')
{
log_err(_("required parameter 'conninfo' was not found\n"));
exit(ERR_BAD_CONFIG);
}
/* The following checks are for valid parameter values */
if (options->master_response_timeout <= 0) if (options->master_response_timeout <= 0)
{ {
log_err(_("'master_response_timeout' must be greater than zero\n")); log_err(_("Master response timeout must be greater than zero. Check the configuration file.\n"));
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
if (options->reconnect_attempts < 0) if (options->reconnect_attempts < 0)
{ {
log_err(_("'reconnect_attempts' must be zero or greater\n")); log_err(_("Reconnect attempts must be zero or greater. Check the configuration file.\n"));
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
if (options->reconnect_intvl < 0) if (options->reconnect_intvl <= 0)
{ {
log_err(_("'reconnect_interval' must be zero or greater\n")); log_err(_("Reconnect intervals must be zero or greater. Check the configuration file.\n"));
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
return true; if (*options->pg_bindir == '\0')
{
log_err(_("pg_bindir config value not found. Check the configuration file.\n"));
exit(ERR_BAD_CONFIG);
}
} }
char * char *
trim(char *s) trim (char *s)
{ {
/* Initialize start, end pointers */ /* Initialize start, end pointers */
char *s1 = s, char *s1 = s, *s2 = &s[strlen (s) - 1];
*s2 = &s[strlen(s) - 1];
/* If string is empty, no action needed */
if(s2 < s1)
return s;
/* Trim and delimit right side */ /* Trim and delimit right side */
while ((isspace(*s2)) && (s2 >= s1)) while ( (isspace (*s2)) && (s2 >= s1) )
--s2; --s2;
*(s2 + 1) = '\0'; *(s2+1) = '\0';
/* Trim left side */ /* Trim left side */
while ((isspace(*s1)) && (s1 < s2)) while ( (isspace (*s1)) && (s1 < s2) )
++s1; ++s1;
/* Copy finished string */ /* Copy finished string */
memmove(s, s1, s2 - s1); memmove (s, s1, s2 - s1);
s[s2 - s1 + 1] = '\0'; s[s2 - s1 + 1] = '\0';
return s; return s;
@@ -327,13 +200,13 @@ trim(char *s)
void void
parse_line(char *buff, char *name, char *value) parse_line(char *buff, char *name, char *value)
{ {
int i = 0; int i = 0;
int j = 0; int j = 0;
/* /*
* first we find the name of the parameter * first we find the name of the parameter
*/ */
for (; i < MAXLEN; ++i) for ( ; i < MAXLEN; ++i)
{ {
if (buff[i] != '=') if (buff[i] != '=')
name[j++] = buff[i]; name[j++] = buff[i];
@@ -346,7 +219,7 @@ parse_line(char *buff, char *name, char *value)
* Now the value * Now the value
*/ */
j = 0; j = 0;
for (++i; i < MAXLEN; ++i) for ( ++i ; i < MAXLEN; ++i)
if (buff[i] == '\'') if (buff[i] == '\'')
continue; continue;
else if (buff[i] != '\n') else if (buff[i] != '\n')
@@ -358,363 +231,98 @@ parse_line(char *buff, char *name, char *value)
} }
bool bool
reload_config(char *config_file, t_configuration_options * orig_options) reload_configuration(char *config_file, t_configuration_options *orig_options)
{ {
PGconn *conn; PGconn *conn;
t_configuration_options new_options; t_configuration_options new_options;
bool config_changed = false;
/* /*
* Re-read the configuration file: repmgr.conf * Re-read the configuration file: repmgr.conf
*/ */
log_info(_("reloading configuration file and updating repmgr tables\n")); log_info(_("Reloading configuration file and updating repmgr tables\n"));
parse_config(config_file, &new_options); parse_config(config_file, &new_options);
if (new_options.node == -1) if (new_options.node == -1)
{ {
log_warning(_("unable to parse new configuration, retaining current configuration\n")); log_warning(_("Cannot load new configuration, will keep current one.\n"));
return false; return false;
} }
if (strcmp(new_options.cluster_name, orig_options->cluster_name) != 0) if (strcmp(new_options.cluster_name, orig_options->cluster_name) != 0)
{ {
log_warning(_("unable to change cluster name, retaining current configuration\n")); log_warning(_("Cannot change cluster name, will keep current configuration.\n"));
return false; return false;
} }
if (new_options.node != orig_options->node) if (new_options.node != orig_options->node)
{ {
log_warning(_("unable to change node ID, retaining current configuration\n")); log_warning(_("Cannot change node number, will keep current configuration.\n"));
return false; return false;
} }
if (strcmp(new_options.node_name, orig_options->node_name) != 0) if (strcmp(new_options.node_name, orig_options->node_name) != 0)
{ {
log_warning(_("unable to change standby name, keeping current configuration\n")); log_warning(_("Cannot change standby name, will keep current configuration.\n"));
return false; return false;
} }
if (new_options.failover != MANUAL_FAILOVER && new_options.failover != AUTOMATIC_FAILOVER) if (new_options.failover != MANUAL_FAILOVER && new_options.failover != AUTOMATIC_FAILOVER)
{ {
log_warning(_("new value for 'failover' must be 'automatic' or 'manual'\n")); log_warning(_("New value for failover is not valid. Should be MANUAL or AUTOMATIC.\n"));
return false; return false;
} }
if (new_options.master_response_timeout <= 0) if (new_options.master_response_timeout <= 0)
{ {
log_warning(_("new value for 'master_response_timeout' must be greater than zero\n")); log_warning(_("New value for master_response_timeout is not valid. Should be greater than zero.\n"));
return false; return false;
} }
if (new_options.reconnect_attempts < 0) if (new_options.reconnect_attempts < 0)
{ {
log_warning(_("new value for 'reconnect_attempts' must be zero or greater\n")); log_warning(_("New value for reconnect_attempts is not valid. Should be greater or equal than zero.\n"));
return false; return false;
} }
if (new_options.reconnect_intvl < 0) if (new_options.reconnect_intvl < 0)
{ {
log_warning(_("new value for 'reconnect_interval' must be zero or greater\n")); log_warning(_("New value for reconnect_interval is not valid. Should be greater or equal than zero.\n"));
return false; return false;
} }
if(strcmp(orig_options->conninfo, new_options.conninfo) != 0) /* Test conninfo string */
conn = establishDBConnection(new_options.conninfo, false);
if (!conn || (PQstatus(conn) != CONNECTION_OK))
{ {
/* Test conninfo string */ log_warning(_("conninfo string is not valid, will keep current configuration.\n"));
conn = establish_db_connection(new_options.conninfo, false); return false;
if (!conn || (PQstatus(conn) != CONNECTION_OK))
{
log_warning(_("'conninfo' string is not valid, retaining current configuration\n"));
return false;
}
PQfinish(conn);
}
/*
* No configuration problems detected - copy any changed values
*
* NB: keep these in the same order as in config.h to make it easier
* to manage them
*/
/* cluster_name */
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)
{
strcpy(orig_options->conninfo, new_options.conninfo);
config_changed = true;
}
/* 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)
{
orig_options->failover = new_options.failover;
config_changed = true;
}
/* 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)
{
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)
{
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)
{
strcpy(orig_options->follow_command, new_options.follow_command);
config_changed = true;
} }
PQfinish(conn);
/* Configuration seems ok, will load new values */
strcpy(orig_options->cluster_name, new_options.cluster_name);
orig_options->node = new_options.node;
strcpy(orig_options->conninfo, new_options.conninfo);
orig_options->failover = new_options.failover;
orig_options->priority = new_options.priority;
strcpy(orig_options->node_name, new_options.node_name);
strcpy(orig_options->promote_command, new_options.promote_command);
strcpy(orig_options->follow_command, new_options.follow_command);
strcpy(orig_options->rsync_options, new_options.rsync_options);
strcpy(orig_options->ssh_options, new_options.ssh_options);
orig_options->master_response_timeout = new_options.master_response_timeout;
orig_options->reconnect_attempts = new_options.reconnect_attempts;
orig_options->reconnect_intvl = new_options.reconnect_intvl;
/* /*
* XXX These ones can change with a simple SIGHUP? * XXX These ones can change with a simple SIGHUP?
*
* strcpy (orig_options->loglevel, new_options.loglevel); strcpy
* (orig_options->logfacility, new_options.logfacility);
*
* logger_shutdown(); XXX do we have progname here ? logger_init(progname,
* orig_options.loglevel, orig_options.logfacility);
*/
/* rsync_options */ strcpy (orig_options->loglevel, new_options.loglevel);
if(strcmp(orig_options->rsync_options, new_options.rsync_options) != 0) strcpy (orig_options->logfacility, new_options.logfacility);
{
strcpy(orig_options->rsync_options, new_options.rsync_options);
config_changed = true;
}
/* ssh_options */ logger_shutdown();
if(strcmp(orig_options->ssh_options, new_options.ssh_options) != 0) XXX do we have progname here ?
{ logger_init(progname, orig_options.loglevel, orig_options.logfacility);
strcpy(orig_options->ssh_options, new_options.ssh_options); */
config_changed = true;
}
/* master_response_timeout */ return true;
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)
{
orig_options->reconnect_attempts = new_options.reconnect_attempts;
config_changed = true;
}
/* reconnect_intvl */
if(orig_options->reconnect_intvl != new_options.reconnect_intvl)
{
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)
{
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)
{
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)
{
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)
{
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)
{
orig_options->use_replication_slots = new_options.use_replication_slots;
config_changed = true;
}
if(config_changed == true)
{
log_debug(_("reload_config(): configuration has changed\n"));
}
else
{
log_debug(_("reload_config(): configuration has not changed\n"));
}
return config_changed;
}
/*
* Split argument into old_dir and new_dir and append to tablespace mapping
* list.
*
* Adapted from pg_basebackup.c
*/
static void
tablespace_list_append(t_configuration_options *options, const char *arg)
{
TablespaceListCell *cell;
char *dst;
char *dst_ptr;
const char *arg_ptr;
cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
if(cell == NULL)
{
log_err(_("unable to allocate memory; terminating\n"));
exit(ERR_BAD_CONFIG);
}
dst_ptr = dst = cell->old_dir;
for (arg_ptr = arg; *arg_ptr; arg_ptr++)
{
if (dst_ptr - dst >= MAXPGPATH)
{
log_err(_("directory name too long\n"));
exit(ERR_BAD_CONFIG);
}
if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
; /* skip backslash escaping = */
else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
{
if (*cell->new_dir)
{
log_err(_("multiple \"=\" signs in tablespace mapping\n"));
exit(ERR_BAD_CONFIG);
}
else
{
dst = dst_ptr = cell->new_dir;
}
}
else
*dst_ptr++ = *arg_ptr;
}
if (!*cell->old_dir || !*cell->new_dir)
{
log_err(_("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"\n"),
arg);
exit(ERR_BAD_CONFIG);
}
canonicalize_path(cell->old_dir);
canonicalize_path(cell->new_dir);
if (options->tablespace_mapping.tail)
options->tablespace_mapping.tail->next = cell;
else
options->tablespace_mapping.head = cell;
options->tablespace_mapping.tail = cell;
}
/*
* parse_event_notifications_list()
*
*
*/
static void
parse_event_notifications_list(t_configuration_options *options, const char *arg)
{
const char *arg_ptr;
char event_type_buf[MAXLEN] = "";
char *dst_ptr = event_type_buf;
for (arg_ptr = arg; arg_ptr <= (arg + strlen(arg)); arg_ptr++)
{
/* ignore whitespace */
if(*arg_ptr == ' ' || *arg_ptr == '\t')
{
continue;
}
/*
* 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')
{
EventNotificationListCell *cell;
cell = (EventNotificationListCell *) pg_malloc0(sizeof(EventNotificationListCell));
if(cell == NULL)
{
log_err(_("unable to allocate memory; terminating\n"));
exit(ERR_BAD_CONFIG);
}
strncpy(cell->event_type, event_type_buf, MAXLEN);
if (options->event_notifications.tail)
{
options->event_notifications.tail->next = cell;
}
else
{
options->event_notifications.head = cell;
}
options->event_notifications.tail = cell;
memset(event_type_buf, 0, MAXLEN);
dst_ptr = event_type_buf;
}
/* ignore duplicated commas */
else if(*arg_ptr == ',')
{
continue;
}
else
{
*dst_ptr++ = *arg_ptr;
}
}
} }

View File

@@ -1,6 +1,6 @@
/* /*
* config.h * config.h
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -20,72 +20,38 @@
#ifndef _REPMGR_CONFIG_H_ #ifndef _REPMGR_CONFIG_H_
#define _REPMGR_CONFIG_H_ #define _REPMGR_CONFIG_H_
#include "postgres_fe.h" #include "repmgr.h"
#include "strutil.h" #include "strutil.h"
typedef struct EventNotificationListCell
{
struct EventNotificationListCell *next;
char event_type[MAXLEN];
} EventNotificationListCell;
typedef struct EventNotificationList
{
EventNotificationListCell *head;
EventNotificationListCell *tail;
} EventNotificationList;
typedef struct TablespaceListCell
{
struct TablespaceListCell *next;
char old_dir[MAXPGPATH];
char new_dir[MAXPGPATH];
} TablespaceListCell;
typedef struct TablespaceList
{
TablespaceListCell *head;
TablespaceListCell *tail;
} TablespaceList;
typedef struct typedef struct
{ {
char cluster_name[MAXLEN]; char cluster_name[MAXLEN];
int node; int node;
int upstream_node; char conninfo[MAXLEN];
char conninfo[MAXLEN]; int failover;
int failover; int priority;
int priority; char node_name[MAXLEN];
char node_name[MAXLEN]; char promote_command[MAXLEN];
char promote_command[MAXLEN]; char follow_command[MAXLEN];
char follow_command[MAXLEN]; char loglevel[MAXLEN];
char loglevel[MAXLEN]; char logfacility[MAXLEN];
char logfacility[MAXLEN]; char rsync_options[QUERY_STR_LEN];
char rsync_options[QUERY_STR_LEN]; char ssh_options[QUERY_STR_LEN];
char ssh_options[QUERY_STR_LEN]; int master_response_timeout;
int master_response_timeout; int reconnect_attempts;
int reconnect_attempts; int reconnect_intvl;
int reconnect_intvl; char pg_bindir[MAXLEN];
char pg_bindir[MAXLEN]; char pgctl_options[MAXLEN];
char pg_ctl_options[MAXLEN]; char logfile[MAXLEN];
char pg_basebackup_options[MAXLEN]; int monitor_interval_secs;
char logfile[MAXLEN]; int retry_promote_interval_secs;
int monitor_interval_secs; } t_configuration_options;
int retry_promote_interval_secs;
int use_replication_slots;
char event_notification_command[MAXLEN];
EventNotificationList event_notifications;
TablespaceList tablespace_mapping;
} t_configuration_options;
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} } #define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", 0, 0 }
void parse_config(const char *config_file, t_configuration_options *options);
bool parse_config(const char *config_file, t_configuration_options *options); void parse_line(char *buff, char *name, char *value);
void parse_line(char *buff, char *name, char *value); char *trim(char *s);
char *trim(char *s); bool reload_configuration(char *config_file, t_configuration_options *orig_options);
bool reload_config(char *config_file, t_configuration_options *orig_options);
#endif #endif

1087
dbutils.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* /*
* dbutils.h * dbutils.h
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -20,49 +20,25 @@
#ifndef _REPMGR_DBUTILS_H_ #ifndef _REPMGR_DBUTILS_H_
#define _REPMGR_DBUTILS_H_ #define _REPMGR_DBUTILS_H_
#include "config.h"
#include "strutil.h" #include "strutil.h"
PGconn *establishDBConnection(const char *conninfo, const bool exit_on_error);
PGconn *establishDBConnectionByParams(const char *keywords[],
const char *values[],
const bool exit_on_error);
int is_standby(PGconn *conn);
int is_witness(PGconn *conn, char *schema, char *cluster, int node_id);
bool is_pgup(PGconn *conn, int timeout);
char *pg_version(PGconn *conn, char* major_version);
int guc_set(PGconn *conn, const char *parameter, const char *op,
const char *value);
int guc_set_typed(PGconn *conn, const char *parameter, const char *op,
const char *value, const char *datatype);
const char *get_cluster_size(PGconn *conn);
PGconn *getMasterConnection(PGconn *standby_conn, char *schema, char *cluster,
int *master_id, char *master_conninfo_out);
PGconn *establish_db_connection(const char *conninfo, int wait_connection_availability(PGconn *conn, long long timeout);
const bool exit_on_error); bool CancelQuery(PGconn *conn, int timeout);
PGconn *establish_db_connection_by_params(const char *keywords[],
const char *values[],
const bool exit_on_error);
bool check_cluster_schema(PGconn *conn);
int is_standby(PGconn *conn);
bool is_pgup(PGconn *conn, int timeout);
int get_master_node_id(PGconn *conn, char *cluster);
int get_server_version(PGconn *conn, char *server_version);
bool get_cluster_size(PGconn *conn, char *size);
bool get_pg_setting(PGconn *conn, const char *setting, char *output);
int guc_set(PGconn *conn, const char *parameter, const char *op,
const char *value);
int guc_set_typed(PGconn *conn, const char *parameter, const char *op,
const char *value, const char *datatype);
PGconn *get_upstream_connection(PGconn *standby_conn, char *cluster,
int node_id,
int *upstream_node_id_ptr,
char *upstream_conninfo_out);
PGconn *get_master_connection(PGconn *standby_conn, char *cluster,
int *master_id, char *master_conninfo_out);
int wait_connection_availability(PGconn *conn, long long timeout);
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 start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint);
bool stop_backup(PGconn *conn, char *last_wal_segment);
bool set_config_bool(PGconn *conn, const char *config_param, bool state);
bool copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name);
bool create_node_record(PGconn *conn, char *action, int node, char *type, int upstream_node, char *cluster_name, char *node_name, char *conninfo, int priority, char *slot_name);
bool delete_node_record(PGconn *conn, int node, char *action);
bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);
#endif #endif

View File

@@ -3,7 +3,7 @@ Version: 2.0beta2
Section: database Section: database
Priority: optional Priority: optional
Architecture: all Architecture: all
Depends: rsync, postgresql-9.0 | postgresql-9.1 | postgresql-9.2 | postgresql-9.3 | postgresql-9.4 Depends: rsync, postgresql-9.0 | postgresql-9.1 | postgresql-9.2 | postgresql-9.3
Maintainer: Jaime Casanova <jaime@2ndQuadrant.com> Maintainer: Jaime Casanova <jaime@2ndQuadrant.com>
Description: PostgreSQL replication setup, magament and monitoring Description: PostgreSQL replication setup, magament and monitoring
has two main executables has two main executables

View File

@@ -1,18 +1,14 @@
# default settings for repmgrd. This file is source by /bin/sh from #!/bin/sh
# /etc/init.d/repmgrd # default settings for repmgrd. This file is source by /bin/sh from
# /etc/init.d/repmgrd
# disable repmgrd by default so it won't get started upon installation
# valid values: yes/no # Options for repmgrd
REPMGRD_ENABLED=no REPMGRD_OPTS=""
# Options for repmgrd (required) # repmgrd binary
#REPMGRD_OPTS="--config-file /path/to/repmgr.conf" REPMGR_BIN="/usr/bin/repmgr"
# User to run repmgrd as # pid file
#REPMGRD_USER=postgres REPMGR_PIDFILE="/var/run/repmgrd.pid"
# repmgrd binary
#REPMGR_BIN=/usr/bin/repmgr
# pid file
#REPMGR_PIDFILE=/var/run/repmgrd.pid

View File

@@ -1,101 +1,48 @@
#!/bin/sh #!/bin/sh
### BEGIN INIT INFO ### BEGIN INIT INFO
# Provides: repmgrd # Provides: repmgrd
# Required-Start: $local_fs $remote_fs $network $syslog postgresql # Required-Start: $local_fs $remote_fs $network $syslog $postgresql
# Required-Stop: $local_fs $remote_fs $network $syslog postgresql # Required-Stop: $local_fs $remote_fs $network $syslog $postgresql
# Should-Start: $syslog postgresql # Should-Start: $syslog $postgresql
# Default-Start: 2 3 4 5 # Should-Start: $syslog $postgresql
# Default-Stop: 0 1 6 # Default-Start: 2 3 4 5
# Short-Description: Start/stop repmgrd # Default-Stop: 0 1 6
# Description: Enable repmgrd replication management and monitoring daemon for PostgreSQL # Short-Description: Start/stop repmgrd
### END INIT INFO ### END INIT INFO
set -e set -e
DESC="PostgreSQL replication management and monitoring daemon" if test -f /etc/default/repmgrd; then
NAME=repmgrd . /etc/default/repmgrd
fi
REPMGRD_ENABLED=no
REPMGRD_OPTS= if [ -z "$REPMGRD_BIN" ]; then
REPMGRD_USER=postgres REPMGRD_BIN="/usr/bin/repmgrd"
REPMGRD_BIN=/usr/bin/repmgrd fi
REPMGRD_PIDFILE=/var/run/repmgrd.pid
if [ -z "$REPMGRD_PIDFILE" ]; then
# Read configuration variable file if it is present REPMGRD_PIDFILE="/var/run/repmgrd.pid"
[ -r /etc/default/$NAME ] && . /etc/default/$NAME fi
test -x $REPMGRD_BIN || exit 0 test -x $REPMGRD_BIN || exit 0
case "$REPMGRD_ENABLED" in case "$1" in
[Yy]*) start)
break start-stop-daemon --start --quiet --make-pidfile --pidfile $REPMGRD_PIDFILE --exec $REPMGRD_BIN $REPMGRD_OPTS
;; ;;
*)
exit 0 stop)
;; start-stop-daemon --stop --oknodo --quiet --pidfile $REPMGRD_PIDFILE
esac ;;
# Define LSB log_* functions. restart)
. /lib/lsb/init-functions $0 stop && $0 start || exit 1
;;
if [ -z "$REPMGRD_OPTS" ]
then *)
log_warning_msg "Not starting $NAME, REPMGRD_OPTS not set in /etc/default/$NAME" echo "Usage: $0 {start|stop|restart}"
exit 0 exit 1
fi ;;
esac
do_start()
{ exit 0
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# other if daemon could not be started or a failure occured
start-stop-daemon --start --quiet --background --chuid $REPMGRD_USER --make-pidfile --pidfile $REPMGRD_PIDFILE --exec $REPMGRD_BIN -- $REPMGRD_OPTS
}
do_stop()
{
# Return
# 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 --exec $REPMGRD_BIN
}
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_progress_msg "already started"
log_end_msg 0 ;;
*) log_end_msg 1 ;;
esac
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0) log_end_msg 0 ;;
1) log_progress_msg "already stopped"
log_end_msg 0 ;;
*) log_end_msg 1 ;;
esac
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
status_of_proc -p $REPMGRD_PIDFILE $REPMGRD_BIN $NAME && exit 0 || exit $?
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" >&2
exit 3
;;
esac
exit 0

View File

@@ -1,6 +1,6 @@
/* /*
* errcode.h * errcode.h
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,15 +25,16 @@
#define SUCCESS 0 #define SUCCESS 0
#define ERR_BAD_CONFIG 1 #define ERR_BAD_CONFIG 1
#define ERR_BAD_RSYNC 2 #define ERR_BAD_RSYNC 2
#define ERR_STOP_BACKUP 3
#define ERR_NO_RESTART 4 #define ERR_NO_RESTART 4
#define ERR_NEEDS_XLOG 5
#define ERR_DB_CON 6 #define ERR_DB_CON 6
#define ERR_DB_QUERY 7 #define ERR_DB_QUERY 7
#define ERR_PROMOTED 8 #define ERR_PROMOTED 8
#define ERR_BAD_PASSWORD 9 #define ERR_BAD_PASSWORD 9
#define ERR_STR_OVERFLOW 10 #define ERR_STR_OVERFLOW 10
#define ERR_FAILOVER_FAIL 11 #define ERR_FAILOVER_FAIL 11
#define ERR_BAD_SSH 12 #define ERR_BAD_SSH 12
#define ERR_SYS_FAILURE 13 #define ERR_SYS_FAILURE 13
#define ERR_BAD_BASEBACKUP 14
#endif /* _ERRCODE_H_ */ #endif /* _ERRCODE_H_ */

126
log.c
View File

@@ -1,6 +1,6 @@
/* /*
* log.c - Logging methods * log.c - Logging methods
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
* This module is a set of methods for logging (currently only syslog) * This module is a set of methods for logging (currently only syslog)
* *
@@ -39,23 +39,24 @@
/* #define REPMGR_DEBUG */ /* #define REPMGR_DEBUG */
void void stderr_log_with_level(const char *level_name, int level, const char *fmt, ...) {
stderr_log_with_level(const char *level_name, int level, const char *fmt, ...) size_t len = strlen(fmt);
{ char fmt1[len + 150];
time_t t; time_t t;
struct tm *tm; struct tm *tm;
char buff[100]; char buff[100];
va_list ap; va_list ap;
if (log_level >= level) if(log_level >= level) {
{
time(&t); time(&t);
tm = localtime(&t); tm = localtime(&t);
strftime(buff, 100, "[%Y-%m-%d %H:%M:%S]", tm);
fprintf(stderr, "%s [%s] ", buff, level_name);
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
strftime(buff, 100, "[%Y-%m-%d %H:%M:%S]", tm);
snprintf(fmt1, len + 150, "%s [%s] %s", buff, level_name, fmt);
vfprintf(stderr, fmt1, ap);
va_end(ap); va_end(ap);
fflush(stderr); fflush(stderr);
@@ -63,20 +64,20 @@ stderr_log_with_level(const char *level_name, int level, const char *fmt, ...)
} }
static int detect_log_level(const char *level); static int detect_log_level(const char* level);
static int detect_log_facility(const char *facility); static int detect_log_facility(const char* facility);
int log_type = REPMGR_STDERR; int log_type = REPMGR_STDERR;
int log_level = LOG_NOTICE; int log_level = LOG_NOTICE;
bool bool logger_init(t_configuration_options *opts, const char* ident, const char* level, const char* facility)
logger_init(t_configuration_options * opts, const char *ident, const char *level, const char *facility)
{ {
int l;
int f; int l;
int f;
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
int syslog_facility = DEFAULT_SYSLOG_FACILITY; int syslog_facility = DEFAULT_SYSLOG_FACILITY;
#endif #endif
#ifdef REPMGR_DEBUG #ifdef REPMGR_DEBUG
@@ -133,33 +134,26 @@ logger_init(t_configuration_options * opts, const char *ident, const char *level
if (log_type == REPMGR_SYSLOG) if (log_type == REPMGR_SYSLOG)
{ {
setlogmask(LOG_UPTO(log_level)); setlogmask (LOG_UPTO (log_level));
openlog(ident, LOG_CONS | LOG_PID | LOG_NDELAY, syslog_facility); openlog (ident, LOG_CONS | LOG_PID | LOG_NDELAY, syslog_facility);
stderr_log_notice(_("Setup syslog (level: %s, facility: %s)\n"), level, facility); stderr_log_notice(_("Setup syslog (level: %s, facility: %s)\n"), level, facility);
} }
#endif #endif
if (*opts->logfile) if (*opts->logfile)
{ {
FILE *fd; freopen(opts->logfile, "a", stderr);
fd = freopen(opts->logfile, "a", stderr);
if (fd == NULL)
{
fprintf(stderr, "error reopening stderr to '%s': %s",
opts->logfile, strerror(errno));
}
} }
return true; return true;
} }
bool bool logger_shutdown(void)
logger_shutdown(void)
{ {
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
if (log_type == REPMGR_SYSLOG) if (log_type == REPMGR_SYSLOG)
closelog(); closelog();
@@ -173,15 +167,13 @@ logger_shutdown(void)
* options, which might increase requested logging over what's specified * options, which might increase requested logging over what's specified
* in the regular configuration file. * in the regular configuration file.
*/ */
void void logger_min_verbose(int minimum)
logger_min_verbose(int minimum)
{ {
if (log_level < minimum) if (log_level < minimum)
log_level = minimum; log_level = minimum;
} }
int int detect_log_level(const char* level)
detect_log_level(const char *level)
{ {
if (!strcmp(level, "DEBUG")) if (!strcmp(level, "DEBUG"))
return LOG_DEBUG; return LOG_DEBUG;
@@ -203,42 +195,40 @@ detect_log_level(const char *level)
return 0; return 0;
} }
int int detect_log_facility(const char* facility)
detect_log_facility(const char *facility)
{ {
int local = 0; int local = 0;
if (!strncmp(facility, "LOCAL", 5) && strlen(facility) == 6) if (!strncmp(facility, "LOCAL", 5) && strlen(facility) == 6)
{ {
local = atoi(&facility[5]); local = atoi (&facility[5]);
switch (local) switch (local)
{ {
case 0: case 0:
return LOG_LOCAL0; return LOG_LOCAL0;
break; break;
case 1: case 1:
return LOG_LOCAL1; return LOG_LOCAL1;
break; break;
case 2: case 2:
return LOG_LOCAL2; return LOG_LOCAL2;
break; break;
case 3: case 3:
return LOG_LOCAL3; return LOG_LOCAL3;
break; break;
case 4: case 4:
return LOG_LOCAL4; return LOG_LOCAL4;
break; break;
case 5: case 5:
return LOG_LOCAL5; return LOG_LOCAL5;
break; break;
case 6: case 6:
return LOG_LOCAL6; return LOG_LOCAL6;
break; break;
case 7: case 7:
return LOG_LOCAL7; return LOG_LOCAL7;
break; break;
} }
} }

37
log.h
View File

@@ -1,6 +1,6 @@
/* /*
* log.h * log.h
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,9 +25,7 @@
#define REPMGR_SYSLOG 1 #define REPMGR_SYSLOG 1
#define REPMGR_STDERR 2 #define REPMGR_STDERR 2
void void stderr_log_with_level(const char *level_name, int level, const char *fmt, ...) __attribute__ ((format (PG_PRINTF_ATTRIBUTE, 3, 4)));
stderr_log_with_level(const char *level_name, int level, const char *fmt,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
/* Standard error logging */ /* Standard error logging */
#define stderr_log_debug(...) stderr_log_with_level("DEBUG", LOG_DEBUG, __VA_ARGS__) #define stderr_log_debug(...) stderr_log_with_level("DEBUG", LOG_DEBUG, __VA_ARGS__)
@@ -90,16 +88,17 @@ __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
if (log_type == REPMGR_SYSLOG) syslog(LOG_ALERT, __VA_ARGS__); \ if (log_type == REPMGR_SYSLOG) syslog(LOG_ALERT, __VA_ARGS__); \
else stderr_log_alert(__VA_ARGS__); \ else stderr_log_alert(__VA_ARGS__); \
} }
#else #else
#define LOG_EMERG 0 /* system is unusable */ #define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */ #define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */ #define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */ #define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */ #define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */ #define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */ #define LOG_DEBUG 7 /* debug-level messages */
#define log_debug(...) stderr_log_debug(__VA_ARGS__) #define log_debug(...) stderr_log_debug(__VA_ARGS__)
#define log_info(...) stderr_log_info(__VA_ARGS__) #define log_info(...) stderr_log_info(__VA_ARGS__)
@@ -109,18 +108,16 @@ __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
#define log_crit(...) stderr_log_crit(__VA_ARGS__) #define log_crit(...) stderr_log_crit(__VA_ARGS__)
#define log_alert(...) stderr_log_alert(__VA_ARGS__) #define log_alert(...) stderr_log_alert(__VA_ARGS__)
#define log_emerg(...) stderr_log_emerg(__VA_ARGS__) #define log_emerg(...) stderr_log_emerg(__VA_ARGS__)
#endif #endif
/* Logger initialisation and shutdown */ /* Logger initialisation and shutdown */
bool logger_shutdown(void); bool logger_shutdown(void);
bool logger_init(t_configuration_options *opts, const char* ident, const char* level, const char* facility);
void logger_min_verbose(int minimum);
bool logger_init(t_configuration_options * opts, const char *ident, extern int log_type;
const char *level, const char *facility); extern int log_level;
void logger_min_verbose(int minimum);
extern int log_type;
extern int log_level;
#endif #endif

3737
repmgr.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,18 @@
################################################### ###################################################
# Replication Manager sample configuration file # Replication Manager configuration file
################################################### ###################################################
# Required configuration items # Cluster name
# ============================ cluster=test
#
# repmgr and repmgrd require these items to be configured:
# Cluster name - this will be used by repmgr to generate its internal # Node ID
# 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
# 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 master)
node=2 node=2
node_name=node2 node_name=standby2
# Database connection information # Connection information
conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr' conninfo='host=192.168.204.104'
rsync_options=--archive --checksum --compress --progress --rsh="ssh -o \"StrictHostKeyChecking no\""
# Optional configuration items ssh_options=-o "StrictHostKeyChecking no"
# ============================
# Replication settings
# ---------------------
# 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 master node, this parameter is not required.
#
# upstream_node=1
# physical replication slots - PostgreSQL 9.4 and later only
# (default: 0)
#
# use_replication_slots=0
# Logging and monitoring settings
# -------------------------------
# Log level: possible values are DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG
# (default: NOTICE)
loglevel=NOTICE
# Logging facility: possible values are STDERR or - for Syslog integration - one of LOCAL0, LOCAL1, ..., LOCAL7, USER
# (default: STDERR)
logfacility=STDERR
# stderr can be redirected to an arbitrary file:
#
# logfile='/var/log/repmgr.log'
# event notifications can be passed to an arbitrary external program
# together with the following parameters:
#
# %n - node ID
# %e - event type
# %s - success (1 or 0)
# %t - timestamp
# %d - details
#
# the values provided for "%t" and "%d" will probably contain spaces,
# so should be quoted in the provided command configuration, e.g.:
#
# event_notification_command='/path/to/some/script %n %e %s "%t" "%d"'
# By default, all notifications will be passed; the notification types
# can be filtered to explicitly named ones:
#
# event_notifications=master_register,standby_register,witness_create
# Environment/command settings
# ----------------------------
# path to PostgreSQL binary directory (location of pg_ctl, pg_basebackup etc.)
# (if not provided, defaults to system $PATH)
# pg_bindir=/usr/bin/
# external command options
# rsync_options=--archive --checksum --compress --progress --rsh="ssh -o \"StrictHostKeyChecking no\""
# ssh_options=-o "StrictHostKeyChecking no"
# external command arguments
# pg_ctl_options='-s'
# pg_basebackup_options='--xlog-method=s'
# Standby clone settings
# ----------------------
#
# These settings apply when cloning a standby (`repmgr standby clone`).
# Tablespaces can be remapped from one file system location to another:
#
# tablespace_mapping=/path/to/original/tablespace=/path/to/new/tablespace
# Failover settings (repmgrd)
# ---------------------------
#
# These settings are only applied when repmgrd is running.
# How many seconds we wait for master response before declaring master failure # How many seconds we wait for master response before declaring master failure
master_response_timeout=60 master_response_timeout=60
@@ -118,20 +22,41 @@ reconnect_attempts=6
reconnect_interval=10 reconnect_interval=10
# Autofailover options # Autofailover options
failover=automatic # one of 'automatic', 'manual' failover=manual
priority=100 # a value of zero or less prevents the node being promoted to master priority=-1
promote_command='repmgr standby promote -f /path/to/repmgr.conf' promote_command='repmgr standby promote -f /path/to/repmgr.conf'
follow_command='repmgr standby follow -f /path/to/repmgr.conf -W' follow_command='repmgr standby follow -f /path/to/repmgr.conf -W'
# monitoring interval; default is 2s # Log level: possible values are DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG
# Default: NOTICE
loglevel=NOTICE
# Logging facility: possible values are STDERR or - for Syslog integration - one of LOCAL0, LOCAL1, ..., LOCAL7, USER
# Default: STDERR
logfacility=STDERR
# path to pg_ctl executable
pg_bindir=/usr/bin/
#
# you may add command line arguments for pg_ctl
#
# pg_ctl_options='-s'
#
# redirect stderr to a logfile
#
# logfile='/var/log/repmgr.log'
#
# change monitoring interval; default is 2s
# #
# monitor_interval_secs=2 # monitor_interval_secs=2
# change wait time for master; before we bail out and exit when the master #
# disappears, we wait 'reconnect_attempts' * 'retry_promote_interval_secs' # change wait time for master; before we bail out and exit when the
# seconds; by default this would be half an hour, as 'retry_promote_interval_secs' # master disappears, we wait 6 * retry_promote_interval_secs seconds;
# default value is 300) # by default this would be half an hour (since sleep_delay default
# value is 300)
# #
# retry_promote_interval_secs=300 # retry_promote_interval_secs=300

View File

@@ -1,6 +1,6 @@
/* /*
* repmgr.h * repmgr.h
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -21,22 +21,20 @@
#define _REPMGR_H_ #define _REPMGR_H_
#include "postgres_fe.h" #include "postgres_fe.h"
#include "libpq-fe.h"
#include "getopt_long.h" #include "getopt_long.h"
#include "libpq-fe.h"
#include "strutil.h" #include "strutil.h"
#include "dbutils.h" #include "dbutils.h"
#include "errcode.h" #include "errcode.h"
#include "config.h"
#define MIN_SUPPORTED_VERSION "9.3" #define PRIMARY_MODE 0
#define MIN_SUPPORTED_VERSION_NUM 90300 #define STANDBY_MODE 1
#define WITNESS_MODE 2
#include "config.h" #include "config.h"
#define MAXFILENAME 1024 #define MAXFILENAME 1024
#define ERRBUFF_SIZE 512 #define ERRBUFF_SIZE 512
#define DEFAULT_CONFIG_FILE "./repmgr.conf" #define DEFAULT_CONFIG_FILE "./repmgr.conf"
#define DEFAULT_WAL_KEEP_SEGMENTS "5000" #define DEFAULT_WAL_KEEP_SEGMENTS "5000"
@@ -44,69 +42,33 @@
#define DEFAULT_MASTER_PORT "5432" #define DEFAULT_MASTER_PORT "5432"
#define DEFAULT_DBNAME "postgres" #define DEFAULT_DBNAME "postgres"
#define DEFAULT_REPMGR_SCHEMA_PREFIX "repmgr_" #define DEFAULT_REPMGR_SCHEMA_PREFIX "repmgr_"
#define DEFAULT_PRIORITY 100
#define FAILOVER_NODES_MAX_CHECK 50
#define MANUAL_FAILOVER 0 #define MANUAL_FAILOVER 0
#define AUTOMATIC_FAILOVER 1 #define AUTOMATIC_FAILOVER 1
#define NODE_NOT_FOUND -1
#define NO_UPSTREAM_NODE -1
typedef enum {
UNKNOWN = 0,
MASTER,
STANDBY,
WITNESS
} t_server_type;
/* Run time options type */ /* Run time options type */
typedef struct typedef struct
{ {
char dbname[MAXLEN]; char dbname[MAXLEN];
char host[MAXLEN]; char host[MAXLEN];
char username[MAXLEN]; char username[MAXLEN];
char dest_dir[MAXFILENAME]; char dest_dir[MAXFILENAME];
char config_file[MAXFILENAME]; char config_file[MAXFILENAME];
char remote_user[MAXLEN]; char remote_user[MAXLEN];
char superuser[MAXLEN]; char wal_keep_segments[MAXLEN];
char wal_keep_segments[MAXLEN]; bool verbose;
bool verbose; bool force;
bool force; bool wait_for_master;
bool wait_for_master; bool ignore_rsync_warn;
bool ignore_rsync_warn;
bool initdb_no_pwprompt; char masterport[MAXLEN];
bool rsync_only; char localport[MAXLEN];
bool fast_checkpoint;
bool ignore_external_config_files;
char masterport[MAXLEN];
char localport[MAXLEN];
/* parameter used by CLUSTER CLEANUP */ /* parameter used by CLUSTER CLEANUP */
int keep_history; int keep_history;
} t_runtime_options;
char pg_bindir[MAXLEN]; #define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, "", "", 0 }
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, "", "", 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 #endif

View File

@@ -1,7 +1,7 @@
/* /*
* repmgr.sql * repmgr.sql
* *
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
*/ */

2190
repmgrd.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
# #
# Makefile # Makefile
# # Copyright (c) 2ndQuadrant, 2010
# Copyright (c) 2ndQuadrant, 2010-2015
# #
MODULE_big = repmgr_funcs MODULE_big = repmgr_funcs

View File

@@ -1,76 +0,0 @@
/*
* Update a repmgr 2.x installation to repmgr 3.0
* ----------------------------------------------
*
* 1. Stop any running repmgrd instances
* 2. On the master node, execute the SQL statements listed below,
* taking care to identify the master node and any inactive
* nodes
* 3. Restart repmgrd (being sure to use repmgr 3.0)
*/
/*
* Set the search path to the name of the schema used by
* your repmgr installation
* (this should be "repmgr_" + the cluster name defined in
* 'repmgr.conf')
*/
-- SET search_path TO 'name_of_repmgr_schema';
BEGIN;
ALTER TABLE repl_nodes RENAME TO repl_nodes2_0;
CREATE TABLE repl_nodes (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL CHECK (type IN('master','standby','witness')),
upstream_node_id INTEGER NULL REFERENCES repl_nodes (id),
cluster TEXT NOT NULL,
name TEXT NOT NULL,
conninfo TEXT NOT NULL,
slot_name TEXT NULL,
priority INTEGER NOT NULL,
active BOOLEAN NOT NULL DEFAULT TRUE
);
INSERT INTO repl_nodes
(id, type, cluster, name, conninfo, priority)
SELECT id,
CASE
WHEN witness IS TRUE THEN 'witness'
ELSE 'standby'
END AS type,
cluster,
name,
conninfo,
priority + 100
FROM repl_nodes2_0;
/*
* You'll need to set the master explicitly; the following query
* should identify the master node ID but will only work if all
* standby servers are connected:
*
* SELECT id FROM repmgr_test.repl_nodes WHERE name NOT IN (SELECT application_name FROM pg_stat_replication)
*
* If in doubt, execute 'repmgr cluster show' will definitively identify
* the master.
*/
UPDATE repl_nodes SET type = 'master' WHERE id = $master_id;
/* If any nodes are known to be inactive, update them here */
-- UPDATE repl_nodes SET active = FALSE WHERE id IN (...);
/* When you're sure of your changes, commit them */
-- COMMIT;
/*
* execute the following command when you are sure you no longer
* require the old table:
*/
-- DROP TABLE repl_nodes2_0;

View File

@@ -9,7 +9,6 @@
#include "fmgr.h" #include "fmgr.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "replication/walreceiver.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/procarray.h" #include "storage/procarray.h"
@@ -19,7 +18,7 @@
#include "utils/timestamp.h" #include "utils/timestamp.h"
/* same definition as the one in xlog_internal.h */ /* same definition as the one in xlog_internal.h */
#define MAXFNAMELEN 64 #define MAXFNAMELEN 64
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
@@ -28,38 +27,37 @@ PG_MODULE_MAGIC;
*/ */
typedef struct repmgrSharedState typedef struct repmgrSharedState
{ {
LWLockId lock; /* protects search/modification */ LWLockId lock; /* protects search/modification */
char location[MAXFNAMELEN]; /* last known xlog location */ char location[MAXFNAMELEN]; /* last known xlog location */
TimestampTz last_updated; TimestampTz last_updated;
} repmgrSharedState; } repmgrSharedState;
/* Links to shared memory state */ /* Links to shared memory state */
static repmgrSharedState *shared_state = NULL; static repmgrSharedState *shared_state = NULL;
static shmem_startup_hook_type prev_shmem_startup_hook = NULL; static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
void _PG_init(void); void _PG_init(void);
void _PG_fini(void); void _PG_fini(void);
static void repmgr_shmem_startup(void); static void repmgr_shmem_startup(void);
static Size repmgr_memsize(void); static Size repmgr_memsize(void);
static bool repmgr_set_standby_location(char *locationstr); static bool repmgr_set_standby_location(char *locationstr);
Datum repmgr_update_standby_location(PG_FUNCTION_ARGS); Datum repmgr_update_standby_location(PG_FUNCTION_ARGS);
Datum repmgr_get_last_standby_location(PG_FUNCTION_ARGS); Datum repmgr_get_last_standby_location(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(repmgr_update_standby_location); PG_FUNCTION_INFO_V1(repmgr_update_standby_location);
PG_FUNCTION_INFO_V1(repmgr_get_last_standby_location); PG_FUNCTION_INFO_V1(repmgr_get_last_standby_location);
Datum repmgr_update_last_updated(PG_FUNCTION_ARGS); Datum repmgr_update_last_updated(PG_FUNCTION_ARGS);
Datum repmgr_get_last_updated(PG_FUNCTION_ARGS); Datum repmgr_get_last_updated(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(repmgr_update_last_updated); PG_FUNCTION_INFO_V1(repmgr_update_last_updated);
PG_FUNCTION_INFO_V1(repmgr_get_last_updated); PG_FUNCTION_INFO_V1(repmgr_get_last_updated);
/* /*
* Module load callback * Module load callback
*/ */
@@ -70,9 +68,9 @@ _PG_init(void)
* In order to create our shared memory area, we have to be loaded via * In order to create our shared memory area, we have to be loaded via
* shared_preload_libraries. If not, fall out without hooking into any of * shared_preload_libraries. If not, fall out without hooking into any of
* the main system. (We don't throw error here because it seems useful to * the main system. (We don't throw error here because it seems useful to
* allow the repmgr functions to be created even when the module isn't * allow the repmgr functions to be created even when the
* active. The functions must protect themselves against being called * module isn't active. The functions must protect themselves against
* then, however.) * being called then, however.)
*/ */
if (!process_shared_preload_libraries_in_progress) if (!process_shared_preload_libraries_in_progress)
return; return;
@@ -122,15 +120,15 @@ repmgr_shmem_startup(void)
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
shared_state = ShmemInitStruct("repmgr shared state", shared_state = ShmemInitStruct("repmgr shared state",
sizeof(repmgrSharedState), sizeof(repmgrSharedState),
&found); &found);
if (!found) if (!found)
{ {
/* First time through ... */ /* First time through ... */
shared_state->lock = LWLockAssign(); shared_state->lock = LWLockAssign();
snprintf(shared_state->location, snprintf(shared_state->location,
sizeof(shared_state->location), "%X/%X", 0, 0); sizeof(shared_state->location), "%X/%X", 0, 0);
} }
LWLockRelease(AddinShmemInitLock); LWLockRelease(AddinShmemInitLock);
@@ -143,20 +141,20 @@ repmgr_shmem_startup(void)
static Size static Size
repmgr_memsize(void) repmgr_memsize(void)
{ {
return MAXALIGN(sizeof(repmgrSharedState)); return MAXALIGN(sizeof(repmgrSharedState));
} }
static bool static bool
repmgr_set_standby_location(char *locationstr) repmgr_set_standby_location(char *locationstr)
{ {
/* Safety check... */ /* Safety check... */
if (!shared_state) if (!shared_state)
return false; return false;
LWLockAcquire(shared_state->lock, LW_EXCLUSIVE); LWLockAcquire(shared_state->lock, LW_EXCLUSIVE);
strncpy(shared_state->location, locationstr, MAXFNAMELEN); strncpy(shared_state->location, locationstr, MAXFNAMELEN);
LWLockRelease(shared_state->lock); LWLockRelease(shared_state->lock);
return true; return true;
} }
@@ -168,7 +166,7 @@ repmgr_set_standby_location(char *locationstr)
Datum Datum
repmgr_get_last_standby_location(PG_FUNCTION_ARGS) repmgr_get_last_standby_location(PG_FUNCTION_ARGS)
{ {
char location[MAXFNAMELEN]; char location[MAXFNAMELEN];
/* Safety check... */ /* Safety check... */
if (!shared_state) if (!shared_state)
@@ -186,14 +184,14 @@ repmgr_get_last_standby_location(PG_FUNCTION_ARGS)
Datum Datum
repmgr_update_standby_location(PG_FUNCTION_ARGS) repmgr_update_standby_location(PG_FUNCTION_ARGS)
{ {
text *location = PG_GETARG_TEXT_P(0); text *location = PG_GETARG_TEXT_P(0);
char *locationstr; char *locationstr;
/* Safety check... */ /* Safety check... */
if (!shared_state) if (!shared_state)
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
locationstr = text_to_cstring(location); locationstr = text_to_cstring(location);
PG_RETURN_BOOL(repmgr_set_standby_location(locationstr)); PG_RETURN_BOOL(repmgr_set_standby_location(locationstr));
} }
@@ -222,9 +220,9 @@ repmgr_get_last_updated(PG_FUNCTION_ARGS)
{ {
TimestampTz last_updated; TimestampTz last_updated;
/* Safety check... */ /* Safety check... */
if (!shared_state) if (!shared_state)
PG_RETURN_NULL(); PG_RETURN_NULL();
LWLockAcquire(shared_state->lock, LW_EXCLUSIVE); LWLockAcquire(shared_state->lock, LW_EXCLUSIVE);
last_updated = shared_state->last_updated; last_updated = shared_state->last_updated;
@@ -232,5 +230,3 @@ repmgr_get_last_updated(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMPTZ(last_updated); PG_RETURN_TIMESTAMPTZ(last_updated);
} }

View File

@@ -1,6 +1,6 @@
/* /*
* repmgr_function.sql * repmgr_function.sql
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
*/ */

View File

@@ -1,6 +1,6 @@
/* /*
* uninstall_repmgr_funcs.sql * uninstall_repmgr_funcs.sql
* Copyright (c) 2ndQuadrant, 2010-2015 * Copyright (c) 2ndQuadrant, 2010-2014
* *
*/ */

View File

@@ -1,7 +1,7 @@
/* /*
* strutil.c * strutil.c
* *
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,21 +25,29 @@
#include "log.h" #include "log.h"
#include "strutil.h" #include "strutil.h"
static int static int xvsnprintf(char *str, size_t size, const char *format, va_list ap) __attribute__ ((format (PG_PRINTF_ATTRIBUTE, 3, 0)));
xvsnprintf(char *str, size_t size, const char *format, va_list ap)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 0))); /* Add strnlen on platforms that don't have it, like OS X */
#ifndef strnlen
size_t
strnlen(const char *s, size_t n)
{
const char *end = (const char *) memchr(s, '\0', n);
return(end ? end - s : n);
}
#endif
static int static int
xvsnprintf(char *str, size_t size, const char *format, va_list ap) xvsnprintf(char *str, size_t size, const char *format, va_list ap)
{ {
int retval; int retval;
retval = vsnprintf(str, size, format, ap); retval = vsnprintf(str, size, format, ap);
if (retval >= (int) size) if (retval >= (int)size)
{ {
log_err(_("Buffer of size not large enough to format entire string '%s'\n"), log_err(_("Buffer of size not large enough to format entire string '%s'\n"),
str); str);
exit(ERR_STR_OVERFLOW); exit(ERR_STR_OVERFLOW);
} }
@@ -48,10 +56,10 @@ xvsnprintf(char *str, size_t size, const char *format, va_list ap)
int int
xsnprintf(char *str, size_t size, const char *format,...) xsnprintf(char *str, size_t size, const char *format, ...)
{ {
va_list arglist; va_list arglist;
int retval; int retval;
va_start(arglist, format); va_start(arglist, format);
retval = xvsnprintf(str, size, format, arglist); retval = xvsnprintf(str, size, format, arglist);
@@ -62,7 +70,7 @@ xsnprintf(char *str, size_t size, const char *format,...)
int int
sqlquery_snprintf(char *str, const char *format,...) sqlquery_snprintf(char *str, const char *format, ...)
{ {
va_list arglist; va_list arglist;
int retval; int retval;
@@ -75,8 +83,7 @@ sqlquery_snprintf(char *str, const char *format,...)
} }
int int maxlen_snprintf(char *str, const char *format, ...)
maxlen_snprintf(char *str, const char *format,...)
{ {
va_list arglist; va_list arglist;
int retval; int retval;

View File

@@ -1,6 +1,6 @@
/* /*
* strutil.h * strutil.h
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@@ -22,7 +22,7 @@
#define _STRUTIL_H_ #define _STRUTIL_H_
#include <stdlib.h> #include <stdlib.h>
#include "errcode.h" #include <errcode.h>
#define QUERY_STR_LEN 8192 #define QUERY_STR_LEN 8192
#define MAXLEN 1024 #define MAXLEN 1024
@@ -31,16 +31,13 @@
#define MAXCONNINFO 1024 #define MAXCONNINFO 1024
extern int extern int xsnprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (PG_PRINTF_ATTRIBUTE, 3, 4)));
xsnprintf(char *str, size_t size, const char *format,...) extern int sqlquery_snprintf(char *str, const char *format, ...) __attribute__ ((format (PG_PRINTF_ATTRIBUTE, 2, 3)));
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4))); extern int maxlen_snprintf(char *str, const char *format, ...) __attribute__ ((format (PG_PRINTF_ATTRIBUTE, 2, 3)));
extern int /* Add strnlen on platforms that don't have it, like OS X */
sqlquery_snprintf(char *str, const char *format,...) #ifndef strnlen
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); extern size_t strnlen(const char *s, size_t n);
#endif
extern int #endif /* _STRUTIL_H_ */
maxlen_snprintf(char *str, const char *format,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
#endif /* _STRUTIL_H_ */

View File

@@ -1,7 +1,7 @@
/* /*
* uninstall_repmgr.sql * uninstall_repmgr.sql
* *
* Copyright (C) 2ndQuadrant, 2010-2015 * Copyright (C) 2ndQuadrant, 2010-2014
* *
*/ */

View File

@@ -1,6 +1,5 @@
#ifndef _VERSION_H_ #ifndef _VERSION_H_
#define _VERSION_H_ #define _VERSION_H_
#define REPMGR_VERSION "3.0.1" #define REPMGR_VERSION "2.0RC1"
#endif #endif