Compare commits

..

109 Commits

Author SHA1 Message Date
Ian Barwick
310f3f31f9 Update HISTORY 2016-01-05 13:36:34 +09:00
Ian Barwick
4f849de95e Suppress warning about -w/--wal-keep-segments unless actually specified
Already implemented in HEAD.
2016-01-05 13:16:38 +09:00
Ian Barwick
0de4260664 repmgr: -r/--rsync-only does not require a parameter 2016-01-05 11:05:54 +09:00
Ian Barwick
fc75084e42 repmgrd: -v/--verbose option does not require a parameter 2016-01-05 10:50:04 +09:00
Ian Barwick
cfbc9dd3c6 Backport update_node_record_status() 2016-01-04 15:27:47 +09:00
Ian Barwick
94579b5f2e Clean up whitespace and comments 2016-01-04 14:41:15 +09:00
Ian Barwick
e9a25c367a Prevent invalid replication_lag values being written to the monitoring table
A fix for this was introduced with commit ee9270fe8d
and removed in 4f1c67a1bf.

Refactor the original fix to simply omit attempting to write an invalid entry
to the monitoring table.
2016-01-04 14:37:22 +09:00
Ian Barwick
3088096318 No need to manually create repmgr schema. 2016-01-04 14:33:41 +09:00
Ian Barwick
3bbd32c73c Add note about why 'hot_standby=on' is currently required 2016-01-04 14:30:15 +09:00
Martin
ac17033d61 This doesn't really mean the standby s following a new master, so we are
removing it.
Basically, on startup the standby will start receiving again from the
begining of the WAL and so received will be lower then applied.

A proper code is needed to make sure the standby is still following the
correct master (as per node information)
2016-01-04 14:29:56 +09:00
Martín Marqués
711ad0a76c Change where we activate back the standby node that was failed.
We will do it where we are sending the message that says that the
standby has recovered, eliminating some complexity
2016-01-04 14:28:39 +09:00
Martín Marqués
ad988dccce Fix bug discovered last week which prevents recovered standby from being
used in the cluster.
Main issue was that if the local repmgrd was not able to connect locally,
it would set the local node as failed (active = false). This is fine, because
we actually don't know if the node is active (actually, it's not active ATM)
so it's best to keep it out of the cluster.
The problem is that if the postgres service comes back up, and is able to
recover by it self, then we should ack that fact and set it as active.
There was another issue related with repmgrd being terminated if the postgres
service was downs. This is not the correct thing to do: we should keep
trying to connect to the local standby.
2016-01-04 14:28:33 +09:00
Martín Marqués
53fe3c7e5a Fix bug discovered last week which prevents recovered standby from being
used in the cluster.
Main issue was that if the local repmgrd was not able to connect locally,
it would set the local node as failed (active = false). This is fine, because
we actually don't know if the node is active (actually, it's not active ATM)
so it's best to keep it out of the cluster.
The problem is that if the postgres service comes back up, and is able to
recover by it self, then we should ack that fact and set it as active.
There was another issue related with repmgrd being terminated if the postgres
service was downs. This is not the correct thing to do: we should keep
trying to connect to the local standby.
2016-01-04 14:28:26 +09:00
József Kószó
7a439c90d0 Debian init script repmgrd process stop fix 2016-01-04 14:28:20 +09:00
Ian Barwick
87e5257cb8 Short option -c does not take a value 2015-12-22 12:37:38 +09:00
Ian Barwick
1f240ff9b3 Update HISTORY 2015-11-30 16:58:13 +09:00
Ian Barwick
9d6cff0d40 Bump version to 3.0.3 2015-11-30 16:30:50 +09:00
Ian Barwick
f86e251430 Backport drop_replication_slot() from HEAD 2015-11-30 16:30:29 +09:00
Ian Barwick
085b7cb8b4 pg_replslot will only exist in 9.4 and later
We need to clean this up regardless of whether "use_replication_slots"
is set.
2015-11-30 16:19:37 +09:00
Ian Barwick
5ccf89ad9b Ensure pg_replslot directory is cleaned up after "standby clone" with rsync
This ensures the directory is in the same state as it would be
after cloning the standby with pg_basebackup, i.e. empty.
2015-11-30 16:19:31 +09:00
Ian Barwick
6ae5401df0 Update TODO 2015-11-30 16:19:25 +09:00
Ian Barwick
4bd8190d02 Drop a previously created replication slot if base backup fails for any reason
Per Github #129
2015-11-30 16:19:19 +09:00
Ian Barwick
efdc2355a7 Ensure all failures encountered during a base backup jump to the stop_backup label 2015-11-30 16:19:05 +09:00
Ian Barwick
61b1f72a0e Put "starting backup" notice after any slot creation 2015-11-30 16:19:00 +09:00
Abhijit Menon-Sen
882bfd9d8e If we're using replication slots, we need to create them earlier
Otherwise, if the backup takes a long time, we might lose WAL we need
long before we create the slot.
2015-11-30 16:18:52 +09:00
Ian Barwick
c93f717305 Ensure 'master register --force' can't create more than one active primary node record 2015-11-30 16:18:46 +09:00
Ian Barwick
85be96a0be Remove unusable setting
Not a configuration item or command line option;
variable is always false.
2015-11-30 16:18:41 +09:00
Ian Barwick
ce2d4fb86f Make t_node_info generally available
And have it include all the fields from the repl_nodes table.
2015-11-30 16:18:35 +09:00
Ian Barwick
40354e1d62 Add item about hash indexes. 2015-11-30 16:18:29 +09:00
Ian Barwick
3e1655f241 Remove hint about hash indexes entirely.
Anyone needing them, particularly in a replication context, should
know what they're doing anyway.

See also: http://www.postgresql.org/docs/current/interactive/sql-createindex.html#AEN74175

"Also, changes to hash indexes are not replicated over streaming or file-based
 replication after the initial base backup, so they give wrong answers to
 queries that subsequently use them. For these reasons, hash index use is presently
 discouraged."
2015-11-30 16:18:24 +09:00
Ian Barwick
8387e7f65e Add missing 'break' 2015-11-30 16:18:17 +09:00
Ian Barwick
aa4dd155b2 Remove unused variable 2015-11-30 16:17:59 +09:00
Ian Barwick
a171a501ab Shift some common but not terribly informative log messages to verbose mode only 2015-11-30 16:17:52 +09:00
Ian Barwick
f42f771ff4 Logging fixes 2015-11-30 16:17:46 +09:00
Ian Barwick
88cfcf358e Update TODO 2015-11-30 16:17:41 +09:00
Ian Barwick
ce3594d52d Add /etc/repmgr.conf as a default configuration file location
Also refactor configuration file handling while we're at it.

Previously a configuration file would be ignored if it couldn't
be opened, however that is now treated as an error.
2015-11-30 16:17:23 +09:00
Ian Barwick
f64c42a514 Simplify logger_init() parameters
We're passing the t_configuration_options structure anyway, no need to
pass items it contains as separate parameters.
2015-11-30 16:17:17 +09:00
Ian Barwick
3072139d06 Update code comments 2015-11-30 16:17:10 +09:00
Ian Barwick
3b7185fd39 Update TODO 2015-11-30 16:17:06 +09:00
Ian Barwick
819f980e76 Don't display warnings about unused command line parameters in --terse mode 2015-11-30 16:16:58 +09:00
Ian Barwick
49316fb8fb repmgr: don't error out on superfluous command line options
When parsing command line arguments in check_parameters_for_action(),
create warnings for paramters supplied but not required (e.g. -D/--data-dir
for MASTER REGISTER), rather than fail with error(s), as the
presence of the parameters won't cause any problems.

Errors will still be raised for required-but-missing parameters, of course.
2015-11-30 16:16:53 +09:00
Ian Barwick
fa4ff73b87 Remove implemented TODO item 2015-11-30 16:16:46 +09:00
Ian Barwick
29842f0e0d Metadata update also handled by repmgr 2015-11-30 16:16:37 +09:00
Ian Barwick
25db1ba737 When following a new primary, have repmgr (not repmgrd) create the new slot 2015-11-30 16:16:26 +09:00
Ian Barwick
7b9f6f5352 Minor log message fixes 2015-11-30 16:16:03 +09:00
Ian Barwick
53b8f99217 Add a TODO item 2015-11-30 16:15:57 +09:00
Ian Barwick
95cdaac91d Update TODO 2015-11-30 16:15:52 +09:00
Ian Barwick
e7dd0f690c Remove implemented items from TODO list
* repmgr: add explicit --log-level flag, repurpose --verbose flag to
  show extra detailed/repetitive output only (see item below too)

  -> e0cbdd5b31

* debug output: show some repetitive output only if --verbose flag set to prevent
  excessive log growth

  -> 8ab1901a93
2015-11-30 16:15:46 +09:00
Ian Barwick
e0c5bb8d31 Refactor get_master_connection() and update description
Use 'remote_conn' instead of 'master_conn', as the connection
handle can potentially be used for any node.
2015-11-30 16:15:36 +09:00
Ian Barwick
df3e55fa35 get_master_connection(): order node list by node type and priority
This should make it more likely that the actual primary is first
in the retrieved list, reducing the number of connections to
other nodes in the cluster which need to be made.
2015-11-30 16:15:30 +09:00
Ian Barwick
0ee2a1e6ba Code formatting 2015-11-30 16:15:25 +09:00
Ian Barwick
df05214970 Fix variable argument handling with log_hint()/log_verbose() 2015-11-30 16:15:19 +09:00
Ian Barwick
bd1314d232 get_master_connection(): possible to use is_standby() now 2015-11-30 16:15:14 +09:00
Ian Barwick
745566605d Tidy up logging output in dbutils.c
Log all executed SQL if verbose mode is enabled.
2015-11-30 16:15:09 +09:00
Ian Barwick
807dcc1038 Repurpose -v/--verbose; add -t/--terse option (repmgr only)
repmgr and particularly repmgrd currently produce substantial
amounts of log output. Much of this is only useful when troubleshooting
or debugging.

Previously the -v/--verbose option just forced the log level to
INFO. With repmgrd this is pretty pointless - just set the log
level in the configuration file. With repmgr the configuration
file can be overriden by the new -L/--log-level option.

-v/--verbose now provides an additional, chattier/pedantic level
of logging ("Opening *this* logfile", "Executing *this* query",
"running in *this* loop") which is helpful for understanding
repmgr/repmgrd's behaviour, particularly for troubleshooting.
What additional verbose logging is generated will of course a
also depends on the log level set, so e.g. someone trying to
work out which configuration file is actually being opened
can use '--log-level=INFO --verbose' without being bothered
by an avalanche of extra verbose debugging output.

-t/--terse option will silence certain non-essential output, at
the moment any HINTs.

Note that -v/--verbose and -t/--terse are not mutually exclusive
(suggestions for better names welcome).
2015-11-30 16:15:03 +09:00
Ian Barwick
acc0ffa81f Add -L/--log-level command line option to repmgr
Overrides any setting in the config file. This will replace the
-v/--verbose option.
2015-11-30 16:14:54 +09:00
Ian Barwick
1725e90308 Change directory warning to a hint 2015-11-30 16:14:44 +09:00
Ian Barwick
2a3fb89603 Explicitly mark static functions as static 2015-11-30 16:14:39 +09:00
Ian Barwick
8f24167f68 detect_log_level(): return -1 to indicate invalid log level
0 is EMERG, which is not actually used but is valid. Prior to this
change, repmgr would complain about an invalid log level if set to
this.
2015-11-30 16:14:31 +09:00
Ian Barwick
6ce94778d7 README.md: add note about setting repmgr user search path 2015-11-30 16:14:25 +09:00
Ian Barwick
3a3c6d5143 Add TODO items 2015-11-30 16:14:18 +09:00
Ian Barwick
73661637e9 Add hint about -c/--fast-checkpoint
When cloning a server without this option, and pg_start_backup() takes time
to complete, repmgr appears to hang and give no indication of what may
or may not be happening. The hint provides an explanation for any
delay and possible action which could be taken to mitigate it.
2015-11-30 16:14:13 +09:00
Ian Barwick
ae84041a4e Add log_hint() function for logging hints
There are a few places where additional hints are written as log
output, usually LOG_NOTICE. Create an explicit function to provide
hints in a standardized manner; by storing the log level of the
previous logger call, we can ensure the hint is only displayed when
the log message itself would be.

Part of an ongoing effort to better control repmgr's logging output.
2015-11-30 16:14:08 +09:00
Ian Barwick
ea01d1d30b Always use catalog path when calling system functions
Removes any risk of issues due to search path mangling etc.
2015-11-30 16:13:31 +09:00
Ian Barwick
53ed8e948c Clean up help output
master/primary register only has one option
2015-11-30 16:13:22 +09:00
Ian Barwick
43626892d0 Improve configuration file parsing
Related to Github #127.

- use the previously introduced repmgr_atoi() function to parse
  integers better
- collate all detected errors and output as a list, rather than
  failing on the first error.
2015-11-30 16:13:16 +09:00
Ian Barwick
8870b7d7f1 Rename variable 'reconnect_intvl' to 'reconnect_interval'
For consistency with the configuration file parameter name
2015-11-30 16:13:08 +09:00
Ian Barwick
72b1e57251 Use strtol() to parse config file arguments too 2015-11-30 16:13:01 +09:00
Ian Barwick
6054da2c25 Use strtol() in place of atoi() to better verify integer parameters
Per GitHub #127
2015-11-30 16:12:30 +09:00
Ian Barwick
049ea4e24f cluster cleanup: standardize error message and return code 2015-11-30 16:12:19 +09:00
Ian Barwick
5f8185ef3a Fix log wording 2015-11-30 16:12:13 +09:00
Ian Barwick
66a6c15773 Add informtative logging output for 'repmgr cluster cleanup'
Per Github issue #126.
2015-11-30 16:12:06 +09:00
Ian Barwick
919fc0fbef "How many" -> "Number of" 2015-11-30 16:11:57 +09:00
Ian Barwick
c7c117130b "in 9.4" -> "from 9.4" 2015-11-30 16:11:52 +09:00
Ian Barwick
df6517f167 Add note about logfile rotation and repmgrd 2015-11-30 16:11:47 +09:00
Ian Barwick
0bf3fb0605 Point out existence of the FAQ 2015-11-30 16:11:41 +09:00
Ian Barwick
c2172d79a5 Wording tweak to prevent ambiguity. 2015-11-30 16:11:35 +09:00
Ian Barwick
709276a19c Add TODO item 2015-11-30 16:11:27 +09:00
Ian Barwick
3f98e1b91b Replace "slave" with "standby" for consistency
"standby" is used everywhere except in these two error messages.
2015-11-30 16:11:21 +09:00
Ian Barwick
8af08ab3f4 Remove duplicated TODO item
Conflicts:
	TODO
2015-11-30 16:11:01 +09:00
Ian Barwick
ff038a5148 Update HISTORY 2015-11-30 16:10:04 +09:00
Ian Barwick
f56f70c2a6 Specify relevant node in error message 2015-11-30 16:09:59 +09:00
Ian Barwick
d353fe2a9f Terminate repmgrd if standby is no longer connected to upstream 2015-11-30 16:09:50 +09:00
Ian Barwick
a70a44605f Update FAQ entry about witness port specification 2015-11-30 16:09:41 +09:00
Ian Barwick
d14dcb3d8b Add FAQ item about repmgr permissions in pg_hba.conf 2015-11-30 16:09:28 +09:00
Ian Barwick
249ac7c72a Update TODO 2015-11-30 16:09:19 +09:00
Ian Barwick
9d850fc4bd Add note about default log level 2015-11-30 16:09:14 +09:00
Ian Barwick
42cb811a07 Put declarations at top of file 2015-11-30 16:09:08 +09:00
Ian Barwick
1e202540e3 Explicitly set default value for 'use_replication_slots' 2015-11-30 16:09:01 +09:00
Ian Barwick
52db03d320 Add 'primary_response_timeout' as synonym for 'master_response_timeout'
We'll switch terminology in a future release and maintain
'master_response_timeout' for backwards compatibility
2015-11-30 16:08:56 +09:00
Ian Barwick
60d720f0c7 Clarify items in the sample repmgr.conf file
Based on customer feedback.
2015-11-30 16:08:47 +09:00
Ian Barwick
34af7dec2a Use pg_malloc0() instead of malloc()
See also d08bd352c1
2015-11-30 16:08:39 +09:00
Ian Barwick
a59ea243c0 Improve logging and event notifications when following new upstream node 2015-11-30 16:08:30 +09:00
Ian Barwick
0c5025b3d6 Add note about checking replication slots when following upstream node 2015-11-30 16:08:07 +09:00
Ian Barwick
42b79b9b54 Improve log messages when following new primary 2015-11-30 16:07:39 +09:00
Ian Barwick
2e47c6b40b Minor formatting tweak 2015-11-30 16:07:32 +09:00
Ian Barwick
6fbff4747f Clarify some items in sample config file
Also change "master" to "primary" in the comments for consistency
with main PostgreSQL terminology. We'll need to add aliases
for the configuration parameters at some point...
2015-11-30 16:07:23 +09:00
Ian Barwick
cc567d38c8 Update TODO
Conflicts:
	TODO
2015-11-30 16:04:39 +09:00
Ian Barwick
69c552b8e0 Only log some debug items if verbose flag is set. 2015-11-30 16:03:56 +09:00
Ian Barwick
51967d2bd8 Add missing space 2015-11-30 16:03:50 +09:00
Ian Barwick
97be9c0cda Reword log level error message to be more like the Postgres one 2015-11-30 16:03:43 +09:00
Ian Barwick
00a28fbb1e Clarify purpose of get_repmgr_schema() 2015-11-30 16:03:17 +09:00
Ian Barwick
d512bac31d Clean up --help output
There are a confusing number of command line options, some
of which are only valid for particular operations, e.g. "standby clone".
2015-11-30 16:02:59 +09:00
Martín Marqués
fb6781775d Fix bug which prevents repmgrd from starting when the cluster name has
upper case letters.
2015-10-08 19:46:34 -03:00
Ian Barwick
04c751a912 Update HISTORY 2015-10-02 14:40:28 +09:00
Ian Barwick
2615cffecc v3.0.2
Not yet tagged, pending a couple of tests
2015-10-02 11:46:13 +09:00
Ian Barwick
1f838f99c2 Update TODO 2015-10-02 11:46:13 +09:00
Ian Barwick
d3f119005b Update version string to 3.0.1 2015-10-02 11:45:57 +09:00
Ian Barwick
db6d4d8820 Update version string 2015-10-02 11:45:57 +09:00
40 changed files with 1738 additions and 4849 deletions

View File

@@ -2,7 +2,7 @@ License and Contributions
=========================
`repmgr` is licensed under the GPL v3. All of its code and documentation is
Copyright 2010-2016, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
Copyright 2010-2015, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
details.
The development of repmgr has primarily been sponsored by 2ndQuadrant customers.

View File

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

View File

@@ -1 +1,238 @@
The contents of this file have been incorporated into the main README.md document.
====================================================
PostgreSQL Automatic Failover - User Documentation
====================================================
Automatic Failover
==================
repmgr allows for automatic failover when it detects the failure of the master node.
Following is a quick setup for this.
Installation
============
For convenience, we define:
**node1**
is the fully qualified domain name of the Master server, IP 192.168.1.10
**node2**
is the fully qualified domain name of the Standby server, IP 192.168.1.11
**witness**
is the fully qualified domain name of the server used as a witness, IP 192.168.1.12
**Note:** We don't recommend using names with the status of a server like «masterserver»,
because it would be confusing once a failover takes place and the Master is
now on the «standbyserver».
Summary
-------
2 PostgreSQL servers are involved in the replication. Automatic failover needs
a vote to decide what server it should promote, so an odd number is required.
A witness-repmgrd is installed in a third server where it uses a PostgreSQL
cluster to communicate with other repmgrd daemons.
1. Install PostgreSQL in all the servers involved (including the witness server)
2. Install repmgr in all the servers involved (including the witness server)
3. Configure the Master PostreSQL
4. Clone the Master to the Standby using "repmgr standby clone" command
5. Configure repmgr in all the servers involved (including the witness server)
6. Register Master and Standby nodes
7. Initiate witness server
8. Start the repmgrd daemons in all nodes
**Note** A complete High-Availability design needs at least 3 servers to still have
a backup node after a first failure.
Install PostgreSQL
------------------
You can install PostgreSQL using any of the recommended methods. You should ensure
it's 9.0 or later.
Install repmgr
--------------
Install repmgr following the steps in the README file.
Configure PostreSQL
-------------------
Log in to node1.
Edit the file postgresql.conf and modify the parameters::
listen_addresses='*'
wal_level = 'hot_standby'
archive_mode = on
archive_command = 'cd .' # we can also use exit 0, anything that
# just does nothing
max_wal_senders = 10
wal_keep_segments = 5000 # 80 GB required on pg_xlog
hot_standby = on
shared_preload_libraries = 'repmgr_funcs'
Edit the file pg_hba.conf and add lines for the replication::
host repmgr repmgr 127.0.0.1/32 trust
host repmgr repmgr 192.168.1.10/30 trust
host replication all 192.168.1.10/30 trust
**Note:** It is also possible to use a password authentication (md5), .pgpass file
should be edited to allow connection between each node.
Create the user and database to manage replication::
su - postgres
createuser -s repmgr
createdb -O repmgr repmgr
Restart the PostgreSQL server::
pg_ctl -D $PGDATA restart
And check everything is fine in the server log.
Create the ssh-key for the postgres user and copy it to other servers::
su - postgres
ssh-keygen # /!\ do not use a passphrase /!\
cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
exit
rsync -avz ~postgres/.ssh/authorized_keys node2:~postgres/.ssh/
rsync -avz ~postgres/.ssh/authorized_keys witness:~postgres/.ssh/
rsync -avz ~postgres/.ssh/id_rsa* node2:~postgres/.ssh/
rsync -avz ~postgres/.ssh/id_rsa* witness:~postgres/.ssh/
Clone Master
------------
Log in to node2.
Clone node1 (the current Master)::
su - postgres
repmgr -d repmgr -U repmgr -h node1 standby clone
Start the PostgreSQL server::
pg_ctl -D $PGDATA start
And check everything is fine in the server log.
Configure repmgr
----------------
Log in to each server and configure repmgr by editing the file
/etc/repmgr/repmgr.conf::
cluster=my_cluster
node=1
node_name=earth
conninfo='host=192.168.1.10 dbname=repmgr user=repmgr'
master_response_timeout=60
reconnect_attempts=6
reconnect_interval=10
failover=automatic
promote_command='promote_command.sh'
follow_command='repmgr standby follow -f /etc/repmgr/repmgr.conf'
**cluster**
is the name of the current replication.
**node**
is the number of the current node (1, 2 or 3 in the current example).
**node_name**
is an identifier for every node.
**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.
**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.
**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.
**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.
**failover**
configure behavior: *manual* or *automatic*.
**promote_command**
the command executed to do the failover (including the PostgreSQL failover itself). The command must return 0 on success.
**follow_command**
the command executed to address the current standby to another Master. The command must return 0 on success.
Register Master and Standby
---------------------------
Log in to node1.
Register the node as master::
su - postgres
repmgr -f /etc/repmgr/repmgr.conf master register
This will also create the repmgr schema and functions.
Log in to node2. Register it as a standby::
su - postgres
repmgr -f /etc/repmgr/repmgr.conf standby register
Initialize witness server
-------------------------
Log in to witness.
Initialize the witness server::
su - postgres
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
line:
* 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).
By default the witness server will listen on port 5499; this value can be
overridden by explicitly providing the port number in the conninfo string
in repmgr.conf. (Note that it is also possible to specify the port number
with the -l/--local-port option, however this option is now deprecated and
will be overridden by a port setting in the conninfo string).
Start the repmgrd daemons
-------------------------
Log in to node2 and witness::
su - postgres
repmgrd -f /etc/repmgr/repmgr.conf --daemonize -> /var/log/postgresql/repmgr.log 2>&1
**Note:** The Master does not need a repmgrd daemon.
Suspend Automatic behavior
==========================
Edit the repmgr.conf of the node to remove from automatic processing and change::
failover=manual
Then, signal repmgrd daemon::
su - postgres
kill -HUP $(pidof repmgrd)
Usage
=====
The repmgr documentation is in the README file (how to build, options, etc.)

18
FAQ.md
View File

@@ -38,7 +38,7 @@ General
No. Hash indexes and replication do not mix well and their use is
explicitly discouraged; see:
https://www.postgresql.org/docs/current/interactive/sql-createindex.html#AEN74175
http://www.postgresql.org/docs/current/interactive/sql-createindex.html#AEN74175
`repmgr`
--------
@@ -120,22 +120,6 @@ General
permission is for PostgreSQL's streaming replication and doesn't
necessarily need to be the `repmgr` user.
- When cloning a standby, why do I need to provide the connection parameters
for the primary server on the command line, not in the configuration file?
Cloning a standby is a one-time action; the role of the server being cloned
from could change, so fixing it in the configuration file would create
confusion. If `repmgr` needs to establish a connection to the primary
server, it can retrieve this from the `repl_nodes` table or if necessary
scan the replication cluster until it locates the active primary.
- Why is there no foreign key on the `node_id` column in the `repl_events`
table?
Under some circumstances event notifications can be generated for servers
which have not yet been registered; it's also useful to retain a record
of events which includes servers removed from the replication cluster
which no longer have an entry in the `repl_nodes` table.
`repmgrd`
---------

39
HISTORY
View File

@@ -1,42 +1,3 @@
3.1.4 2016-07-
repmgr: new configuration option for setting "restore_command"
in the recovery.conf file generated by repmgr (Martín)
repmgr: add --csv option to "repmgr cluster show" (Gianni)
repmgr: enable provision of a conninfo string as the -d/--dbname
parameter, similar to other PostgreSQL utilities (Ian)
repmgr: during switchover operations improve detection of
demotion candidate shutdown (Ian)
various bugfixes and documentation updates (Ian, Martín)
3.1.3 2016-05-17
repmgrd: enable monitoring when a standby is catching up by
replaying archived WAL (Ian)
repmgrd: when upstream_node_id is NULL, assume upstream node
to be current master (Ian)
repmgrd: check for reappearance of the master node if standby
promotion fails (Ian)
improve handling of rsync failure conditions (Martín)
3.1.2 2016-04-12
Fix pg_ctl path generation in do_standby_switchover() (Ian)
Regularly sync witness server repl_nodes table (Ian)
Documentation improvements (Gianni, dhyannataraj)
(Experimental) ensure repmgr handles failover slots when copying
in rsync mode (Craig, Ian)
rsync mode handling fixes (Martín)
Enable repmgr to compile against 9.6devel (Ian)
3.1.1 2016-02-24
Add '-P/--pwprompt' option for "repmgr create witness" (Ian)
Prevent repmgr/repmgrd running as root (Ian)
3.1.0 2016-02-01
Add "repmgr standby switchover" command (Ian)
Revised README file (Ian)
Remove requirement for 'archive_mode' to be enabled (Ian)
Improve -?/--help output, showing default values if relevant (Ian)
Various bugfixes to command line/configuration parameter handling (Ian)
3.0.3 2016-01-04
Create replication slot if required before base backup is run (Abhijit)
standy clone: when using rsync, clean up "pg_replslot" directory (Ian)

View File

@@ -1,33 +1,24 @@
#
# Makefile
# Copyright (c) 2ndQuadrant, 2010-2016
HEADERS = $(wildcard *.h)
# Copyright (c) 2ndQuadrant, 2010-2015
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 dirmod.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o
DATA = repmgr.sql uninstall_repmgr.sql
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport)
PG_LIBS = $(libpq_pgport)
all: repmgrd repmgr
all: repmgrd repmgr
$(MAKE) -C sql
repmgrd: $(repmgrd_OBJS)
$(CC) -o repmgrd $(CFLAGS) $(repmgrd_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS)
$(CC) $(CFLAGS) $(repmgrd_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o repmgrd
$(MAKE) -C sql
repmgr: $(repmgr_OBJS)
$(CC) -o repmgr $(CFLAGS) $(repmgr_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS)
# Make all objects depend on all include files. This is a bit of a
# shotgun approach, but the codebase is small enough that a complete rebuild
# is very fast anyway.
$(repmgr_OBJS): $(HEADERS)
$(repmgrd_OBJS): $(HEADERS)
$(CC) $(CFLAGS) $(repmgr_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o repmgr
ifdef USE_PGXS
PG_CONFIG = pg_config
@@ -40,8 +31,8 @@ include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
# XXX: This overrides the pgxs install target - we're building two binaries,
# which is not supported by pgxs.mk's PROGRAM construct.
# XXX: Try to use PROGRAM construct (see pgxs.mk) someday. Right now
# is overriding pgxs install.
install: install_prog install_ext
install_prog:
@@ -52,12 +43,6 @@ install_prog:
install_ext:
$(MAKE) -C sql install
# Distribution-specific package building targets
# ----------------------------------------------
#
# XXX we recommend using the PGDG-supplied packages where possible;
# see README.md for details.
install_rhel:
mkdir -p '$(DESTDIR)/etc/init.d/'
$(INSTALL_PROGRAM) RHEL/repmgrd.init '$(DESTDIR)/etc/init.d/repmgrd'
@@ -82,21 +67,16 @@ clean:
rm -f repmgr
$(MAKE) -C sql clean
# Get correct version numbers and install paths, depending on your postgres version
PG_VERSION = $(shell pg_config --version | cut -d ' ' -f 2 | cut -d '.' -f 1,2)
REPMGR_VERSION = $(shell grep REPMGR_VERSION version.h | cut -d ' ' -f 3 | cut -d '"' -f 2)
PKGLIBDIR = $(shell pg_config --pkglibdir)
SHAREDIR = $(shell pg_config --sharedir)
deb: repmgrd repmgr
mkdir -p ./debian/usr/bin
cp repmgrd repmgr ./debian/usr/bin/
mkdir -p ./debian$(SHAREDIR)/contrib/
cp sql/repmgr_funcs.sql ./debian$(SHAREDIR)/contrib/
cp sql/uninstall_repmgr_funcs.sql ./debian$(SHAREDIR)/contrib/
mkdir -p ./debian$(PKGLIBDIR)/
cp sql/repmgr_funcs.so ./debian$(PKGLIBDIR)/
mkdir -p ./debian/usr/share/postgresql/9.0/contrib/
cp sql/repmgr_funcs.sql ./debian/usr/share/postgresql/9.0/contrib/
cp sql/uninstall_repmgr_funcs.sql ./debian/usr/share/postgresql/9.0/contrib/
mkdir -p ./debian/usr/lib/postgresql/9.0/lib/
cp sql/repmgr_funcs.so ./debian/usr/lib/postgresql/9.0/lib/
dpkg-deb --build debian
mv debian.deb ../postgresql-repmgr-$(PG_VERSION)_$(REPMGR_VERSION).deb
mv debian.deb ../postgresql-repmgr-9.0_1.0.0.deb
rm -rf ./debian/usr

View File

@@ -1 +1,118 @@
The contents of this file have been incorporated into the main README.md document.
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 README.md for sample
settings)
- 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=repmgr_node1 user=repmgr_usr dbname=repmgr_db | | 0 | t
2 | standby | 1 | test | node2 | host=repmgr_node2 user=repmgr_usr dbname=repmgr_db | | 0 | t
(2 rows)

1528
README.md

File diff suppressed because it is too large Load Diff

61
RHEL/repmgr3-93.spec Normal file
View File

@@ -0,0 +1,61 @@
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

133
RHEL/repmgrd.init Executable file
View File

@@ -0,0 +1,133 @@
#!/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

21
RHEL/repmgrd.sysconfig Normal file
View File

@@ -0,0 +1,21 @@
# 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/repmgrd
# pid file
#REPMGRD_PIDFILE=/var/lib/pgsql/repmgr/repmgrd.pid
# log file
#REPMGRD_LOG=/var/lib/pgsql/repmgr/repmgrd.log

11
TODO
View File

@@ -40,6 +40,13 @@ Planned feature improvements
* make old master node ID available for event notification commands
(See github issue #80).
* Have pg_basebackup use replication slots, if and when support for
this is added; see:
http://www.postgresql.org/message-id/555DD2B2.7020000@gmx.net
* use "primary/standby" terminology in place of "master/slave" for consistency
with main PostrgreSQL usage
* repmgr standby clone: possibility to use barman instead of performing a new base backup
* possibility to transform a failed master into a new standby with pg_rewind
@@ -53,10 +60,6 @@ Planned feature improvements
requested, activate the replication slot using pg_receivexlog to negate the
need to set `wal_keep_segments` just for the initial clone (9.4 and 9.5).
* repmgr: enable "standby follow" to point a standby at another standby, not
just the replication cluster master (see GitHub #130)
Usability improvements
======================

View File

@@ -1,6 +1,6 @@
/*
* check_dir.c - Directories management functions
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
* check_dir.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
* config.c - Functions to parse the config file
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,10 +28,10 @@ static void parse_event_notifications_list(t_configuration_options *options, con
static void tablespace_list_append(t_configuration_options *options, const char *arg);
static void exit_with_errors(ErrorList *config_errors);
const static char *_progname = NULL;
const static char *_progname = '\0';
static char config_file_path[MAXPGPATH];
static bool config_file_provided = false;
bool config_file_found = false;
static bool config_file_found = false;
void
@@ -149,7 +149,7 @@ load_config(const char *config_file, bool verbose, t_configuration_options *opti
if (verbose == true)
{
log_notice(_("looking for configuration file in %s\n"), sysconf_etc_path);
log_notice(_("looking for configuration file in %s"), sysconf_etc_path);
}
snprintf(config_file_path, MAXPGPATH, "%s/%s", sysconf_etc_path, CONFIG_FILE_NAME);
@@ -198,13 +198,11 @@ parse_config(t_configuration_options *options)
/* For sanity-checking provided conninfo string */
PQconninfoOption *conninfo_options;
char *conninfo_errmsg = NULL;
char *conninfo_errmsg = NULL;
/* Collate configuration file errors here for friendlier reporting */
static ErrorList config_errors = { NULL, NULL };
bool node_found = false;
/* Initialize configuration options with sensible defaults
* note: the default log level is set in log.c and does not need
* to be initialised here
@@ -224,7 +222,6 @@ parse_config(t_configuration_options *options)
memset(options->pg_bindir, 0, sizeof(options->pg_bindir));
memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options));
memset(options->pg_basebackup_options, 0, sizeof(options->pg_basebackup_options));
memset(options->restore_command, 0, sizeof(options->restore_command));
/* default master_response_timeout is 60 seconds */
options->master_response_timeout = 60;
@@ -236,12 +233,7 @@ parse_config(t_configuration_options *options)
options->monitor_interval_secs = 2;
options->retry_promote_interval_secs = 300;
/* default to resyncing repl_nodes table every 30 seconds on the witness server */
options->witness_repl_nodes_sync_interval_secs = 30;
memset(options->event_notification_command, 0, sizeof(options->event_notification_command));
options->event_notifications.head = NULL;
options->event_notifications.tail = NULL;
options->tablespace_mapping.head = NULL;
options->tablespace_mapping.tail = NULL;
@@ -252,7 +244,7 @@ parse_config(t_configuration_options *options)
*/
if (config_file_found == false)
{
log_verbose(LOG_NOTICE, _("no configuration file provided and no default file found - "
log_notice(_("no configuration file provided and no default file found - "
"continuing with default values\n"));
return true;
}
@@ -298,12 +290,9 @@ parse_config(t_configuration_options *options)
if (strcmp(name, "cluster") == 0)
strncpy(options->cluster_name, value, MAXLEN);
else if (strcmp(name, "node") == 0)
{
options->node = repmgr_atoi(value, "node", &config_errors, false);
node_found = true;
}
options->node = repmgr_atoi(value, "node", &config_errors);
else if (strcmp(name, "upstream_node") == 0)
options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors, false);
options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors);
else if (strcmp(name, "conninfo") == 0)
strncpy(options->conninfo, value, MAXLEN);
else if (strcmp(name, "rsync_options") == 0)
@@ -334,7 +323,7 @@ parse_config(t_configuration_options *options)
}
}
else if (strcmp(name, "priority") == 0)
options->priority = repmgr_atoi(value, "priority", &config_errors, true);
options->priority = repmgr_atoi(value, "priority", &config_errors);
else if (strcmp(name, "node_name") == 0)
strncpy(options->node_name, value, MAXLEN);
else if (strcmp(name, "promote_command") == 0)
@@ -342,17 +331,16 @@ parse_config(t_configuration_options *options)
else if (strcmp(name, "follow_command") == 0)
strncpy(options->follow_command, value, MAXLEN);
else if (strcmp(name, "master_response_timeout") == 0)
options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors, false);
/*
* 'primary_response_timeout' as synonym for 'master_response_timeout' -
options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors);
/* 'primary_response_timeout' as synonym for 'master_response_timeout' -
* we'll switch terminology in a future release (3.1?)
*/
else if (strcmp(name, "primary_response_timeout") == 0)
options->master_response_timeout = repmgr_atoi(value, "primary_response_timeout", &config_errors, false);
options->master_response_timeout = repmgr_atoi(value, "primary_response_timeout", &config_errors);
else if (strcmp(name, "reconnect_attempts") == 0)
options->reconnect_attempts = repmgr_atoi(value, "reconnect_attempts", &config_errors, false);
options->reconnect_attempts = repmgr_atoi(value, "reconnect_attempts", &config_errors);
else if (strcmp(name, "reconnect_interval") == 0)
options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", &config_errors, false);
options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", &config_errors);
else if (strcmp(name, "pg_bindir") == 0)
strncpy(options->pg_bindir, value, MAXLEN);
else if (strcmp(name, "pg_ctl_options") == 0)
@@ -362,22 +350,18 @@ parse_config(t_configuration_options *options)
else if (strcmp(name, "logfile") == 0)
strncpy(options->logfile, value, MAXLEN);
else if (strcmp(name, "monitor_interval_secs") == 0)
options->monitor_interval_secs = repmgr_atoi(value, "monitor_interval_secs", &config_errors, false);
options->monitor_interval_secs = repmgr_atoi(value, "monitor_interval_secs", &config_errors);
else if (strcmp(name, "retry_promote_interval_secs") == 0)
options->retry_promote_interval_secs = repmgr_atoi(value, "retry_promote_interval_secs", &config_errors, false);
else if (strcmp(name, "witness_repl_nodes_sync_interval_secs") == 0)
options->witness_repl_nodes_sync_interval_secs = repmgr_atoi(value, "witness_repl_nodes_sync_interval_secs", &config_errors, false);
options->retry_promote_interval_secs = repmgr_atoi(value, "retry_promote_interval_secs", &config_errors);
else if (strcmp(name, "use_replication_slots") == 0)
/* XXX we should have a dedicated boolean argument format */
options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", &config_errors, false);
options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", &config_errors);
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 if (strcmp(name, "restore_command") == 0)
strncpy(options->restore_command, value, MAXLEN);
else
{
known_parameter = false;
@@ -403,17 +387,29 @@ parse_config(t_configuration_options *options)
fclose(fp);
/* Check config settings */
if (node_found == false)
/* The following checks are for the presence of the parameter */
if (*options->cluster_name == '\0')
{
error_list_append(&config_errors, _("\"node\": parameter was not found"));
}
else if (options->node == 0)
{
error_list_append(&config_errors, _("\"node\": must be greater than zero"));
error_list_append(&config_errors, _("\"cluster\": parameter was not found\n"));
}
if (strlen(options->conninfo))
if (options->node == -1)
{
error_list_append(&config_errors, _("\"node\": parameter was not found\n"));
}
if (*options->node_name == '\0')
{
error_list_append(&config_errors, _("\"node_name\": parameter was not found\n"));
}
if (*options->conninfo == '\0')
{
error_list_append(&config_errors, _("\"conninfo\": parameter was not found\n"));
}
else
{
/* Sanity check the provided conninfo string
@@ -795,7 +791,7 @@ error_list_append(ErrorList *error_list, char *error_message)
* otherwise exit
*/
int
repmgr_atoi(const char *value, const char *config_item, ErrorList *error_list, bool allow_negative)
repmgr_atoi(const char *value, const char *config_item, ErrorList *error_list)
{
char *endptr;
long longval = 0;
@@ -826,8 +822,8 @@ repmgr_atoi(const char *value, const char *config_item, ErrorList *error_list, b
}
}
/* Disallow negative values for most parameters */
if (allow_negative == false && longval < 0)
/* Currently there are no values which could be negative */
if (longval < 0)
{
snprintf(error_message_buf,
MAXLEN,

View File

@@ -1,6 +1,6 @@
/*
* config.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2015
*
* 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
@@ -72,22 +72,16 @@ typedef struct
char pg_bindir[MAXLEN];
char pg_ctl_options[MAXLEN];
char pg_basebackup_options[MAXLEN];
char restore_command[MAXLEN];
char logfile[MAXLEN];
int monitor_interval_secs;
int retry_promote_interval_secs;
int witness_repl_nodes_sync_interval_secs;
int use_replication_slots;
char event_notification_command[MAXLEN];
EventNotificationList event_notifications;
TablespaceList tablespace_mapping;
} t_configuration_options;
/*
* The following will initialize the structure with a minimal set of options;
* actual defaults are set in parse_config() before parsing the configuration file
*/
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", "", 0, 0, 0, 0, "", { NULL, NULL }, { NULL, NULL } }
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} }
typedef struct ErrorListCell
{
@@ -112,7 +106,6 @@ char *trim(char *s);
void error_list_append(ErrorList *error_list, char *error_message);
int repmgr_atoi(const char *s,
const char *config_item,
ErrorList *error_list,
bool allow_negative);
ErrorList *error_list);
#endif

462
dbutils.c
View File

@@ -1,6 +1,6 @@
/*
* dbutils.c - Database connection/management functions
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,15 +26,11 @@
#include "strutil.h"
#include "log.h"
#include "catalog/pg_control.h"
char repmgr_schema[MAXLEN] = "";
char repmgr_schema_quoted[MAXLEN] = "";
static int _get_node_record(PGconn *conn, char *cluster, char *sqlquery, t_node_info *node_info);
PGconn *
_establish_db_connection(const char *conninfo, const bool exit_on_error, const bool log_notice)
establish_db_connection(const char *conninfo, const bool exit_on_error)
{
/* Make a connection to the database */
PGconn *conn = NULL;
@@ -50,16 +46,8 @@ _establish_db_connection(const char *conninfo, const bool exit_on_error, const b
/* Check to see that the backend connection was successfully made */
if ((PQstatus(conn) != CONNECTION_OK))
{
if (log_notice)
{
log_notice(_("connection to database failed: %s\n"),
PQerrorMessage(conn));
}
else
{
log_err(_("connection to database failed: %s\n"),
PQerrorMessage(conn));
}
log_err(_("connection to database failed: %s\n"),
PQerrorMessage(conn));
if (exit_on_error)
{
@@ -71,19 +59,6 @@ _establish_db_connection(const char *conninfo, const bool exit_on_error, const b
return conn;
}
PGconn *
establish_db_connection(const char *conninfo, const bool exit_on_error)
{
return _establish_db_connection(conninfo, exit_on_error, false);
}
PGconn *
test_db_connection(const char *conninfo, const bool exit_on_error)
{
return _establish_db_connection(conninfo, exit_on_error, true);
}
PGconn *
establish_db_connection_by_params(const char *keywords[], const char *values[],
const bool exit_on_error)
@@ -333,7 +308,7 @@ get_master_node_id(PGconn *conn, char *cluster)
}
else if (PQntuples(res) == 0)
{
log_verbose(LOG_WARNING, _("get_master_node_id(): no active primary found\n"));
log_warning(_("get_master_node_id(): no active primary found\n"));
retval = NODE_NOT_FOUND;
}
else
@@ -421,7 +396,7 @@ guc_set_typed(PGconn *conn, const char *parameter, const char *op,
" WHERE name = '%s' AND setting::%s %s '%s'::%s",
parameter, datatype, op, value, datatype);
log_verbose(LOG_DEBUG, "guc_set_typed():\n%s\n", sqlquery);
log_verbose(LOG_DEBUG, "guc_set_typed():n%s\n", sqlquery);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -470,6 +445,7 @@ get_cluster_size(PGconn *conn, char *size)
}
bool
get_pg_setting(PGconn *conn, const char *setting, char *output)
{
@@ -512,7 +488,7 @@ get_pg_setting(PGconn *conn, const char *setting, char *output)
if (success == true)
{
log_verbose(LOG_DEBUG, _("get_pg_setting(): returned value is \"%s\"\n"), output);
log_debug(_("get_pg_setting(): returned value is \"%s\"\n"), output);
}
PQclear(res);
@@ -521,48 +497,6 @@ get_pg_setting(PGconn *conn, const char *setting, char *output)
}
/*
* get_conninfo_value()
*
* Extract the value represented by 'keyword' in 'conninfo' and copy
* it to the 'output' buffer.
*
* Returns true on success, or false on failure (conninfo string could
* not be parsed, or provided keyword not found).
*/
bool
get_conninfo_value(const char *conninfo, const char *keyword, char *output)
{
PQconninfoOption *conninfo_options;
PQconninfoOption *conninfo_option;
conninfo_options = PQconninfoParse(conninfo, NULL);
if (conninfo_options == NULL)
{
log_err(_("Unable to parse provided conninfo string \"%s\""), conninfo);
return false;
}
for (conninfo_option = conninfo_options; conninfo_option->keyword != NULL; conninfo_option++)
{
if (strcmp(conninfo_option->keyword, keyword) == 0)
{
if (conninfo_option->val != NULL && conninfo_option->val[0] != '\0')
{
strncpy(output, conninfo_option->val, MAXLEN);
break;
}
}
}
PQconninfoFree(conninfo_options);
return true;
}
/*
* get_upstream_connection()
*
@@ -588,7 +522,7 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
upstream_conninfo = upstream_conninfo_out;
sqlquery_snprintf(sqlquery,
" SELECT un.conninfo, un.id "
" SELECT un.conninfo, un.name, un.id "
" FROM %s.repl_nodes un "
"INNER JOIN %s.repl_nodes n "
" ON (un.id = n.upstream_node_id AND un.cluster = n.cluster)"
@@ -605,7 +539,7 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("error when attempting to find upstream server\n%s\n"),
log_err(_("unable to get conninfo for upstream server\n%s\n"),
PQerrorMessage(standby_conn));
PQclear(res);
return NULL;
@@ -613,36 +547,9 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
if (!PQntuples(res))
{
log_notice(_("no record found for upstream server"));
PQclear(res);
log_debug("no record found for upstream server\n");
sqlquery_snprintf(sqlquery,
" SELECT un.conninfo, un.id "
" FROM %s.repl_nodes un "
" WHERE un.cluster = '%s' "
" AND un.type='master' "
" AND un.active IS TRUE",
get_repmgr_schema_quoted(standby_conn),
cluster);
res = PQexec(standby_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("error when attempting to find active master server\n%s\n"),
PQerrorMessage(standby_conn));
PQclear(res);
return NULL;
}
if (!PQntuples(res))
{
PQclear(res);
log_notice(_("no record found for active master server\n"));
return NULL;
}
log_debug("record found for active master server\n");
return NULL;
}
strncpy(upstream_conninfo, PQgetvalue(res, 0, 0), MAXCONNINFO);
@@ -691,13 +598,6 @@ get_master_connection(PGconn *standby_conn, char *cluster,
int i,
node_id;
/*
* If the caller wanted to get a copy of the connection info string, sub
* out the local stack pointer for the pointer passed by the caller.
*/
if (master_conninfo_out != NULL)
remote_conninfo = master_conninfo_out;
if (master_id != NULL)
{
*master_id = NODE_NOT_FOUND;
@@ -917,12 +817,10 @@ get_repmgr_schema_quoted(PGconn *conn)
bool
create_replication_slot(PGconn *conn, char *slot_name, int server_version_num)
create_replication_slot(PGconn *conn, char *slot_name)
{
char sqlquery[QUERY_STR_LEN];
int query_res;
PGresult *res;
t_replication_slot slot_info;
char sqlquery[QUERY_STR_LEN];
PGresult *res;
/*
* Check whether slot exists already; if it exists and is active, that
@@ -930,43 +828,48 @@ create_replication_slot(PGconn *conn, char *slot_name, int server_version_num)
* if not we can reuse it as-is
*/
query_res = get_slot_record(conn, slot_name, &slot_info);
sqlquery_snprintf(sqlquery,
"SELECT active, slot_type "
" FROM pg_replication_slots "
" WHERE slot_name = '%s' ",
slot_name);
if (query_res)
log_verbose(LOG_DEBUG, "create_replication_slot():\n%s\n", sqlquery);
res = PQexec(conn, sqlquery);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
{
if (strcmp(slot_info.slot_type, "physical") != 0)
log_err(_("unable to query pg_replication_slots: %s\n"),
PQerrorMessage(conn));
PQclear(res);
return false;
}
if (PQntuples(res))
{
if (strcmp(PQgetvalue(res, 0, 1), "physical") != 0)
{
log_err(_("Slot '%s' exists and is not a physical slot\n"),
slot_name);
return false;
PQclear(res);
}
if (slot_info.active == false)
if (strcmp(PQgetvalue(res, 0, 0), "f") == 0)
{
PQclear(res);
log_debug("Replication slot '%s' exists but is inactive; reusing\n",
slot_name);
return true;
}
PQclear(res);
log_err(_("Slot '%s' already exists as an active slot\n"),
slot_name);
return false;
}
/* In 9.6 and later, reserve the LSN straight away */
if (server_version_num >= 90600)
{
sqlquery_snprintf(sqlquery,
"SELECT * FROM pg_create_physical_replication_slot('%s', TRUE)",
slot_name);
}
else
{
sqlquery_snprintf(sqlquery,
"SELECT * FROM pg_create_physical_replication_slot('%s')",
slot_name);
}
sqlquery_snprintf(sqlquery,
"SELECT * FROM pg_create_physical_replication_slot('%s')",
slot_name);
log_debug(_("create_replication_slot(): Creating slot '%s' on primary\n"), slot_name);
log_verbose(LOG_DEBUG, "create_replication_slot():\n%s\n", sqlquery);
@@ -985,46 +888,6 @@ create_replication_slot(PGconn *conn, char *slot_name, int server_version_num)
return true;
}
int
get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record)
{
char sqlquery[QUERY_STR_LEN];
PGresult *res;
sqlquery_snprintf(sqlquery,
"SELECT slot_name, slot_type, active "
" FROM pg_replication_slots "
" WHERE slot_name = '%s' ",
slot_name);
log_verbose(LOG_DEBUG, "get_slot_record():\n%s\n", sqlquery);
res = PQexec(conn, sqlquery);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("unable to query pg_replication_slots: %s\n"),
PQerrorMessage(conn));
PQclear(res);
return -1;
}
if (!PQntuples(res))
{
return 0;
}
strncpy(record->slot_name, PQgetvalue(res, 0, 0), MAXLEN);
strncpy(record->slot_type, PQgetvalue(res, 0, 1), MAXLEN);
record->active = (strcmp(PQgetvalue(res, 0, 2), "t") == 0)
? true
: false;
PQclear(res);
return 1;
}
bool
drop_replication_slot(PGconn *conn, char *slot_name)
{
@@ -1149,7 +1012,7 @@ set_config_bool(PGconn *conn, const char *config_param, bool state)
/*
* witness_copy_node_records()
* copy_configuration()
*
* Copy records in master's `repl_nodes` table to witness database
*
@@ -1157,49 +1020,29 @@ set_config_bool(PGconn *conn, const char *config_param, bool state)
* `repmgrd` after a failover event occurs
*/
bool
witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
copy_configuration(PGconn *masterconn, PGconn *witnessconn, char *cluster_name)
{
char sqlquery[MAXLEN];
PGresult *res;
int i;
begin_transaction(witnessconn);
/* Defer constraints */
sqlquery_snprintf(sqlquery, "SET CONSTRAINTS ALL DEFERRED;");
log_verbose(LOG_DEBUG, "witness_copy_node_records():\n%s\n", sqlquery);
res = PQexec(witnessconn, sqlquery);
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{
log_err(_("Unable to defer constraints:\n%s\n"),
PQerrorMessage(witnessconn));
rollback_transaction(witnessconn);
return false;
}
/* Truncate existing records */
sqlquery_snprintf(sqlquery, "TRUNCATE TABLE %s.repl_nodes", get_repmgr_schema_quoted(witnessconn));
log_verbose(LOG_DEBUG, "witness_copy_node_records():\n%s\n", sqlquery);
log_verbose(LOG_DEBUG, "copy_configuration():\n%s\n", sqlquery);
res = PQexec(witnessconn, sqlquery);
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{
log_err(_("Unable to truncate witness servers's repl_nodes table:\n%s\n"),
PQerrorMessage(witnessconn));
rollback_transaction(witnessconn);
return false;
}
/* Get current records from primary */
sqlquery_snprintf(sqlquery,
"SELECT id, type, upstream_node_id, name, conninfo, priority, slot_name, active FROM %s.repl_nodes",
"SELECT id, type, upstream_node_id, name, conninfo, priority, slot_name FROM %s.repl_nodes",
get_repmgr_schema_quoted(masterconn));
log_verbose(LOG_DEBUG, "witness_copy_node_records():\n%s\n", sqlquery);
log_verbose(LOG_DEBUG, "copy_configuration():\n%s\n", sqlquery);
res = PQexec(masterconn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -1207,23 +1050,20 @@ witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster
log_err("Unable to retrieve node records from master:\n%s\n",
PQerrorMessage(masterconn));
PQclear(res);
rollback_transaction(witnessconn);
return false;
}
/* Insert primary records into witness table */
for (i = 0; i < PQntuples(res); i++)
{
bool node_record_created;
log_verbose(LOG_DEBUG,
"witness_copy_node_records(): writing node record for node %s (id: %s)\n",
PQgetvalue(res, i, 3),
"copy_configuration(): writing node record for node %s (id: %s)\n",
PQgetvalue(res, i, 4),
PQgetvalue(res, i, 0));
node_record_created = create_node_record(witnessconn,
"witness_copy_node_records",
"copy_configuration",
atoi(PQgetvalue(res, i, 0)),
PQgetvalue(res, i, 1),
strlen(PQgetvalue(res, i, 2))
@@ -1235,10 +1075,7 @@ witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster
atoi(PQgetvalue(res, i, 5)),
strlen(PQgetvalue(res, i, 6))
? PQgetvalue(res, i, 6)
: NULL,
(strcmp(PQgetvalue(res, i, 7), "t") == 0)
? true
: false
: NULL
);
if (node_record_created == false)
@@ -1247,16 +1084,11 @@ witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster
log_err("Unable to copy node record to witness database\n%s\n",
PQerrorMessage(witnessconn));
rollback_transaction(witnessconn);
return false;
}
}
PQclear(res);
/* And finished */
commit_transaction(witnessconn);
return true;
}
@@ -1269,7 +1101,7 @@ witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster
* XXX we should pass the record parameters as a struct.
*/
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 active)
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)
{
char sqlquery[QUERY_STR_LEN];
char upstream_node_id[MAXLEN];
@@ -1310,8 +1142,8 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_nodes "
" (id, type, upstream_node_id, cluster, "
" name, conninfo, slot_name, priority, active) "
"VALUES (%i, '%s', %s, '%s', '%s', '%s', %s, %i, %s) ",
" name, conninfo, slot_name, priority) "
"VALUES (%i, '%s', %s, '%s', '%s', '%s', %s, %i) ",
get_repmgr_schema_quoted(conn),
node,
type,
@@ -1320,8 +1152,7 @@ create_node_record(PGconn *conn, char *action, int node, char *type, int upstrea
node_name,
conninfo,
slot_name_buf,
priority,
active == true ? "TRUE" : "FALSE");
priority);
log_verbose(LOG_DEBUG, "create_node_record(): %s\n", sqlquery);
@@ -1361,7 +1192,7 @@ delete_node_record(PGconn *conn, int node, char *action)
if (action != NULL)
{
log_verbose(LOG_DEBUG, "delete_node_record(): action is \"%s\"\n", action);
log_verbose(LOG_DEBUG, "create_node_record(): action is \"%s\"\n", action);
}
res = PQexec(conn, sqlquery);
@@ -1596,7 +1427,6 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
return success;
}
/*
* Update node record following change of status
* (e.g. inactive primary converted to standby)
@@ -1605,7 +1435,7 @@ bool
update_node_record_status(PGconn *conn, char *cluster_name, int this_node_id, char *type, int upstream_node_id, bool active)
{
PGresult *res;
char sqlquery[QUERY_STR_LEN];
char sqlquery[QUERY_STR_LEN];
sqlquery_snprintf(sqlquery,
" UPDATE %s.repl_nodes "
@@ -1678,181 +1508,21 @@ update_node_record_set_upstream(PGconn *conn, char *cluster_name, int this_node_
}
int
get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info)
PGresult *
get_node_record(PGconn *conn, char *cluster, int node_id)
{
char sqlquery[QUERY_STR_LEN];
int result;
sqlquery_snprintf(
sqlquery,
"SELECT id, type, upstream_node_id, name, conninfo, slot_name, priority, active"
" FROM %s.repl_nodes "
" WHERE cluster = '%s' "
" AND id = %i",
get_repmgr_schema_quoted(conn),
cluster,
node_id);
sprintf(sqlquery,
"SELECT id, upstream_node_id, conninfo, type, slot_name, active "
" FROM %s.repl_nodes "
" WHERE cluster = '%s' "
" AND id = %i",
get_repmgr_schema_quoted(conn),
cluster,
node_id);
log_verbose(LOG_DEBUG, "get_node_record():\n%s\n", sqlquery);
result = _get_node_record(conn, cluster, sqlquery, node_info);
if (result == 0)
{
log_verbose(LOG_DEBUG, "get_node_record(): no record found for node %i\n", node_id);
}
return result;
}
int
get_node_record_by_name(PGconn *conn, char *cluster, const char *node_name, t_node_info *node_info)
{
char sqlquery[QUERY_STR_LEN];
int result;
sqlquery_snprintf(
sqlquery,
"SELECT id, type, upstream_node_id, name, conninfo, slot_name, priority, active"
" FROM %s.repl_nodes "
" WHERE cluster = '%s' "
" AND name = '%s'",
get_repmgr_schema_quoted(conn),
cluster,
node_name);
log_verbose(LOG_DEBUG, "get_node_record_by_name():\n%s\n", sqlquery);
result = _get_node_record(conn, cluster, sqlquery, node_info);
if (result == 0)
{
log_verbose(LOG_DEBUG, "get_node_record(): no record found for node %s\n", node_name);
}
return result;
}
static int
_get_node_record(PGconn *conn, char *cluster, char *sqlquery, t_node_info *node_info)
{
int ntuples;
PGresult *res;
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
return -1;
}
ntuples = PQntuples(res);
if (ntuples == 0)
{
return 0;
}
node_info->node_id = atoi(PQgetvalue(res, 0, 0));
node_info->type = parse_node_type(PQgetvalue(res, 0, 1));
node_info->upstream_node_id = atoi(PQgetvalue(res, 0, 2));
strncpy(node_info->name, PQgetvalue(res, 0, 3), MAXLEN);
strncpy(node_info->conninfo_str, PQgetvalue(res, 0, 4), MAXLEN);
strncpy(node_info->slot_name, PQgetvalue(res, 0, 5), MAXLEN);
node_info->priority = atoi(PQgetvalue(res, 0, 6));
node_info->active = (strcmp(PQgetvalue(res, 0, 7), "t") == 0)
? true
: false;
PQclear(res);
return ntuples;
}
int
get_node_replication_state(PGconn *conn, char *node_name, char *output)
{
char sqlquery[QUERY_STR_LEN];
PGresult * res;
sqlquery_snprintf(
sqlquery,
" SELECT state "
" FROM pg_catalog.pg_stat_replication"
" WHERE application_name = '%s'",
node_name
);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
PQclear(res);
return -1;
}
if (PQntuples(res) == 0)
{
PQclear(res);
return 0;
}
strncpy(output, PQgetvalue(res, 0, 0), MAXLEN);
PQclear(res);
return true;
}
t_server_type
parse_node_type(const char *type)
{
if (strcmp(type, "master") == 0)
{
return MASTER;
}
else if (strcmp(type, "standby") == 0)
{
return STANDBY;
}
else if (strcmp(type, "witness") == 0)
{
return WITNESS;
}
return UNKNOWN;
}
int
get_data_checksum_version(const char *data_directory)
{
ControlFileData control_file;
int fd;
char control_file_path[MAXPGPATH];
snprintf(control_file_path, MAXPGPATH, "%s/global/pg_control", data_directory);
if ((fd = open(control_file_path, O_RDONLY | PG_BINARY, 0)) == -1)
{
log_err(_("Unable to open control file \"%s\" for reading: %s\n"),
control_file_path, strerror(errno));
return -1;
}
if (read(fd, &control_file, sizeof(ControlFileData)) != sizeof(ControlFileData))
{
log_err(_("could not read file \"%s\": %s\n"),
control_file_path, strerror(errno));
close(fd);
return -1;
}
close(fd);
return (int)control_file.data_checksum_version;
return PQexec(conn, sqlquery);
}

View File

@@ -1,6 +1,6 @@
/*
* dbutils.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2015
*
* 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
@@ -66,26 +66,8 @@ typedef struct s_node_info
InvalidXLogRecPtr \
}
/*
* Struct to store replication slot information
*/
typedef struct s_replication_slot
{
char slot_name[MAXLEN];
char slot_type[MAXLEN];
bool active;
} t_replication_slot;
PGconn *_establish_db_connection(const char *conninfo,
const bool exit_on_error,
const bool log_notice);
PGconn *establish_db_connection(const char *conninfo,
const bool exit_on_error);
PGconn *test_db_connection(const char *conninfo,
const bool exit_on_error);
const bool exit_on_error);
PGconn *establish_db_connection_by_params(const char *keywords[],
const char *values[],
const bool exit_on_error);
@@ -104,7 +86,7 @@ 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);
bool get_conninfo_value(const char *conninfo, const char *keyword, char *output);
PGconn *get_upstream_connection(PGconn *standby_conn, char *cluster,
int node_id,
int *upstream_node_id_ptr,
@@ -116,23 +98,18 @@ 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, int server_version_num);
int get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
bool create_replication_slot(PGconn *conn, char *slot_name);
bool drop_replication_slot(PGconn *conn, char *slot_name);
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint);
bool stop_backup(PGconn *conn, char *last_wal_segment);
bool set_config_bool(PGconn *conn, const char *config_param, bool state);
bool witness_copy_node_records(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 active);
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);
int get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info);
int get_node_record_by_name(PGconn *conn, char *cluster, const char *node_name, t_node_info *node_info);
bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);
bool update_node_record_status(PGconn *conn, char *cluster_name, int this_node_id, char *type, int upstream_node_id, bool active);
bool update_node_record_set_upstream(PGconn *conn, char *cluster_name, int this_node_id, int new_upstream_node_id);
bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);
PGresult * get_node_record(PGconn *conn, char *cluster, int node_id);
int get_node_replication_state(PGconn *conn, char *node_name, char *output);
t_server_type parse_node_type(const char *type);
int get_data_checksum_version(const char *data_directory);
#endif

View File

@@ -1,9 +1,9 @@
Package: repmgr-auto
Version: 3.1.3
Version: 2.0beta2
Section: database
Priority: optional
Architecture: all
Depends: rsync, postgresql-9.3 | postgresql-9.4 | postgresql-9.5
Maintainer: Self built package <user@localhost>
Depends: rsync, postgresql-9.0 | postgresql-9.1 | postgresql-9.2 | postgresql-9.3 | postgresql-9.4
Maintainer: Jaime Casanova <jaime@2ndQuadrant.com>
Description: PostgreSQL replication setup, magament and monitoring
has two main executables

194
dirmod.c
View File

@@ -1,194 +0,0 @@
/*
*
* dirmod.c
* directory handling functions
*
* Copyright (C) 2ndQuadrant, 2010-2016
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "postgres_fe.h"
/* Don't modify declarations in system headers */
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
/*
* pgfnames
*
* return a list of the names of objects in the argument directory. Caller
* must call pgfnames_cleanup later to free the memory allocated by this
* function.
*/
char **
pgfnames(const char *path)
{
DIR *dir;
struct dirent *file;
char **filenames;
int numnames = 0;
int fnsize = 200; /* enough for many small dbs */
dir = opendir(path);
if (dir == NULL)
{
return NULL;
}
filenames = (char **) palloc(fnsize * sizeof(char *));
while (errno = 0, (file = readdir(dir)) != NULL)
{
if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
{
if (numnames + 1 >= fnsize)
{
fnsize *= 2;
filenames = (char **) repalloc(filenames,
fnsize * sizeof(char *));
}
filenames[numnames++] = pstrdup(file->d_name);
}
}
if (errno)
{
fprintf(stderr, _("could not read directory \"%s\": %s\n"),
path, strerror(errno));
}
filenames[numnames] = NULL;
if (closedir(dir))
{
fprintf(stderr, _("could not close directory \"%s\": %s\n"),
path, strerror(errno));
}
return filenames;
}
/*
* pgfnames_cleanup
*
* deallocate memory used for filenames
*/
void
pgfnames_cleanup(char **filenames)
{
char **fn;
for (fn = filenames; *fn; fn++)
pfree(*fn);
pfree(filenames);
}
/*
* rmtree
*
* Delete a directory tree recursively.
* Assumes path points to a valid directory.
* Deletes everything under path.
* If rmtopdir is true deletes the directory too.
* Returns true if successful, false if there was any problem.
* (The details of the problem are reported already, so caller
* doesn't really have to say anything more, but most do.)
*/
bool
rmtree(const char *path, bool rmtopdir)
{
bool result = true;
char pathbuf[MAXPGPATH];
char **filenames;
char **filename;
struct stat statbuf;
/*
* we copy all the names out of the directory before we start modifying
* it.
*/
filenames = pgfnames(path);
if (filenames == NULL)
return false;
/* now we have the names we can start removing things */
for (filename = filenames; *filename; filename++)
{
snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename);
/*
* It's ok if the file is not there anymore; we were just about to
* delete it anyway.
*
* This is not an academic possibility. One scenario where this
* happens is when bgwriter has a pending unlink request for a file in
* a database that's being dropped. In dropdb(), we call
* ForgetDatabaseFsyncRequests() to flush out any such pending unlink
* requests, but because that's asynchronous, it's not guaranteed that
* the bgwriter receives the message in time.
*/
if (lstat(pathbuf, &statbuf) != 0)
{
if (errno != ENOENT)
{
result = false;
}
continue;
}
if (S_ISDIR(statbuf.st_mode))
{
/* call ourselves recursively for a directory */
if (!rmtree(pathbuf, true))
{
/* we already reported the error */
result = false;
}
}
else
{
if (unlink(pathbuf) != 0)
{
if (errno != ENOENT)
{
result = false;
}
}
}
}
if (rmtopdir)
{
if (rmdir(path) != 0)
{
result = false;
}
}
pgfnames_cleanup(filenames);
return result;
}

View File

@@ -1,23 +0,0 @@
/*
* dirmod.h
* Copyright (c) 2ndQuadrant, 2010-2016
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _DIRMOD_H_
#define _DIRMOD_H_
#endif

View File

@@ -1,6 +1,6 @@
/*
* errcode.h
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* 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
@@ -29,6 +29,7 @@
#define ERR_DB_CON 6
#define ERR_DB_QUERY 7
#define ERR_PROMOTED 8
#define ERR_BAD_PASSWORD 9
#define ERR_STR_OVERFLOW 10
#define ERR_FAILOVER_FAIL 11
#define ERR_BAD_SSH 12
@@ -36,7 +37,5 @@
#define ERR_BAD_BASEBACKUP 14
#define ERR_INTERNAL 15
#define ERR_MONITORING_FAIL 16
#define ERR_BAD_BACKUP_LABEL 17
#define ERR_SWITCHOVER_FAIL 18
#endif /* _ERRCODE_H_ */

7
log.c
View File

@@ -1,6 +1,6 @@
/*
* log.c - Logging methods
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* This module is a set of methods for logging (currently only syslog)
*
@@ -40,8 +40,7 @@
/* #define REPMGR_DEBUG */
static int detect_log_facility(const char *facility);
static void _stderr_log_with_level(const char *level_name, int level, const char *fmt, va_list ap)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 0)));
static void _stderr_log_with_level(const char *level_name, int level, const char *fmt, va_list ap);
int log_type = REPMGR_STDERR;
int log_level = LOG_NOTICE;
@@ -49,7 +48,7 @@ int last_log_level = LOG_NOTICE;
int verbose_logging = false;
int terse_logging = false;
extern void
void
stderr_log_with_level(const char *level_name, int level, const char *fmt, ...)
{
va_list arglist;

10
log.h
View File

@@ -1,6 +1,6 @@
/*
* log.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2015
*
* 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
@@ -25,7 +25,7 @@
#define REPMGR_SYSLOG 1
#define REPMGR_STDERR 2
extern void
void
stderr_log_with_level(const char *level_name, int level, const char *fmt,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
@@ -123,10 +123,8 @@ bool logger_shutdown(void);
void logger_set_verbose(void);
void logger_set_terse(void);
void log_hint(const char *fmt, ...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
void log_verbose(int level, const char *fmt, ...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
void log_hint(const char *fmt, ...);
void log_verbose(int level, const char *fmt, ...);
extern int log_type;
extern int log_level;

2813
repmgr.c

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,6 @@
# Replication Manager sample configuration file
###################################################
# Some configuration items will be set with a default value; this
# is noted for each item. Where no default value is shown, the
# parameter will be treated as empty or false.
# Required configuration items
# ============================
#
@@ -15,29 +11,21 @@
# schema (pattern: "repmgr_{cluster}"); while this name will be quoted
# to preserve case, we recommend using lower case and avoiding whitespace
# to facilitate easier querying of the repmgr views and tables.
#cluster=example_cluster
cluster=example_cluster
# Node ID and name
# (Note: we recommend to avoid naming nodes after their initial
# replication function, as this will cause confusion when e.g.
# replication funcion, as this will cause confusion when e.g.
# "standby2" is promoted to primary)
#node=2 # a unique integer
#node_name=node2 # an arbitrary (but unique) string; we recommend using
node=2 # a unique integer
node_name=node2 # an arbitrary (but unique) string; we recommend using
# the server's hostname or another identifier unambiguously
# associated with the server to avoid confusion
# Database connection information as a conninfo string
# This must be accessible to all servers in the cluster; for details see:
#
# https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
#
#conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
#
# If repmgrd is in use, consider explicitly setting `connect_timeout` in the
# conninfo string to determine the length of time which elapses before
# a network connection attempt is abandoned; for details see:
#
# https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNECT-CONNECT-TIMEOUT
# http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
# Optional configuration items
# ============================
@@ -45,17 +33,18 @@
# Replication settings
# ---------------------
# When using cascading replication, a standby can connect to another
# upstream standby node which is specified by setting 'upstream_node'.
# In that case, the upstream node must exist before the new standby
# can be registered. If 'upstream_node' is not set, then the standby
# will connect directly to the primary node.
#upstream_node=1
# when using cascading replication and a standby is to be connected to an
# upstream standby, specify that node's ID with 'upstream_node'. The node
# must exist before the new standby can be registered. If a standby is
# to connect directly to a primary node, this parameter is not required.
#
# upstream_node=1
# use physical replication slots - PostgreSQL 9.4 and later only
# physical replication slots - PostgreSQL 9.4 and later only
# (default: 0)
#use_replication_slots=0
#
# use_replication_slots=0
#
# NOTE: 'max_replication_slots' should be configured for at least the
# number of standbys which will connect to the primary.
@@ -64,15 +53,15 @@
# Log level: possible values are DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG
# (default: NOTICE)
#loglevel=NOTICE
loglevel=NOTICE
# Logging facility: possible values are STDERR or - for Syslog integration - one of LOCAL0, LOCAL1, ..., LOCAL7, USER
# (default: STDERR)
#logfacility=STDERR
logfacility=STDERR
# stderr can be redirected to an arbitrary file:
#
#logfile='/var/log/repmgr/repmgr.log'
# logfile='/var/log/repmgr.log'
# event notifications can be passed to an arbitrary external program
# together with the following parameters:
@@ -86,12 +75,12 @@
# 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"'
# 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
# event_notifications=master_register,standby_register,witness_create
# Environment/command settings
@@ -99,17 +88,17 @@
# path to PostgreSQL binary directory (location of pg_ctl, pg_basebackup etc.)
# (if not provided, defaults to system $PATH)
#pg_bindir=/usr/bin/
# pg_bindir=/usr/bin/
# external command options
#rsync_options=--archive --checksum --compress --progress --rsh="ssh -o \"StrictHostKeyChecking no\""
#ssh_options=-o "StrictHostKeyChecking no"
# rsync_options=--archive --checksum --compress --progress --rsh="ssh -o \"StrictHostKeyChecking no\""
# ssh_options=-o "StrictHostKeyChecking no"
# external command arguments. Values shown are examples.
# external command arguments
#pg_ctl_options='-s'
#pg_basebackup_options='--xlog-method=s'
# pg_ctl_options='-s'
# pg_basebackup_options='--xlog-method=s'
# Standby clone settings
@@ -121,44 +110,35 @@
#
# tablespace_mapping=/path/to/original/tablespace=/path/to/new/tablespace
# You can specify a restore_command to be used in the recovery.conf that
# will be placed in the cloned standby
#
# restore_command = cp /path/to/archived/wals/%f %p
# Failover settings (repmgrd)
# ---------------------------
#
# These settings are only applied when repmgrd is running. Values shown
# are defaults.
# These settings are only applied when repmgrd is running.
# Number of seconds to wait for a response from the primary server before
# deciding it has failed.
# deciding it has failed
#master_response_timeout=60
master_response_timeout=60
# Number of attempts at what interval (in seconds) to try and
# connect to a server to establish its status (e.g. master
# during failover)
#reconnect_attempts=6
#reconnect_interval=10
# Number of times to try and reconnect to the primary before starting
# the failover procedure
reconnect_attempts=6
reconnect_interval=10
# Autofailover options
#failover=manual # one of 'automatic', 'manual'
# (default: manual)
#priority=100 # a value of zero or less prevents the node being promoted to primary
# (default: 100)
#promote_command='repmgr standby promote -f /path/to/repmgr.conf'
#follow_command='repmgr standby follow -f /path/to/repmgr.conf -W'
failover=automatic # one of 'automatic', 'manual'
priority=100 # a value of zero or less prevents the node being promoted to primary
promote_command='repmgr standby promote -f /path/to/repmgr.conf'
follow_command='repmgr standby follow -f /path/to/repmgr.conf -W'
# monitoring interval in seconds; default is 2
#monitor_interval_secs=2
#
# monitor_interval_secs=2
# change wait time for primary; before we bail out and exit when the primary
# disappears, we wait 'reconnect_attempts' * 'retry_promote_interval_secs'
# seconds; by default this would be half an hour, as 'retry_promote_interval_secs'
# default value is 300)
#retry_promote_interval_secs=300
# Number of seconds after which the witness server resyncs the repl_nodes table
#witness_repl_nodes_sync_interval_secs=15
#
# retry_promote_interval_secs=300

View File

@@ -1,6 +1,6 @@
/*
* repmgr.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2015
*
* 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
@@ -28,15 +28,18 @@
#include "dbutils.h"
#include "errcode.h"
#include "config.h"
#include "dirmod.h"
#define MIN_SUPPORTED_VERSION "9.3"
#define MIN_SUPPORTED_VERSION_NUM 90300
#include "config.h"
#define MAXFILENAME 1024
#define ERRBUFF_SIZE 512
#define DEFAULT_WAL_KEEP_SEGMENTS "5000"
#define DEFAULT_DEST_DIR "."
#define DEFAULT_MASTER_PORT "5432"
#define DEFAULT_DBNAME "postgres"
#define DEFAULT_REPMGR_SCHEMA_PREFIX "repmgr_"
#define DEFAULT_PRIORITY 100
#define FAILOVER_NODES_MAX_CHECK 50
@@ -56,8 +59,8 @@ typedef struct
char dbname[MAXLEN];
char host[MAXLEN];
char username[MAXLEN];
char dest_dir[MAXPGPATH];
char config_file[MAXPGPATH];
char dest_dir[MAXFILENAME];
char config_file[MAXFILENAME];
char remote_user[MAXLEN];
char superuser[MAXLEN];
char wal_keep_segments[MAXLEN];
@@ -66,51 +69,25 @@ typedef struct
bool force;
bool wait_for_master;
bool ignore_rsync_warn;
bool witness_pwprompt;
bool initdb_no_pwprompt;
bool rsync_only;
bool fast_checkpoint;
bool ignore_external_config_files;
bool csv_mode;
char masterport[MAXLEN];
/*
* configuration file parameters which can be overridden on the
* command line
*/
char localport[MAXLEN];
char loglevel[MAXLEN];
/* parameter used by STANDBY SWITCHOVER */
char remote_config_file[MAXLEN];
char pg_rewind[MAXPGPATH];
char pg_ctl_mode[MAXLEN];
/* parameter used by STANDBY {ARCHIVE_CONFIG | RESTORE_CONFIG} */
char config_archive_dir[MAXLEN];
/* parameter used by CLUSTER CLEANUP */
int keep_history;
char pg_bindir[MAXLEN];
char recovery_min_apply_delay[MAXLEN];
/* deprecated command line options */
char localport[MAXLEN];
bool initdb_no_pwprompt;
} t_runtime_options;
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, false, "", "", "", "", "fast", "", 0, "", "", "", false }
struct BackupLabel
{
XLogRecPtr start_wal_location;
char start_wal_file[MAXLEN];
XLogRecPtr checkpoint_location;
char backup_from[MAXLEN];
char backup_method[MAXLEN];
char start_time[MAXLEN];
char label[MAXLEN];
XLogRecPtr min_failover_slot_lsn;
};
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, "", "", "", 0, "", "" }
extern char repmgr_schema[MAXLEN];
extern bool config_file_found;
#endif

View File

@@ -1,7 +1,7 @@
/*
* repmgr.sql
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
*/
@@ -59,12 +59,3 @@ WHERE (standby_node, last_monitor_time) IN (SELECT standby_node, MAX(last_monito
ALTER VIEW repl_status OWNER TO repmgr;
CREATE INDEX idx_repl_status_sort ON repl_monitor(last_monitor_time, standby_node);
/*
* This view shows the list of nodes with the information of which one is the upstream
* in each case (when appliable)
*/
CREATE VIEW repl_show_nodes AS
SELECT rn.id, rn.conninfo, rn.type, rn.name, rn.cluster,
rn.priority, rn.active, sq.name AS upstream_node_name
FROM repl_nodes as rn LEFT JOIN repl_nodes AS sq ON sq.id=rn.upstream_node_id;

440
repmgrd.c
View File

@@ -1,6 +1,6 @@
/*
* repmgrd.c - Replication manager daemon
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* This module connects to the nodes of a replication cluster and monitors
* how far are they from master
@@ -44,11 +44,11 @@
/* Local info */
t_configuration_options local_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
t_configuration_options local_options;
PGconn *my_local_conn = NULL;
/* Master info */
t_configuration_options master_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
t_configuration_options master_options;
PGconn *master_conn = NULL;
@@ -61,6 +61,8 @@ bool failover_done = false;
char *pid_file = NULL;
t_configuration_options config = T_CONFIGURATION_OPTIONS_INITIALIZER;
static void help(void);
static void usage(void);
static void check_cluster_configuration(PGconn *conn);
@@ -77,6 +79,7 @@ static void do_master_failover(void);
static bool do_upstream_standby_failover(t_node_info upstream_node);
static t_node_info get_node_info(PGconn *conn, char *cluster, int node_id);
static t_server_type parse_node_type(const char *type);
static XLogRecPtr lsn_to_xlogrecptr(char *lsn, bool *format_ok);
/*
@@ -140,20 +143,6 @@ main(int argc, char **argv)
set_progname(argv[0]);
/* Disallow running as root to prevent directory ownership problems */
if (geteuid() == 0)
{
fprintf(stderr,
_("%s: cannot be run as root\n"
"Please log in (using, e.g., \"su\") as the "
"(unprivileged) user that owns "
"the data directory.\n"
),
progname());
exit(1);
}
while ((c = getopt_long(argc, argv, "?Vf:vmdp:", long_options, &optindex)) != -1)
{
switch (c)
@@ -272,14 +261,7 @@ main(int argc, char **argv)
/* Retrieve record for this node from the local database */
node_info = get_node_info(my_local_conn, local_options.cluster_name, local_options.node);
/*
* No node record found - exit gracefully
*
* Note: it's highly unlikely this situation will occur when starting
* repmgrd on a witness, unless someone goes to the trouble of
* deleting the node record from the previously copied table.
*/
/* No node record found - exit gracefully */
if (node_info.node_id == NODE_NOT_FOUND)
{
log_err(_("No metadata record found for this node - terminating\n"));
@@ -296,12 +278,9 @@ main(int argc, char **argv)
*/
do
{
/* Timer for repl_nodes synchronisation interval */
int sync_repl_nodes_elapsed = 0;
/*
* Set my server mode, establish a connection to master and start
* monitoring
* monitor
*/
switch (node_info.type)
@@ -397,12 +376,12 @@ main(int argc, char **argv)
case STANDBY:
/* We need the node id of the master server as well as a connection to it */
log_info(_("connecting to master node of cluster '%s'\n"),
log_info(_("connecting to master node '%s'\n"),
local_options.cluster_name);
master_conn = get_master_connection(my_local_conn,
local_options.cluster_name,
&master_options.node, NULL);
local_options.cluster_name,
&master_options.node, NULL);
if (master_conn == NULL)
{
@@ -410,7 +389,8 @@ main(int argc, char **argv)
initPQExpBuffer(&errmsg);
appendPQExpBuffer(&errmsg,
_("unable to connect to master node"));
_("unable to connect to master node '%s'"),
master_options.node_name);
log_err("%s\n", errmsg.data);
@@ -460,37 +440,19 @@ main(int argc, char **argv)
do
{
if (node_info.type == STANDBY)
log_verbose(LOG_DEBUG, "standby check loop...\n");
if (node_info.type == WITNESS)
{
log_verbose(LOG_DEBUG, "standby check loop...\n");
standby_monitor();
}
else if (node_info.type == WITNESS)
{
log_verbose(LOG_DEBUG, "witness check loop...\n");
witness_monitor();
}
else if (node_info.type == STANDBY)
{
standby_monitor();
}
sleep(local_options.monitor_interval_secs);
/*
* On a witness node, regularly resync the repl_nodes table
* to keep up with any changes on the primary
*
* TODO: only resync the table if changes actually detected
*/
if (node_info.type == WITNESS)
{
sync_repl_nodes_elapsed += local_options.monitor_interval_secs;
log_debug(_("seconds since last node record sync: %i (sync interval: %i)\n"), sync_repl_nodes_elapsed, local_options.witness_repl_nodes_sync_interval_secs);
if(sync_repl_nodes_elapsed >= local_options.witness_repl_nodes_sync_interval_secs)
{
log_debug(_("Resyncing repl_nodes table\n"));
witness_copy_node_records(master_conn, my_local_conn, local_options.cluster_name);
sync_repl_nodes_elapsed = 0;
}
}
if (got_SIGHUP)
{
/*
@@ -505,7 +467,6 @@ main(int argc, char **argv)
}
got_SIGHUP = false;
}
if (failover_done)
{
log_debug(_("standby check loop will terminate\n"));
@@ -598,7 +559,7 @@ witness_monitor(void)
* XXX it would be neat to be able to handle this with e.g. table-based
* logical replication
*/
witness_copy_node_records(master_conn, my_local_conn, local_options.cluster_name);
copy_configuration(master_conn, my_local_conn, local_options.cluster_name);
break;
}
@@ -665,7 +626,7 @@ witness_monitor(void)
" replication_lag, apply_lag )"
" VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, NULL, "
" pg_catalog.pg_current_xlog_location(), NULL, "
" pg_current_xlog_location(), NULL, "
" 0, 0) ",
get_repmgr_schema_quoted(my_local_conn),
master_options.node,
@@ -693,19 +654,16 @@ standby_monitor(void)
{
PGresult *res;
char monitor_standby_timestamp[MAXLEN];
char last_wal_primary_location[MAXLEN];
char last_xlog_receive_location[MAXLEN];
char last_xlog_replay_location[MAXLEN];
char last_xact_replay_timestamp[MAXLEN];
bool last_xlog_receive_location_gte_replayed;
char last_wal_master_location[MAXLEN];
char last_wal_standby_received[MAXLEN];
char last_wal_standby_applied[MAXLEN];
char last_wal_standby_applied_timestamp[MAXLEN];
bool last_wal_standby_received_gte_replayed;
char sqlquery[QUERY_STR_LEN];
XLogRecPtr lsn_master_current_xlog_location;
XLogRecPtr lsn_last_xlog_receive_location;
XLogRecPtr lsn_last_xlog_replay_location;
long long unsigned int replication_lag;
long long unsigned int apply_lag;
XLogRecPtr lsn_master;
XLogRecPtr lsn_standby_received;
XLogRecPtr lsn_standby_applied;
int connection_retries,
ret;
@@ -717,9 +675,8 @@ standby_monitor(void)
t_node_info upstream_node;
int active_master_id;
const char *upstream_node_type = NULL;
const char *type = NULL;
bool receiving_streamed_wal = true;
/*
* Verify that the local node is still available - if not there's
* no point in doing much else anyway
@@ -744,19 +701,19 @@ standby_monitor(void)
upstream_conn = get_upstream_connection(my_local_conn,
local_options.cluster_name,
local_options.node,
&upstream_node_id,
upstream_conninfo);
&upstream_node_id, upstream_conninfo);
upstream_node_type = (upstream_node_id == master_options.node)
type = upstream_node_id == master_options.node
? "master"
: "upstream";
// ZZZ "5 minutes"?
/*
* Check that the upstream node is still available
* If not, initiate failover process
* Check if the upstream node is still available, if after 5 minutes of retries
* we cannot reconnect, try to get a new upstream node.
*/
check_connection(&upstream_conn, upstream_node_type, upstream_conninfo);
check_connection(&upstream_conn, type, upstream_conninfo);
/*
* This takes up to local_options.reconnect_attempts *
* local_options.reconnect_interval seconds
@@ -769,7 +726,7 @@ standby_monitor(void)
if (local_options.failover == MANUAL_FAILOVER)
{
log_err(_("Unable to reconnect to %s. Now checking if another node has been promoted.\n"), upstream_node_type);
log_err(_("Unable to reconnect to %s. Now checking if another node has been promoted.\n"), type);
for (connection_retries = 0; connection_retries < local_options.reconnect_attempts; connection_retries++)
{
@@ -820,24 +777,26 @@ standby_monitor(void)
else if (local_options.failover == AUTOMATIC_FAILOVER)
{
/*
* When we return from this function we will have a new master
* When we returns from this function we will have a new master
* and a new master_conn
*
*/
/*
* Failover handling is handled differently depending on whether
* the failed node is the master or a cascading standby
*/
upstream_node = get_node_info(my_local_conn, local_options.cluster_name, upstream_node_id);
upstream_node = get_node_info(my_local_conn, local_options.cluster_name, node_info.upstream_node_id);
if (upstream_node.type == MASTER)
{
log_debug(_("failure detected on master node (%i); attempting to promote a standby\n"),
node_info.upstream_node_id);
do_master_failover();
}
else
{
log_debug(_("failure detected on upstream node %i; attempting to reconnect to new upstream node\n"),
node_info.upstream_node_id);
if (upstream_node.type == MASTER)
{
log_debug(_("failure detected on master node (%i); attempting to promote a standby\n"),
node_info.upstream_node_id);
do_master_failover();
}
else
{
log_debug(_("failure detected on upstream node %i; attempting to reconnect to new upstream node\n"),
node_info.upstream_node_id);
if (!do_upstream_standby_failover(upstream_node))
{
@@ -845,20 +804,20 @@ standby_monitor(void)
initPQExpBuffer(&errmsg);
appendPQExpBuffer(&errmsg,
_("unable to reconnect to new upstream node, terminating..."));
_("unable to reconnect to new upstream node, terminating..."));
log_err("%s\n", errmsg.data);
create_event_record(master_conn,
&local_options,
local_options.node,
"repmgrd_shutdown",
false,
errmsg.data);
&local_options,
local_options.node,
"repmgrd_shutdown",
false,
errmsg.data);
terminate(ERR_DB_CON);
}
}
}
return;
}
}
@@ -929,7 +888,7 @@ standby_monitor(void)
* from the upstream node to write monitoring information
*/
upstream_node = get_node_info(my_local_conn, local_options.cluster_name, upstream_node_id);
upstream_node = get_node_info(my_local_conn, local_options.cluster_name, node_info.upstream_node_id);
sprintf(sqlquery,
"SELECT id "
@@ -961,7 +920,7 @@ standby_monitor(void)
if (active_master_id != master_options.node)
{
log_notice(_("connecting to active master (node %i)...\n"), active_master_id);
log_notice(_("connecting to active master (node %i)...\n"), active_master_id); \
if (master_conn != NULL)
{
PQfinish(master_conn);
@@ -984,11 +943,9 @@ standby_monitor(void)
/* Get local xlog info */
sqlquery_snprintf(sqlquery,
"SELECT CURRENT_TIMESTAMP, "
"pg_catalog.pg_last_xlog_receive_location(), "
"pg_catalog.pg_last_xlog_replay_location(), "
"pg_catalog.pg_last_xact_replay_timestamp(), "
"pg_catalog.pg_last_xlog_receive_location() >= pg_catalog.pg_last_xlog_replay_location()");
"SELECT CURRENT_TIMESTAMP, pg_last_xlog_receive_location(), "
"pg_last_xlog_replay_location(), pg_last_xact_replay_timestamp(), "
"pg_last_xlog_receive_location() >= pg_last_xlog_replay_location()");
res = PQexec(my_local_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -1000,50 +957,32 @@ standby_monitor(void)
}
strncpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
strncpy(last_xlog_receive_location, PQgetvalue(res, 0, 1), MAXLEN);
strncpy(last_xlog_replay_location, PQgetvalue(res, 0, 2), MAXLEN);
strncpy(last_xact_replay_timestamp, PQgetvalue(res, 0, 3), MAXLEN);
last_xlog_receive_location_gte_replayed = (strcmp(PQgetvalue(res, 0, 4), "t") == 0)
strncpy(last_wal_standby_received, PQgetvalue(res, 0, 1), MAXLEN);
strncpy(last_wal_standby_applied, PQgetvalue(res, 0, 2), MAXLEN);
strncpy(last_wal_standby_applied_timestamp, PQgetvalue(res, 0, 3), MAXLEN);
last_wal_standby_received_gte_replayed = (strcmp(PQgetvalue(res, 0, 4), "t") == 0)
? true
: false;
/*
* If pg_last_xlog_receive_location is NULL, this means we're in archive
* recovery and will need to calculate lag based on pg_last_xlog_replay_location
*/
/*
* Replayed WAL is greater than received streamed WAL
*/
if (PQgetisnull(res, 0, 1))
{
receiving_streamed_wal = false;
}
PQclear(res);
/*
* In the unusual event of a standby becoming disconnected from the primary,
* while this repmgrd remains connected to the primary, subtracting
* "last_xlog_replay_location" from "lsn_last_xlog_receive_location" and coercing to
* "lsn_standby_applied" from "lsn_standby_received" and coercing to
* (long long unsigned int) will result in a meaningless, very large
* value which will overflow a BIGINT column and spew error messages into the
* PostgreSQL log. In the absence of a better strategy, skip attempting
* to insert a monitoring record.
*/
if (receiving_streamed_wal == true && last_xlog_receive_location_gte_replayed == false)
if (last_wal_standby_received_gte_replayed == false)
{
log_verbose(LOG_WARNING,
"Replayed WAL newer than received WAL - is this standby connected to its upstream?\n");
"Invalid replication_lag value calculated - is this standby connected to its upstream?\n");
return;
}
/*
* Get master xlog position
*
* TODO: investigate whether pg_current_xlog_insert_location() would be a better
* choice; see: https://github.com/2ndQuadrant/repmgr/issues/189
*/
/* Get master xlog info */
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()");
res = PQexec(master_conn, sqlquery);
@@ -1054,73 +993,34 @@ standby_monitor(void)
return;
}
strncpy(last_wal_primary_location, PQgetvalue(res, 0, 0), MAXLEN);
strncpy(last_wal_master_location, PQgetvalue(res, 0, 0), MAXLEN);
PQclear(res);
lsn_master_current_xlog_location = lsn_to_xlogrecptr(last_wal_primary_location, NULL);
lsn_last_xlog_replay_location = lsn_to_xlogrecptr(last_xlog_replay_location, NULL);
/* Calculate apply lag */
if (last_xlog_receive_location_gte_replayed == false)
{
/*
* We're not receiving streaming WAL - in this case the receive location
* equals the last replayed location
*/
apply_lag = 0;
strncpy(last_xlog_receive_location, last_xlog_replay_location, MAXLEN);
lsn_last_xlog_receive_location = lsn_to_xlogrecptr(last_xlog_replay_location, NULL);
}
else
{
apply_lag = (long long unsigned int)lsn_last_xlog_receive_location - lsn_last_xlog_replay_location;
lsn_last_xlog_receive_location = lsn_to_xlogrecptr(last_xlog_receive_location, NULL);
}
/* Calculate replication lag */
if (lsn_master_current_xlog_location >= lsn_last_xlog_receive_location)
{
replication_lag = (long long unsigned int)(lsn_master_current_xlog_location - lsn_last_xlog_receive_location);
}
else
{
/* This should never happen, but in case it does set lag to zero */
log_warning("Master xlog (%s) location appears less than standby receive location (%s)\n",
last_wal_primary_location,
last_xlog_receive_location);
replication_lag = 0;
}
/* Calculate the lag */
lsn_master = lsn_to_xlogrecptr(last_wal_master_location, NULL);
lsn_standby_received = lsn_to_xlogrecptr(last_wal_standby_received, NULL);
lsn_standby_applied = lsn_to_xlogrecptr(last_wal_standby_applied, NULL);
/*
* Build the SQL to execute on master
*/
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor "
" (primary_node, "
" standby_node, "
" last_monitor_time, "
" last_apply_time, "
" last_wal_primary_location, "
" last_wal_standby_location, "
" replication_lag, "
" apply_lag ) "
" VALUES(%d, "
" %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, "
" '%s'::TIMESTAMP WITH TIME ZONE, "
" '%s', "
" '%s', "
" %llu, "
" %llu) ",
" (primary_node, standby_node, "
" last_monitor_time, last_apply_time, "
" last_wal_primary_location, last_wal_standby_location, "
" replication_lag, apply_lag ) "
" VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, '%s'::TIMESTAMP WITH TIME ZONE, "
" '%s', '%s', "
" %llu, %llu) ",
get_repmgr_schema_quoted(master_conn),
master_options.node,
local_options.node,
monitor_standby_timestamp,
last_xact_replay_timestamp,
last_wal_primary_location,
last_xlog_receive_location,
replication_lag,
apply_lag);
master_options.node, local_options.node,
monitor_standby_timestamp, last_wal_standby_applied_timestamp,
last_wal_master_location, last_wal_standby_received,
(long long unsigned int)(lsn_master - lsn_standby_received),
(long long unsigned int)(lsn_standby_received - lsn_standby_applied));
/*
* Execute the query asynchronously, but don't check for a result. We will
* check the result next time we pause for a monitor step.
@@ -1157,7 +1057,7 @@ do_master_failover(void)
XLogRecPtr xlog_recptr;
bool lsn_format_ok;
char last_xlog_replay_location[MAXLEN];
char last_wal_standby_applied[MAXLEN];
PGconn *node_conn = NULL;
@@ -1167,8 +1067,8 @@ do_master_failover(void)
*/
t_node_info nodes[FAILOVER_NODES_MAX_CHECK];
/* Store details of the failed node here */
t_node_info failed_master = T_NODE_INFO_INITIALIZER;
/* Store details of the failed node here */
t_node_info failed_master = T_NODE_INFO_INITIALIZER;
/* Store details of the best candidate for promotion to master here */
t_node_info best_candidate = T_NODE_INFO_INITIALIZER;
@@ -1178,7 +1078,7 @@ do_master_failover(void)
"SELECT id, conninfo, type, upstream_node_id "
" FROM %s.repl_nodes "
" WHERE cluster = '%s' "
" AND active IS TRUE "
" AND active IS TRUE "
" AND priority > 0 "
" ORDER BY priority DESC, id "
" LIMIT %i ",
@@ -1191,6 +1091,7 @@ do_master_failover(void)
{
log_err(_("unable to retrieve node records: %s\n"), PQerrorMessage(my_local_conn));
PQclear(res);
PQfinish(my_local_conn);
terminate(ERR_DB_QUERY);
}
@@ -1339,8 +1240,8 @@ do_master_failover(void)
" considered as new master and exit.\n"),
PQerrorMessage(my_local_conn));
PQclear(res);
sprintf(last_xlog_replay_location, "'%X/%X'", 0, 0);
update_shared_memory(last_xlog_replay_location);
sprintf(last_wal_standby_applied, "'%X/%X'", 0, 0);
update_shared_memory(last_wal_standby_applied);
terminate(ERR_DB_QUERY);
}
/* write last location in shared memory */
@@ -1421,7 +1322,7 @@ do_master_failover(void)
log_crit(
_("unable to obtain LSN from node %i"), nodes[i].node_id
);
log_hint(
log_info(
_("please check that 'shared_preload_libraries=repmgr_funcs' is set in postgresql.conf\n")
);
@@ -1470,6 +1371,9 @@ do_master_failover(void)
PQfinish(node_conn);
}
/* Close the connection to this server */
PQfinish(my_local_conn);
my_local_conn = NULL;
/*
* determine which one is the best candidate to promote to master
@@ -1517,24 +1421,18 @@ do_master_failover(void)
terminate(ERR_FAILOVER_FAIL);
}
log_debug("best candidate node id is %i\n", best_candidate.node_id);
/* if local node is the best candidate, promote it */
if (best_candidate.node_id == local_options.node)
{
PQExpBufferData event_details;
/* Close the connection to this server */
PQfinish(my_local_conn);
my_local_conn = NULL;
initPQExpBuffer(&event_details);
/* wait */
sleep(5);
log_notice(_("this node is the best candidate to be the new master, promoting...\n"));
log_debug("promote command is: \"%s\"\n",
log_debug(_("promote command is: \"%s\"\n"),
local_options.promote_command);
if (log_type == REPMGR_STDERR && *local_options.logfile)
@@ -1545,33 +1443,6 @@ do_master_failover(void)
r = system(local_options.promote_command);
if (r != 0)
{
/*
* Check whether the primary reappeared, which will have caused the
* promote command to fail
*/
my_local_conn = establish_db_connection(local_options.conninfo, false);
if (my_local_conn != NULL)
{
int master_node_id;
master_conn = get_master_connection(my_local_conn,
local_options.cluster_name,
&master_node_id, NULL);
if (master_conn != NULL && master_node_id == failed_master.node_id)
{
log_notice(_("Original master reappeared before this standby was promoted - no action taken\n"));
PQfinish(master_conn);
master_conn = NULL;
/* no failover occurred but we'll want to restart connections */
failover_done = true;
return;
}
}
log_err(_("promote command failed. You could check and try it manually.\n"));
terminate(ERR_DB_QUERY);
@@ -1603,40 +1474,12 @@ do_master_failover(void)
{
PGconn *new_master_conn;
PQExpBufferData event_details;
int master_node_id;
initPQExpBuffer(&event_details);
/* wait */
sleep(10);
/*
* Check whether the primary reappeared while we were waiting, so we
* don't end up following the promotion candidate
*/
master_conn = get_master_connection(my_local_conn,
local_options.cluster_name,
&master_node_id, NULL);
if (master_conn != NULL && master_node_id == failed_master.node_id)
{
log_notice(_("Original master reappeared - no action taken\n"));
PQfinish(master_conn);
/* no failover occurred but we'll want to restart connections */
failover_done = true;
return;
}
/* Close the connection to this server */
PQfinish(my_local_conn);
my_local_conn = NULL;
/* XXX double-check the promotion candidate did become the new primary */
log_notice(_("node %d is the best candidate for new master, attempting to follow...\n"),
log_info(_("node %d is the best candidate for new master, attempting to follow...\n"),
best_candidate.node_id);
/*
@@ -1682,11 +1525,11 @@ do_master_failover(void)
node_info = get_node_info(new_master_conn, local_options.cluster_name, local_options.node);
appendPQExpBuffer(&event_details,
_("node %i now following new upstream node %i"),
_("Node %i now following new upstream node %i"),
node_info.node_id,
best_candidate.node_id);
log_notice("%s\n", event_details.data);
log_info("%s\n", event_details.data);
create_event_record(new_master_conn,
&local_options,
@@ -1759,7 +1602,7 @@ do_upstream_standby_failover(t_node_info upstream_node)
if (PQntuples(res) == 0)
{
log_err(_("no node with id %i found\n"), upstream_node_id);
log_err(_("no node with id %i found"), upstream_node_id);
PQclear(res);
return false;
}
@@ -1842,7 +1685,7 @@ do_upstream_standby_failover(t_node_info upstream_node)
}
appendPQExpBuffer(&event_details,
_("node %i is now following upstream node %i"),
_("Node %i is now following upstream node %i"),
node_info.node_id,
upstream_node_id);
@@ -1924,7 +1767,7 @@ check_connection(PGconn **conn, const char *type, const char *conninfo)
static bool
set_local_node_status(void)
{
PGresult *res;
PGresult *res;
char sqlquery[QUERY_STR_LEN];
int active_master_node_id = NODE_NOT_FOUND;
char master_conninfo[MAXLEN];
@@ -2017,12 +1860,10 @@ check_cluster_configuration(PGconn *conn)
log_info(_("checking cluster configuration with schema '%s'\n"), get_repmgr_schema());
sqlquery_snprintf(sqlquery,
"SELECT oid FROM pg_catalog.pg_class "
"SELECT oid FROM pg_class "
" WHERE oid = '%s.repl_nodes'::regclass ",
get_repmgr_schema_quoted(master_conn));
get_repmgr_schema_quoted(master_conn));
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_err(_("PQexec failed: %s\n"), PQerrorMessage(conn));
@@ -2094,8 +1935,6 @@ check_node_configuration(void)
/* Adding the node */
log_info(_("adding node %d to cluster '%s'\n"),
local_options.node, local_options.cluster_name);
/* XXX use create_node_record() */
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_nodes"
" (id, cluster, name, conninfo, priority, witness) "
@@ -2217,7 +2056,7 @@ terminate(int retval)
static void
update_shared_memory(char *last_xlog_replay_location)
update_shared_memory(char *last_wal_standby_applied)
{
PGresult *res;
char sqlquery[QUERY_STR_LEN];
@@ -2225,7 +2064,7 @@ update_shared_memory(char *last_xlog_replay_location)
sprintf(sqlquery,
"SELECT %s.repmgr_update_standby_location('%s')",
get_repmgr_schema_quoted(my_local_conn),
last_xlog_replay_location);
last_wal_standby_applied);
/* If an error happens, just inform about that and continue */
res = PQexec(my_local_conn, sqlquery);
@@ -2415,13 +2254,13 @@ check_and_create_pid_file(const char *pid_file)
t_node_info
get_node_info(PGconn *conn, char *cluster, int node_id)
{
int res;
PGresult *res;
t_node_info node_info = T_NODE_INFO_INITIALIZER;
res = get_node_record(conn, cluster, node_id, &node_info);
res = get_node_record(conn, cluster, node_id);
if (res == -1)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
PQExpBufferData errmsg;
initPQExpBuffer(&errmsg);
@@ -2440,16 +2279,47 @@ get_node_info(PGconn *conn, char *cluster, int node_id)
false,
errmsg.data);
PQfinish(conn);
conn = NULL;
PQclear(res);
terminate(ERR_DB_QUERY);
}
if (res == 0)
{
log_warning(_("No record found for node %i\n"), node_id);
if (!PQntuples(res)) {
log_warning(_("No record found record for node %i\n"), node_id);
PQclear(res);
node_info.node_id = NODE_NOT_FOUND;
return node_info;
}
node_info.node_id = atoi(PQgetvalue(res, 0, 0));
node_info.upstream_node_id = atoi(PQgetvalue(res, 0, 1));
strncpy(node_info.conninfo_str, PQgetvalue(res, 0, 2), MAXLEN);
node_info.type = parse_node_type(PQgetvalue(res, 0, 3));
strncpy(node_info.slot_name, PQgetvalue(res, 0, 4), MAXLEN);
node_info.active = (strcmp(PQgetvalue(res, 0, 5), "t") == 0)
? true
: false;
PQclear(res);
return node_info;
}
static t_server_type
parse_node_type(const char *type)
{
if (strcmp(type, "master") == 0)
{
return MASTER;
}
else if (strcmp(type, "standby") == 0)
{
return STANDBY;
}
else if (strcmp(type, "witness") == 0)
{
return WITNESS;
}
return UNKNOWN;
}

View File

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

View File

@@ -63,15 +63,6 @@ UPDATE repl_nodes SET type = 'master' WHERE id = $master_id;
-- UPDATE repl_nodes SET active = FALSE WHERE id IN (...);
/* There's also an event table which we need to create */
CREATE TABLE repl_events (
node_id INTEGER NOT NULL,
event TEXT NOT NULL,
successful BOOLEAN NOT NULL DEFAULT TRUE,
event_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
details TEXT NULL
);
/* When you're sure of your changes, commit them */
-- COMMIT;

View File

@@ -1,35 +0,0 @@
/*
* Update a repmgr 3.0 installation to repmgr 3.1
* ----------------------------------------------
*
* The new repmgr package should be installed first. Then
* carry out these steps:
*
* 1. (If repmgrd is used) stop any running repmgrd instances
* 2. On the master node, execute the SQL statements listed below
* 3. (If repmgrd is used) restart repmgrd
*/
/*
* If your repmgr installation is not included in your repmgr
* user's search path, please set the search path to the name
* of the repmgr schema to ensure objects are installed in
* the correct location.
*
* The repmgr schema is "repmgr_" + the cluster name defined in
* 'repmgr.conf'.
*/
-- SET search_path TO 'name_of_repmgr_schema';
BEGIN;
-- New view "repl_show_nodes" which also displays the server's
-- upstream node
CREATE VIEW repl_show_nodes AS
SELECT rn.id, rn.conninfo, rn.type, rn.name, rn.cluster,
rn.priority, rn.active, sq.name AS upstream_node_name
FROM repl_nodes as rn LEFT JOIN repl_nodes AS sq ON sq.id=rn.upstream_node_id;
COMMIT;

View File

@@ -1,32 +0,0 @@
/*
* Update a repmgr 3.1.1 installation to repmgr 3.1.2
* --------------------------------------------------
*
* This update is only required if repmgrd is being used in conjunction
* with a witness server.
*
* The new repmgr package should be installed first. Then
* carry out these steps:
*
* 1. (If repmgrd is used) stop any running repmgrd instances
* 2. On the master node, execute the SQL statement listed below
* 3. (If repmgrd is used) restart repmgrd
*/
/*
* If your repmgr installation is not included in your repmgr
* user's search path, please set the search path to the name
* of the repmgr schema to ensure objects are installed in
* the correct location.
*
* The repmgr schema is "repmgr_" + the cluster name defined in
* 'repmgr.conf'.
*/
-- SET search_path TO 'name_of_repmgr_schema';
BEGIN;
ALTER TABLE repl_nodes DROP CONSTRAINT repl_nodes_upstream_node_id_fkey,
ADD CONSTRAINT repl_nodes_upstream_node_id_fkey FOREIGN KEY (upstream_node_id) REFERENCES repl_nodes(id) DEFERRABLE;
COMMIT;

View File

@@ -83,12 +83,7 @@ _PG_init(void)
* resources in repmgr_shmem_startup().
*/
RequestAddinShmemSpace(repmgr_memsize());
#if (PG_VERSION_NUM >= 90600)
RequestNamedLWLockTranche("repmgr", 1);
#else
RequestAddinLWLocks(1);
#endif
/*
* Install hooks.
@@ -133,11 +128,7 @@ repmgr_shmem_startup(void)
if (!found)
{
/* First time through ... */
#if (PG_VERSION_NUM >= 90600)
shared_state->lock = &(GetNamedLWLockTranche("repmgr"))->lock;
#else
shared_state->lock = LWLockAssign();
#endif
snprintf(shared_state->location,
sizeof(shared_state->location), "%X/%X", 0, 0);
}

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
/*
* strutil.c
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
* 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

View File

@@ -1,6 +1,6 @@
/*
* strutil.h
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (C) 2ndQuadrant, 2010-2015
*
*
* This program is free software: you can redistribute it and/or modify
@@ -24,17 +24,12 @@
#include <stdlib.h>
#include "errcode.h"
#define QUERY_STR_LEN 8192
#define MAXLEN 1024
#define MAXLINELENGTH 4096
#define MAXVERSIONSTR 16
#define MAXCONNINFO 1024
/* Why? http://stackoverflow.com/a/5459929/398670 */
#define STR(x) CppAsString(x)
#define MAXLEN_STR STR(MAXLEN)
extern int
xsnprintf(char *str, size_t size, const char *format,...)

View File

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

View File

@@ -1,6 +1,6 @@
#ifndef _VERSION_H_
#define _VERSION_H_
#define REPMGR_VERSION "3.1.4"
#define REPMGR_VERSION "3.0.3"
#endif