Compare commits

...

143 Commits

Author SHA1 Message Date
Ian Barwick
e59b57376d Update code comments 2016-07-12 10:59:48 +09:00
Ian Barwick
3db87e6a31 Remove unused error code ERR_BAD_PASSWORD 2016-07-12 10:59:42 +09:00
Ian Barwick
94d05619c3 README: update error code list 2016-07-12 10:59:37 +09:00
Ian Barwick
807c7c926c Update README with details about conninfo parameter handling
From 3.1.4 `repmgr` will behave like other PostgreSQL utilities
when handling database connection parameters, in particular
accepting a conninfo string and honouring libpq connection defaults.
2016-07-12 10:59:30 +09:00
Ian Barwick
df68f1f3f6 Make more consistent use of conninfo parameters
Removed the existing keyword array which has a fixed, limited number
of parameters and replace it with a dynamic array which can be
used to store as many parameters as reported by libpq.
2016-07-12 10:59:26 +09:00
Ian Barwick
d4c75bb6c7 Add missing space when setting "application_name" 2016-07-12 10:59:20 +09:00
Ian Barwick
94d4e1128d Improve default host/dbname handling
repmgr disallows socket connections anyway (the whole point of providing
the host is to connect to a remote machine) so don't show that as
a fallback default in the -?/--help output.
2016-07-12 10:59:13 +09:00
Ian Barwick
dbd82ba687 Enable a conninfo string to be passed to repmgr in the -d/--dbname parameter
This matches the behaviour of other PostgreSQL utilities such as pg_basebackup,
psql et al.

Note that unlike psql, but like pg_basebackup, repmgr does not accept a
"left-over" parameter as a conninfo string; this could be added later.

Parameters specified in the conninfo string will override any parameters
supplied correcly (e.g. `-d "host=foo"` will override `-h bar`).
2016-07-12 10:59:09 +09:00
Ian Barwick
0888fbc538 Generate "primary_conninfo" using the primary connection's parameters
Having successfully connected to the primary, we can use the actual parameters
reported by libpq to create "primary_conninfo", rather than the limited
subset previously defined by repmgr. Assuming that the user can
pass a conninfo string to repmgr (see following commit), this makes it
possible to provide other connection parameters, e.g. related to
SSL usage.
2016-07-12 10:59:05 +09:00
Ian Barwick
92a84bd950 Remove now-superfluous wildcard in rmtree() call 2016-07-07 09:54:50 +09:00
Ian Barwick
a3318d65d2 Bump version
3.1.4
2016-07-07 08:49:42 +09:00
Ian Barwick
374e9811c9 Merge branch 'master' of github.com:2ndQuadrant/repmgr into REL3_1_STABLE 2016-07-06 16:43:39 +09:00
Ian Barwick
72f9b0145a Use rmtree instead of executing "rm -rf"
This completes the task mentioned in b6b6439819
2016-07-06 15:55:56 +09:00
Ian Barwick
5e03ef40cb Update HISTORY 2016-07-06 15:48:14 +09:00
Ian Barwick
091541619d Fix repmgrd monitoring calculation when in archive recovery 2016-07-06 09:27:31 +09:00
Ian Barwick
5e9db47d12 Fix query in get_node_record_by_name() 2016-07-05 21:06:31 +09:00
Ian Barwick
e8a0cd33b5 Ensure all node record structures are initialised 2016-07-05 11:33:06 +09:00
Ian Barwick
8cd79fd7dd Revert "repmgr: add option -B/--remote-pg_bindir for standby switchover"
This reverts commit c30447ac90.
2016-07-04 11:30:36 +09:00
Ian Barwick
013b4b4b8a Update README/TODO about following non-master server 2016-07-01 12:15:37 +09:00
Ian Barwick
c5a721a3cf TODO: remove resolved item 2016-07-01 12:07:10 +09:00
Ian Barwick
a6294b7da0 Update README.md
Add note about logging configuration and settings in `pg_ctl_options`
for switchover operations.
2016-07-01 10:47:20 +09:00
Ian Barwick
a0f02e454c repmgr: during switchover check demotion candidate's pidfile
If the pidfile is still there after apparent shutdown, or we're
unable to access the server at all, something has gone wrong and
the switchover should be aborted.
2016-07-01 09:42:55 +09:00
Ian Barwick
69d9d137e0 repmgr: change default pg_ctl shutdown mode to "fast"
This matches the default pg_ctl behaviour (from 9.5) and is the
more sensible option for performing time-critical operations such
as switchover.
2016-07-01 08:52:26 +09:00
Ian Barwick
60bceae905 Use PQping only to check for server shutdown
Functionally there's no difference between that and attempting to
make an actual connection, so use one method only, which also
simplifies the code.
2016-07-01 07:50:27 +09:00
Ian Barwick
746c9793ed Better detect completion of demotion candidate shutdown
If a connection attempt fails, keep pinging the server until it
finally away, or the timeout kicks in.

Addresses issue reported in GitHub #188 and previously noted in
repmgr.c
2016-06-30 21:35:00 +09:00
Ian Barwick
c30447ac90 repmgr: add option -B/--remote-pg_bindir for standby switchover
This enables the switchover operation to function if the remote server
(current primary) has a different binary directory to the current
server, and addresses the issue reported in GitHub #172.
2016-06-30 12:51:54 +09:00
Ian Barwick
097024a32f repmgr: add new error code ERR_SWITCHOVER_FAIL 2016-06-29 12:11:53 +09:00
Ian Barwick
66b7dbbed7 repmgr: use make_pg_path() consistently
Per comment from gciolli.
2016-06-29 11:33:33 +09:00
Ian Barwick
74f6f97f26 repmgrd: log whether in standby or witness monitor loop
This is mainly for development and debugging purposes.
2016-06-29 10:31:57 +09:00
Ian Barwick
968c2f1954 Add notes about connect_timeout conninfo parameter.
Per suggestion in GitHub #148
2016-06-27 13:57:40 +09:00
Ian Barwick
bd76d0eb92 Update postgresql.org links to https 2016-06-27 12:32:10 +09:00
Ian Barwick
f1ee6e19b6 Ensure configuration options correctly initialised in repmgrd.c
Per GitHub #150.

Also remove unused variable.
2016-06-27 11:26:05 +09:00
Ian Barwick
fbb65b4a43 Remove RHEL packaging files.
There's no point in maintaining in parallel to the PGDG packages.
See also notes in GitHub #156.
2016-06-24 10:19:20 +09:00
Ian Barwick
3fac975de6 Prevent multiple nodes being registered with the same name.
Fixes GitHub #192.
2016-06-24 09:25:41 +09:00
Ian Barwick
a2b5ba595a repmgrd: reword log message for clarity 2016-06-23 09:47:35 +09:00
Ian Barwick
c16ab3c889 Fix handling of global PGconn variables in repmgrd
Don't call PQfinish before calling terminate(), elsewhere always
set to NULL after calling PQfinish().

This fixes GitHub #182.
2016-06-21 17:30:22 +09:00
Ian Barwick
dd5b6f9f12 Whitespace fixes 2016-06-21 16:04:41 +09:00
Ian Barwick
303bb22ee1 Note potential replication lag check improvement 2016-06-20 12:23:34 +09:00
Ian Barwick
5d8b1a3a31 monitoring: ensure that invalid replication_lag value is not inserted.
Per Github #189.
2016-06-20 10:55:25 +09:00
Ian Barwick
3d6c349d88 Rename "pg_restore_command" to "restore_command"
The 'pg_' prefix could cause confusion with actual binaries
(pg_ctl, pg_basebackup etc.) and there's no obvious reason
why we need it.
2016-06-17 14:43:02 +09:00
Ian Barwick
1ade1acb22 Report standby location as last apply location when in archive recovery
Otherwise the monitoring table's 'last_wal_standby_location' will stay at
the location of the last streaming WAL received.

This complements the bugfix applied in e814c1120e.
2016-06-15 15:41:10 +09:00
Ian Barwick
66fd003ab4 Schema-qualify pg_catalog objects 2016-06-10 17:58:10 +09:00
Ian Barwick
0d42b771f5 Remove unnecessary get_server_version() calls in do_standby_clone()
We cache the value at the start of the function, and it's reasonable to
assume that the server version is not going to suddenly change.
2016-06-06 23:41:47 +09:00
Ian Barwick
005640be51 Fix PQconninfoParse() return type check 2016-06-05 10:20:42 +09:00
Martin
b6ebd34e2f Some other indentation fixes found 2016-06-03 20:20:43 -03:00
Martin
951879f80d Typo noticed by Brett Maton. 2016-06-03 20:20:43 -03:00
Martin
46ff9fb587 No code change, just indentation was incorrect in the failover part
making it hard to read.
2016-06-03 20:20:43 -03:00
Ian Barwick
cc610f995d Whitespace and typo fixes 2016-06-03 21:25:22 +09:00
Martin
384618cb33 There was a missing table in sql/repmgr2_repmgr3.sql which made events
error when trying to insert them.

This is just a copy and paste from the table creation in repmgr.c

This fixes #184 reported by Andreas Kretschmer
2016-06-02 14:23:14 -03:00
Martin
0dd617cfca The ALTER TABLE to set the foreign key as DEFERRABLE only worked on
9.4+, as there is no ALTER CONSTRAINT in 9.3.

This new ALTER TABLE does the same in two hops by removing the foreign
key and creating it again in the same ALTER TABLE.

This fixes #183
2016-06-02 13:56:06 -03:00
Martin
f18d629bd2 Typo in a comment.
Reported by @nucfisher
2016-05-30 09:41:38 -03:00
Ian Barwick
afc904f876 Fix typos and whitespace 2016-05-30 16:01:38 +09:00
Ian Barwick
3bcea46c3b Clarify handling of tablespace_map file. 2016-05-30 10:48:08 +09:00
Ian Barwick
d7e85f7565 Clarify purpose of remapping code 2016-05-30 07:47:09 +09:00
Ian Barwick
b14d8ddb74 Ensure read_backup_label() does not exit on error
This would leave an unstopped backup; we'll let do_standby_clone()
do any cleanup necessary before exiting with an error.
2016-05-24 17:04:54 +09:00
Ian Barwick
9b2a907b09 Convert erroneously forgotten printf debug to proper logging 2016-05-24 16:44:22 +09:00
Martín Marqués
f63d42fe77 Merge pull request #178 from gciolli/master
Debian auto-build version upgrade
2016-05-23 14:48:28 -03:00
Gianni Ciolli
560066fa9d Basic CSV mode for "repmgr cluster show", enabled by --csv command
line option
2016-05-23 19:00:25 +02:00
Martin
3937670d14 Added logrotate configuration file (for those who would like one ;))
and changed parameters in sysconfig file so that we use /var/log for
logs and /var/run for pid files.
2016-05-23 13:30:05 -03:00
Gianni Ciolli
0daa7381b3 Debian auto-build version upgrade 2016-05-22 22:10:31 +02:00
Martín Marqués
e53545af4f Merge pull request #177 from petere/fix-compiler-warning
Fix compiler warning
2016-05-22 09:55:50 -03:00
Peter Eisentraut
45178c19d8 Fix compiler warning
For a char * variable, '\0' is just a strange way to write NULL, and
clang warns about it.
2016-05-21 21:04:19 -04:00
Martin
cf46834041 Add new option pg_restore_command.
This can be used so that repmgr standby clone adds the string
specified in repmgr.conf as a restore_command in recovery.conf.

We can use this option for integration with barman by setting the
parameter to an appropriate get-wal call.
2016-05-17 15:21:40 -03:00
Martin
c30609426a Fix several inconsistencies added in d5d8eb2bcb8862607799d602af620e5ca98bc837 2016-05-17 15:21:40 -03:00
Martin
1c49c4159c Add -X stream parameter to pg_basebackup to ensure that we get
all the WALs needed without needing to set wal_keep_segments to
a ridiculously high value.

This is not necessary on 9.6 if we are using replication slots,
as all WAL segments needed will be kept on the primary until
they are consumed by the slot.
2016-05-17 15:21:40 -03:00
Martin
b6b6439819 I copied over the rmtree function (and other functions needed by this one)
from the postgresql code so we use that instead of issuing system calls
with rm -rf ....

I also eliminated the rm -rf for pg_xlog.

Will later do the same with the other system call to remove files
in pg_replslot/
2016-05-17 15:21:40 -03:00
Ian Barwick
16896510dc Fix log formatting 2016-05-17 17:24:30 +09:00
Ian Barwick
9a05999abb Fix log formatting 2016-05-17 17:24:02 +09:00
Ian Barwick
1c155a1088 Update HISTORY 2016-05-17 11:12:18 +09:00
Ian Barwick
4c463a66b7 Update HISTORY 2016-05-17 10:37:14 +09:00
Ian Barwick
31d57f4122 Update Makefile
Add include file dependencies (see caveat in file).

Also update comments.
2016-05-16 19:15:58 +09:00
Ian Barwick
209de699ce README.md: improve documentation of repl_status view 2016-05-16 13:51:40 +09:00
Ian Barwick
7b313b9d71 README.md: improve documentation of repl_status view 2016-05-16 13:51:08 +09:00
Ian Barwick
cf126642bd repmgrd: handle situations where streaming replication is inactive 2016-05-16 12:31:31 +09:00
Ian Barwick
52281fcde8 repmgrd: rename some variables to better match the system functions they're populated from 2016-05-16 12:31:06 +09:00
Ian Barwick
de573edaaa Remove extraneous PQfinish() 2016-05-16 12:23:39 +09:00
Ian Barwick
4cb7f301ad Correct check for wal_level in 9.3 2016-05-16 12:23:33 +09:00
Ian Barwick
87d8de4441 Remove unneeded column 2016-05-16 12:23:25 +09:00
Ian Barwick
6db742f81e repmgrd: better handling of missing upstream_node_id
Ensure we default to master node.
2016-05-16 12:23:20 +09:00
Ian Barwick
c79933685c Add missing newlines in log messages 2016-05-16 12:23:15 +09:00
Ian Barwick
04ba672b9f repmgrd: avoid additional connection to local instance in do_master_failover() 2016-05-16 12:23:09 +09:00
Ian Barwick
4f4111063a Suppress gnu_printf format warning 2016-05-16 12:23:03 +09:00
Ian Barwick
3a3a536e6d repmgrd: rename variable for clarity 2016-05-16 12:22:58 +09:00
Ian Barwick
6f7206a5a1 Don't follow the promotion candidate standby if the primary reappears 2016-05-16 12:22:49 +09:00
Ian Barwick
f9fd1dd227 Don't terminate a standby's repmgrd if self-promotion fails due to master reappearing
Per GitHub #173
2016-05-16 12:22:40 +09:00
Martin
8140ba9c27 The commit fixes problems not taking in account while working on the
issue with rsync returning non-zero status on vanishing files on commit
83e5f98171.

Alvaro Herrera gave me some tips which pointed me in the correct
direction.

This was reported by sungjae lee <sj860908@gmail.com>
2016-05-16 12:22:27 +09:00
Ian Barwick
32dba444e1 Enable long option --pgdata as alias for -D/--data-dir
pg_ctl provides -D/--pgdata; we want to be as close to the core utilities
as possible.
2016-05-16 12:22:17 +09:00
Ian Barwick
e814c1120e repmgrd: handle situations where streaming replication is inactive 2016-05-12 22:17:44 +09:00
Ian Barwick
247823db4d Remove extraneous PQfinish() 2016-05-12 14:05:44 +09:00
Ian Barwick
beda22d5f9 Correct check for wal_level in 9.3 2016-05-12 13:06:10 +09:00
Ian Barwick
2eb00a3e6f Remove unneeded column 2016-05-12 09:56:29 +09:00
Ian Barwick
0a798bf6e4 Comment fixes and formatting tweaks 2016-05-12 09:52:22 +09:00
Ian Barwick
21b2ff1a1f repmgrd: better handling of missing upstream_node_id
Ensure we default to master node.
2016-05-12 09:20:33 +09:00
Ian Barwick
8212ff8d8a Bump version
3.1.3
2016-05-12 07:54:42 +09:00
Ian Barwick
57f9432692 Add missing newlines in log messages 2016-05-11 21:47:40 +09:00
Ian Barwick
54d3c7a4ca repmgrd: avoid additional connection to local instance in do_master_failover() 2016-05-11 09:55:38 +09:00
Ian Barwick
7fd44a3d74 Suppress gnu_printf format warning 2016-05-11 09:23:06 +09:00
Ian Barwick
b0f6b7bad7 repmgrd: rename variable for clarity 2016-05-11 08:29:55 +09:00
Ian Barwick
4dbbf40196 Don't follow the promotion candidate standby if the primary reappears 2016-05-10 13:58:59 +09:00
Ian Barwick
d5e24689a4 Don't terminate a standby's repmgrd if self-promotion fails due to master reappearing
Per GitHub #173
2016-05-10 11:45:03 +09:00
Martin
10e47441a2 The commit fixes problems not taking in account while working on the
issue with rsync returning non-zero status on vanishing files on commit
83e5f98171.

Alvaro Herrera gave me some tips which pointed me in the correct
direction.

This was reported by sungjae lee <sj860908@gmail.com>
2016-05-06 17:34:46 -03:00
Martin
1ccd0edad2 We were not checking the return code after rsyncing the tablespaces.
This fixes #168
2016-04-17 17:59:50 -03:00
Martin
59b31dd1ca Ignore rsync error code for vanished files.
It's very common to come over vanish files during a backup or rsync
o the data directory (dropped index, temp tables, etc.)

This fixes #149
2016-04-17 17:59:50 -03:00
Ian Barwick
300b9f0cc2 Fix pre-9.6 wal_level check 2016-04-12 16:18:29 +09:00
Ian Barwick
0efee4cf65 Fix hint message formatting 2016-04-12 16:07:38 +09:00
Ian Barwick
0cb2584886 Bump version
3.1.2
2016-04-12 15:56:39 +09:00
Ian Barwick
b88d27248c Use "immediately_reserve" parameter in pg_create_physical_replication_slot (9.6) 2016-04-12 15:56:06 +09:00
Ian Barwick
683c54325e Enable repmgr to be compiled with PostgreSQL 9.6 2016-04-12 15:55:51 +09:00
Ian Barwick
70d398cd47 Update HISTORY 2016-04-12 15:53:40 +09:00
Ian Barwick
7b7d80e5f2 Update HISTORY 2016-04-12 15:53:33 +09:00
Ian Barwick
96b0e26084 Remove duplicate inclusion from header file 2016-04-06 14:16:00 +09:00
Ian Barwick
91c498f6f1 Update HISTORY 2016-04-06 11:57:46 +09:00
Ian Barwick
d48093e732 Preserver failover slots when cloning a standby, if enabled 2016-04-06 11:20:27 +09:00
Ian Barwick
3f0d1754a4 MAXFILENAME -> MAXPGPATH 2016-04-06 11:20:27 +09:00
Craig Ringer
f27979bbe1 WIP support for preserving failover slots 2016-04-06 11:20:27 +09:00
Ian Barwick
e9445a5d5e Make self-referencing foreign key on repl_nodes table deferrable 2016-04-01 15:20:36 +09:00
Ian Barwick
9a2717b5e3 Improve debugging output for node resyncing
We'll need this for testing.
2016-04-01 15:20:32 +09:00
Ian Barwick
dd6ea1cd77 Rename copy_configuration () to witness_copy_node_records()
As it's witness-specific. Per suggestion from Martín.
2016-04-01 11:30:08 +09:00
Ian Barwick
de5908c122 Make witness server node update an atomic operation
If the connection to the primary is lost, roll back to the previously
known state.

TRUNCATE is of course not MVCC-friendly, but that shouldn't matter here
as only one process should ever be looking at this table.
2016-04-01 11:15:27 +09:00
Ian Barwick
4b5c84921c Replace MAXFILENAME with MAXPGPATH 2016-03-31 20:11:10 +09:00
Ian Barwick
aaa8d70cef Comment out configuration items in sample config file
The configured values are either the defaults, or examples which
may not work in a real environment. If this file is being used as
a template, the onus is on the user to uncomment and check all
desired parameters.
2016-03-31 15:14:30 +09:00
Gianni Ciolli
ca31b846e7 Rewording comment for clarity. 2016-03-31 15:01:29 +09:00
Ian Barwick
a27cecb559 Update HISTORY 2016-03-31 14:59:03 +09:00
Ian Barwick
cf0cdfa6a1 Bump version
3.1.2rc1
2016-03-31 14:56:49 +09:00
Ian Barwick
31489d92c0 Check directory entity filetype in a more portable way 2016-03-30 20:21:41 +09:00
Ian Barwick
b7fd13aed2 Fix pg_ctl path generation in do_standby_switchover() 2016-03-30 16:46:57 +09:00
Ian Barwick
3c4bf27aa7 Add headers as dependencies in Makefile 2016-03-30 15:06:15 +09:00
Ian Barwick
0ebd9c15d9 Regularly sync witness server repl_nodes table.
Although the witness server will resync the repl_nodes table following
a failover, other operations (e.g. removing or cloning a standby)
were previously not reflected in the witness server's copy of this
table.

As a short-term workaround, automatically resync the table at regular
intervals (defined by the configuration file parameter
"witness_repl_nodes_sync_interval_secs", default 30 seconds).
2016-03-30 15:06:12 +09:00
Nikolay Shaplov
f9dba283d4 Better use /24 network mask in this example 2016-03-30 15:05:29 +09:00
Ian Barwick
205f1cebbb It's unlikely this situation will occur on a witness server
Which is why the error message is for master/standby only.
2016-03-30 15:05:26 +09:00
Ian Barwick
4d97c1ebf7 Add hint about registering the server after cloning it.
This step is easy to forget.
2016-03-30 15:05:20 +09:00
Ian Barwick
12c395e91f README: Add note about 'repmgr_funcs' 2016-03-30 15:05:17 +09:00
Ian Barwick
bd1e4f71d6 repmgrd: fix error message 2016-03-30 15:05:10 +09:00
Ian Barwick
cb49071ea4 Fix code comment 2016-03-30 15:05:06 +09:00
Ian Barwick
5ad674edff Bump version
3.1.1
2016-02-23 15:56:24 +09:00
Ian Barwick
ac09bad89c Minor fixes to README.md 2016-02-23 14:37:59 +09:00
Ian Barwick
009d92fec8 Ensure witness node is registered before the repl_nodes table is copied
This fixes a bug introduced into the previous commit, where the
witness node was registered last to prevent a spurious node record
being created even if witness server creation failed.
2016-02-23 14:37:54 +09:00
Martin
b3d8a68a1d Fix a few paragraphs from the README.md. 2016-02-23 14:37:48 +09:00
Ian Barwick
05b47cb2a8 Prevent repmgr/repmgrd running as root 2016-02-23 14:37:44 +09:00
Ian Barwick
dc542a1b7d Better handling of errors during witness creation
Ensure witness is only registered after all steps for creation
have been successfully completed.

Also write an event record if connection could not be made to
the witness server after initial creation.

This addresses GitHub issue #146.
2016-02-23 14:37:39 +09:00
Ian Barwick
6ce8058749 witness creation: extract database and user names from the local conninfo string
99.9% of the time they'll be the same as the primary connection, but
it's more consistent to use the provided local conninfo string
(from which the port is already extracted).
2016-02-23 14:37:31 +09:00
Ian Barwick
2edcac77f0 README.md: update witness server section 2016-02-23 14:37:27 +09:00
Ian Barwick
f740374392 Add '-P/--pwprompt' option for "repmgr create witness"
Optionally prompt for superuser and repmgr user when creating a witness.
This ensures a password can be provided if the primary's pg_hba.conf
mandates it.

This deprecates '--initdb-no-pwprompt'; and changes the default behaviour of
"repmgr create witness", which previously required a superuser password
unless '--initdb-no-pwprompt' was supplied.

This behaviour is more consistent with other PostgreSQL utilities such
as createuser.

Partial fix for GitHub issue #145.
2016-02-23 14:37:23 +09:00
25 changed files with 1201 additions and 594 deletions

2
FAQ.md
View File

@@ -38,7 +38,7 @@ General
No. Hash indexes and replication do not mix well and their use is No. Hash indexes and replication do not mix well and their use is
explicitly discouraged; see: explicitly discouraged; see:
http://www.postgresql.org/docs/current/interactive/sql-createindex.html#AEN74175 https://www.postgresql.org/docs/current/interactive/sql-createindex.html#AEN74175
`repmgr` `repmgr`
-------- --------

19
HISTORY
View File

@@ -1,3 +1,22 @@
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 3.1.2 2016-04-12
Fix pg_ctl path generation in do_standby_switchover() (Ian) Fix pg_ctl path generation in do_standby_switchover() (Ian)
Regularly sync witness server repl_nodes table (Ian) Regularly sync witness server repl_nodes table (Ian)

View File

@@ -5,7 +5,7 @@
HEADERS = $(wildcard *.h) HEADERS = $(wildcard *.h)
repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o dirmod.o
DATA = repmgr.sql uninstall_repmgr.sql DATA = repmgr.sql uninstall_repmgr.sql

166
README.md
View File

@@ -48,7 +48,7 @@ This guide assumes that you are familiar with PostgreSQL administration and
streaming replication concepts. For further details on streaming streaming replication concepts. For further details on streaming
replication, see this link: replication, see this link:
http://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION https://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION
The following terms are used throughout the `repmgr` documentation. The following terms are used throughout the `repmgr` documentation.
@@ -215,6 +215,34 @@ command line options:
- `-b/--pg_bindir` - `-b/--pg_bindir`
### Command line options and environment variables
For some commands, e.g. `repmgr standby clone`, database connection parameters
need to be provided. Like other PostgreSQL utilities, following standard
parameters can be used:
- `-d/--dbname=DBNAME`
- `-h/--host=HOSTNAME`
- `-p/--port=PORT`
- `-U/--username=USERNAME`
If `-d/--dbname` contains an `=` sign or starts with a valid URI prefix (`postgresql://`
or `postgres://`), it is treated as a conninfo string. See the PostgreSQL
documentation for further details:
https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
Note that if a `conninfo` string is provided, values set in this will override any
provided as individual parameters. For example, with `-d 'host=foo' --host bar`, `foo`
will be chosen over `bar`.
Like other PostgreSQL utilities, `repmgr` will default to any values set in environment
variables if explicit command line parameters are not provided. See the PostgreSQL
documentation for further details:
https://www.postgresql.org/docs/current/static/libpq-envars.html
Setting up a simple replication cluster with repmgr Setting up a simple replication cluster with repmgr
--------------------------------------------------- ---------------------------------------------------
@@ -237,15 +265,19 @@ both servers.
On the master server, a PostgreSQL instance must be initialised and running. On the master server, a PostgreSQL instance must be initialised and running.
The following replication settings must be included in `postgresql.conf`: The following replication settings must be included in `postgresql.conf`:
# Enable replication connections; set this figure to at least one more
# than the number of standbys which will connect to this server
# (note that repmgr will execute `pg_basebackup` in WAL streaming mode,
# which requires two free WAL senders)
max_wal_senders = 10
# Ensure WAL files contain enough information to enable read-only queries # Ensure WAL files contain enough information to enable read-only queries
# on the standby # on the standby
wal_level = 'hot_standby' wal_level = 'hot_standby'
# Enable up to 10 replication connections
max_wal_senders = 10
# How much WAL to retain on the master to allow a temporarily # How much WAL to retain on the master to allow a temporarily
# disconnected standby to catch up again. The larger this is, the # disconnected standby to catch up again. The larger this is, the
# longer the standby can be disconnected. This is needed only in # longer the standby can be disconnected. This is needed only in
@@ -259,16 +291,10 @@ The following replication settings must be included in `postgresql.conf`:
hot_standby = on hot_standby = on
# If archive_mode is enabled, check that 'archive_command' is non empty # Enable WAL file archiving
# (however it's not practical to check that it actually represents a valid
# command).
#
# From PostgreSQL 9.5, archive_mode can be one of 'off', 'on' or 'always'
# so for ease of backwards compatibility, rather than explicitly check for an
# enabled mode, check that it's not "off".
archive_mode = on archive_mode = on
# Set archive command to a script or application that will safetly store # Set archive command to a script or application that will safely store
# you WALs in a secure place. /bin/true is an example of a command that # you WALs in a secure place. /bin/true is an example of a command that
# ignores archiving. Use something more sensible. # ignores archiving. Use something more sensible.
archive_command = '/bin/true' archive_command = '/bin/true'
@@ -472,7 +498,11 @@ so should be used with care.
Further options can be passed to the `pg_basebackup` utility via Further options can be passed to the `pg_basebackup` utility via
the setting `pg_basebackup_options` in `repmgr.conf`. See the PostgreSQL the setting `pg_basebackup_options` in `repmgr.conf`. See the PostgreSQL
documentation for more details of available options: documentation for more details of available options:
<<<<<<< HEAD
http://www.postgresql.org/docs/current/static/app-pgbasebackup.html http://www.postgresql.org/docs/current/static/app-pgbasebackup.html
=======
https://www.postgresql.org/docs/current/static/app-pgbasebackup.html
>>>>>>> 72f9b0145afab1060dd1202c8f8937653c8b2e39
### Using rsync to clone a standby ### Using rsync to clone a standby
@@ -490,7 +520,6 @@ and destination server as the contents of files existing on both servers need
to be compared, meaning this method is not necessarily faster than making a to be compared, meaning this method is not necessarily faster than making a
fresh clone with `pg_basebackup`. fresh clone with `pg_basebackup`.
### Dealing with PostgreSQL configuration files ### Dealing with PostgreSQL configuration files
By default, `repmgr` will attempt to copy the standard configuration files By default, `repmgr` will attempt to copy the standard configuration files
@@ -505,6 +534,21 @@ which enables any valid `rsync` options to be passed to that command, e.g.:
rsync_options='--exclude=postgresql.local.conf' rsync_options='--exclude=postgresql.local.conf'
### Controlling `primary_conninfo` in `recovery.conf`
`repmgr` will create the `primary_conninfo` setting in `recovery.conf` based
on the connection parameters provided to `repmgr standby clone` and PostgreSQL's
standard connection defaults, including any environment variables set on the
local node.
To include specific connection parameters other than the standard host, port,
username and database values (e.g. `sslmode`), include these in a `conninfo`-style
tring passed to `repmgr` with `-d/--dbname` (see above for details), and/or set
appropriate environment variables.
Note that PostgreSQL will always set explicit defaults for `sslmode` and
`sslcompression`.
Setting up cascading replication with repmgr Setting up cascading replication with repmgr
-------------------------------------------- --------------------------------------------
@@ -608,13 +652,13 @@ place. If using the default `pg_basebackup` method, we recommend setting
pg_basebackup_options='--xlog-method=stream' pg_basebackup_options='--xlog-method=stream'
See the `pg_basebackup` documentation for details: See the `pg_basebackup` documentation for details:
http://www.postgresql.org/docs/current/static/app-pgbasebackup.html https://www.postgresql.org/docs/current/static/app-pgbasebackup.html
Otherwise it's necessary to set `wal_keep_segments` to an appropriately high Otherwise it's necessary to set `wal_keep_segments` to an appropriately high
value. value.
Further information on replication slots in the PostgreSQL documentation: Further information on replication slots in the PostgreSQL documentation:
http://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION-SLOTS https://www.postgresql.org/docs/current/interactive/warm-standby.html#STREAMING-REPLICATION-SLOTS
Promoting a standby server with repmgr Promoting a standby server with repmgr
@@ -713,8 +757,9 @@ updated to reflect this:
Note that with cascading replication, `repmgr standby follow` can also be Note that with cascading replication, `repmgr standby follow` can also be
used to detach a standby from its current upstream server and follow another used to detach a standby from its current upstream server and follow the
upstream server, including the master. master. However it's currently not possible to have it follow another standby;
we hope to improve this in a future release.
Performing a switchover with repmgr Performing a switchover with repmgr
@@ -741,7 +786,7 @@ both passwordless SSH access and the path of `repmgr.conf` on that server.
> careful preparation and with adequate attention. In particular you should > careful preparation and with adequate attention. In particular you should
> be confident that your network environment is stable and reliable. > be confident that your network environment is stable and reliable.
> >
> We recommend running `repmgr standby switchover` at the most verbose > We recommend running `repmgr standby switchover` at the most verbose
> logging level (`--log-level DEBUG --verbose`) and capturing all output > logging level (`--log-level DEBUG --verbose`) and capturing all output
> to assist troubleshooting any problems. > to assist troubleshooting any problems.
> >
@@ -807,7 +852,7 @@ should have been updated to reflect this:
### Caveats ### Caveats
- the functionality provided `repmgr standby switchover` is primarily aimed - The functionality provided `repmgr standby switchover` is primarily aimed
at a two-server master/standby replication cluster and currently does at a two-server master/standby replication cluster and currently does
not support additional standbys. not support additional standbys.
- `repmgr standby switchover` is designed to use the `pg_rewind` utility, - `repmgr standby switchover` is designed to use the `pg_rewind` utility,
@@ -816,11 +861,16 @@ should have been updated to reflect this:
- `pg_rewind` *requires* that either `wal_log_hints` is enabled, or that - `pg_rewind` *requires* that either `wal_log_hints` is enabled, or that
data checksums were enabled when the cluster was initialized. See the data checksums were enabled when the cluster was initialized. See the
`pg_rewind` documentation for details: `pg_rewind` documentation for details:
http://www.postgresql.org/docs/current/static/app-pgrewind.html https://www.postgresql.org/docs/current/static/app-pgrewind.html
- `repmgrd` should not be running when a switchover is carried out, otherwise - `repmgrd` should not be running when a switchover is carried out, otherwise
the `repmgrd` may try and promote a standby by itself. the `repmgrd` may try and promote a standby by itself.
- Any other standbys attached to the old master will need to be manually - Any other standbys attached to the old master will need to be manually
instructed to point to the new master (e.g. with `repmgr standby follow`). instructed to point to the new master (e.g. with `repmgr standby follow`).
- You must ensure that following a server start using `pg_ctl`, log output
is not send to STDERR (the default behaviour). If logging is not configured,
We recommend setting `logging_collector=on` in `postgresql.conf` and
providing an explicit `-l/--log` setting in `repmgr.conf`'s `pg_ctl_options`
parameter.
We hope to remove some of these restrictions in future versions of `repmgr`. We hope to remove some of these restrictions in future versions of `repmgr`.
@@ -874,8 +924,8 @@ Adjust schema and node ID accordingly. A future `repmgr` release
will make it possible to unregister failed standbys. will make it possible to unregister failed standbys.
Automatic failover with repmgrd Automatic failover with `repmgrd`
------------------------------- ---------------------------------
`repmgrd` is a management and monitoring daemon which runs on standby nodes `repmgrd` is a management and monitoring daemon which runs on standby nodes
and which can automate actions such as failover and updating standbys to and which can automate actions such as failover and updating standbys to
@@ -995,8 +1045,8 @@ during the failover:
(3 rows) (3 rows)
repmgrd log rotation `repmgrd` log rotation
-------------------- ----------------------
Note that currently `repmgrd` does not provide logfile rotation. To ensure Note that currently `repmgrd` does not provide logfile rotation. To ensure
the current logfile does not grow indefinitely, configure your system's `logrotate` the current logfile does not grow indefinitely, configure your system's `logrotate`
@@ -1012,12 +1062,36 @@ for up to 52 weeks and rotation forced if a file grows beyond 100Mb:
create 0600 postgres postgres create 0600 postgres postgres
} }
Monitoring
---------- `repmgrd` and PostgreSQL connection settings
--------------------------------------------
In addition to the `repmgr` configuration settings, parameters in the
`conninfo` string influence how `repmgr` makes a network connection to
PostgreSQL. In particular, if another server in the replication cluster
is unreachable at network level, system network settings will influence
the length of time it takes to determine that the connection is not possible.
In particular explicitly setting a parameter for `connect_timeout` should
be considered; the effective minimum value of `2` (seconds) will ensure
that a connection failure at network level is reported as soon as possible,
otherwise dependeing on the system settings (e.g. `tcp_syn_retries` in Linux)
a delay of a minute or more is possible.
For further details on `conninfo` network connection parameters, see:
https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
Monitoring with `repmgrd`
-------------------------
When `repmgrd` is running with the option `-m/--monitoring-history`, it will When `repmgrd` is running with the option `-m/--monitoring-history`, it will
constantly write node status information to the `repl_monitor` table, which can constantly write standby node status information to the `repl_monitor` table,
be queried easily using the view `repl_status`: providing a near-real time overview of replication status on all nodes
in the cluster.
The view `repl_status` shows the most recent state for each node, e.g.:
repmgr=# SELECT * FROM repmgr_test.repl_status; repmgr=# SELECT * FROM repmgr_test.repl_status;
-[ RECORD 1 ]-------------+----------------------------- -[ RECORD 1 ]-------------+-----------------------------
@@ -1042,6 +1116,10 @@ table , it's advisable to regularly purge historical data with
`repmgr cluster cleanup`; use the `-k/--keep-history` to specify how `repmgr cluster cleanup`; use the `-k/--keep-history` to specify how
many day's worth of data should be retained. many day's worth of data should be retained.
Note that when a standby node is not streaming directly from its upstream
node, i.e. recovering WAL from an archive, `apply_lag` will always
appear as `0 bytes`.
Using a witness server with repmgrd Using a witness server with repmgrd
------------------------------------ ------------------------------------
@@ -1327,20 +1405,22 @@ which contains connection details for the local database.
`repmgr` or `repmgrd` will return one of the following error codes on program `repmgr` or `repmgrd` will return one of the following error codes on program
exit: exit:
* SUCCESS (0) Program ran successfully. * SUCCESS (0) Program ran successfully.
* ERR_BAD_CONFIG (1) Configuration file could not be parsed or was invalid * ERR_BAD_CONFIG (1) Configuration file could not be parsed or was invalid
* ERR_BAD_RSYNC (2) An rsync call made by the program returned an error * ERR_BAD_RSYNC (2) An rsync call made by the program returned an error (repmgr only)
* ERR_NO_RESTART (4) An attempt to restart a PostgreSQL instance failed * ERR_NO_RESTART (4) An attempt to restart a PostgreSQL instance failed
* ERR_DB_CON (6) Error when trying to connect to a database * ERR_DB_CON (6) Error when trying to connect to a database
* ERR_DB_QUERY (7) Error while executing a database query * ERR_DB_QUERY (7) Error while executing a database query
* ERR_PROMOTED (8) Exiting program because the node has been promoted to master * ERR_PROMOTED (8) Exiting program because the node has been promoted to master
* ERR_BAD_PASSWORD (9) Password used to connect to a database was rejected * ERR_STR_OVERFLOW (10) String overflow error
* ERR_STR_OVERFLOW (10) String overflow error * ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only)
* ERR_FAILOVER_FAIL (11) Error encountered during failover (repmgrd only) * ERR_BAD_SSH (12) Error when connecting to remote host via SSH (repmgr only)
* ERR_BAD_SSH (12) Error when connecting to remote host via SSH * ERR_SYS_FAILURE (13) Error when forking (repmgrd only)
* ERR_SYS_FAILURE (13) Error when forking (repmgrd only) * ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup (repmgr only)
* ERR_BAD_BASEBACKUP (14) Error when executing pg_basebackup * ERR_MONITORING_FAIL (16) Unrecoverable error encountered during monitoring (repmgrd only)
* ERR_MONITORING_FAIL (16) Unrecoverable error encountered during monitoring (repmgrd only) * ERR_BAD_BACKUP_LABEL (17) Corrupt or unreadable backup label encountered (repmgr only)
* ERR_SWITCHOVER_FAIL (18) Error encountered during switchover (repmgr only)
Support and Assistance Support and Assistance
---------------------- ----------------------

View File

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

View File

@@ -1,133 +0,0 @@
#!/bin/sh
#
# chkconfig: - 75 16
# description: Enable repmgrd replication management and monitoring daemon for PostgreSQL
# processname: repmgrd
# pidfile="/var/run/${NAME}.pid"
# Source function library.
INITD=/etc/rc.d/init.d
. $INITD/functions
# Get function listing for cross-distribution logic.
TYPESET=`typeset -f|grep "declare"`
# Get network config.
. /etc/sysconfig/network
DESC="PostgreSQL replication management and monitoring daemon"
NAME=repmgrd
REPMGRD_ENABLED=no
REPMGRD_OPTS=
REPMGRD_USER=postgres
REPMGRD_BIN=/usr/pgsql-9.3/bin/repmgrd
REPMGRD_PIDFILE=/var/run/repmgrd.pid
REPMGRD_LOCK=/var/lock/subsys/${NAME}
REPMGRD_LOG=/var/lib/pgsql/9.3/data/pg_log/repmgrd.log
# Read configuration variable file if it is present
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
# For SELinux we need to use 'runuser' not 'su'
if [ -x /sbin/runuser ]
then
SU=runuser
else
SU=su
fi
test -x $REPMGRD_BIN || exit 0
case "$REPMGRD_ENABLED" in
[Yy]*)
break
;;
*)
exit 0
;;
esac
if [ -z "${REPMGRD_OPTS}" ]
then
echo "Not starting ${NAME}, REPMGRD_OPTS not set in /etc/sysconfig/${NAME}"
exit 0
fi
start()
{
REPMGRD_START=$"Starting ${NAME} service: "
# Make sure startup-time log file is valid
if [ ! -e "${REPMGRD_LOG}" -a ! -h "${REPMGRD_LOG}" ]
then
touch "${REPMGRD_LOG}" || exit 1
chown ${REPMGRD_USER}:postgres "${REPMGRD_LOG}"
chmod go-rwx "${REPMGRD_LOG}"
[ -x /sbin/restorecon ] && /sbin/restorecon "${REPMGRD_LOG}"
fi
echo -n "${REPMGRD_START}"
$SU -l $REPMGRD_USER -c "${REPMGRD_BIN} ${REPMGRD_OPTS} -p ${REPMGRD_PIDFILE} &" >> "${REPMGRD_LOG}" 2>&1 < /dev/null
sleep 2
pid=`head -n 1 "${REPMGRD_PIDFILE}" 2>/dev/null`
if [ "x${pid}" != "x" ]
then
success "${REPMGRD_START}"
touch "${REPMGRD_LOCK}"
echo $pid > "${REPMGRD_PIDFILE}"
echo
else
failure "${REPMGRD_START}"
echo
script_result=1
fi
}
stop()
{
echo -n $"Stopping ${NAME} service: "
if [ -e "${REPMGRD_LOCK}" ]
then
killproc ${NAME}
ret=$?
if [ $ret -eq 0 ]
then
echo_success
rm -f "${REPMGRD_PIDFILE}"
rm -f "${REPMGRD_LOCK}"
else
echo_failure
script_result=1
fi
else
# not running; per LSB standards this is "ok"
echo_success
fi
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status -p $REPMGRD_PIDFILE $NAME
script_result=$?
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 2
esac
exit $script_result

View File

@@ -1,21 +0,0 @@
# default settings for repmgrd. This file is source by /bin/sh from
# /etc/init.d/repmgrd
# disable repmgrd by default so it won't get started upon installation
# valid values: yes/no
REPMGRD_ENABLED=no
# Options for repmgrd (required)
#REPMGRD_OPTS="--verbose -d -f /var/lib/pgsql/repmgr/repmgr.conf"
# User to run repmgrd as
#REPMGRD_USER=postgres
# repmgrd binary
#REPMGRD_BIN=/usr/bin/repmgrd
# pid file
#REPMGRD_PIDFILE=/var/lib/pgsql/repmgr/repmgrd.pid
# log file
#REPMGRD_LOG=/var/lib/pgsql/repmgr/repmgrd.log

5
TODO
View File

@@ -53,8 +53,9 @@ Planned feature improvements
requested, activate the replication slot using pg_receivexlog to negate the 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). need to set `wal_keep_segments` just for the initial clone (9.4 and 9.5).
* Take into account the fact that a standby can obtain WAL from an archive, * repmgr: enable "standby follow" to point a standby at another standby, not
so even if direct streaming replication is interrupted, it may be up-to-date just the replication cluster master (see GitHub #130)
Usability improvements Usability improvements
====================== ======================

View File

@@ -28,7 +28,7 @@ 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 tablespace_list_append(t_configuration_options *options, const char *arg);
static void exit_with_errors(ErrorList *config_errors); static void exit_with_errors(ErrorList *config_errors);
const static char *_progname = '\0'; const static char *_progname = NULL;
static char config_file_path[MAXPGPATH]; static char config_file_path[MAXPGPATH];
static bool config_file_provided = false; static bool config_file_provided = false;
bool config_file_found = false; bool config_file_found = false;
@@ -224,6 +224,7 @@ parse_config(t_configuration_options *options)
memset(options->pg_bindir, 0, sizeof(options->pg_bindir)); memset(options->pg_bindir, 0, sizeof(options->pg_bindir));
memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options)); memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options));
memset(options->pg_basebackup_options, 0, sizeof(options->pg_basebackup_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 */ /* default master_response_timeout is 60 seconds */
options->master_response_timeout = 60; options->master_response_timeout = 60;
@@ -239,6 +240,8 @@ parse_config(t_configuration_options *options)
options->witness_repl_nodes_sync_interval_secs = 30; options->witness_repl_nodes_sync_interval_secs = 30;
memset(options->event_notification_command, 0, sizeof(options->event_notification_command)); 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.head = NULL;
options->tablespace_mapping.tail = NULL; options->tablespace_mapping.tail = NULL;
@@ -340,7 +343,8 @@ parse_config(t_configuration_options *options)
strncpy(options->follow_command, value, MAXLEN); strncpy(options->follow_command, value, MAXLEN);
else if (strcmp(name, "master_response_timeout") == 0) else if (strcmp(name, "master_response_timeout") == 0)
options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors, false); options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors, false);
/* 'primary_response_timeout' as synonym for 'master_response_timeout' - /*
* 'primary_response_timeout' as synonym for 'master_response_timeout' -
* we'll switch terminology in a future release (3.1?) * we'll switch terminology in a future release (3.1?)
*/ */
else if (strcmp(name, "primary_response_timeout") == 0) else if (strcmp(name, "primary_response_timeout") == 0)
@@ -372,6 +376,8 @@ parse_config(t_configuration_options *options)
parse_event_notifications_list(options, value); parse_event_notifications_list(options, value);
else if (strcmp(name, "tablespace_mapping") == 0) else if (strcmp(name, "tablespace_mapping") == 0)
tablespace_list_append(options, value); tablespace_list_append(options, value);
else if (strcmp(name, "restore_command") == 0)
strncpy(options->restore_command, value, MAXLEN);
else else
{ {
known_parameter = false; known_parameter = false;

View File

@@ -72,6 +72,7 @@ typedef struct
char pg_bindir[MAXLEN]; char pg_bindir[MAXLEN];
char pg_ctl_options[MAXLEN]; char pg_ctl_options[MAXLEN];
char pg_basebackup_options[MAXLEN]; char pg_basebackup_options[MAXLEN];
char restore_command[MAXLEN];
char logfile[MAXLEN]; char logfile[MAXLEN];
int monitor_interval_secs; int monitor_interval_secs;
int retry_promote_interval_secs; int retry_promote_interval_secs;
@@ -82,7 +83,11 @@ typedef struct
TablespaceList tablespace_mapping; TablespaceList tablespace_mapping;
} t_configuration_options; } t_configuration_options;
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", 0, 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} } /*
* 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 } }
typedef struct ErrorListCell typedef struct ErrorListCell
{ {

View File

@@ -31,6 +31,7 @@
char repmgr_schema[MAXLEN] = ""; char repmgr_schema[MAXLEN] = "";
char repmgr_schema_quoted[MAXLEN] = ""; char repmgr_schema_quoted[MAXLEN] = "";
static int _get_node_record(PGconn *conn, char *cluster, char *sqlquery, t_node_info *node_info);
PGconn * 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, const bool log_notice)
@@ -420,7 +421,7 @@ guc_set_typed(PGconn *conn, const char *parameter, const char *op,
" WHERE name = '%s' AND setting::%s %s '%s'::%s", " WHERE name = '%s' AND setting::%s %s '%s'::%s",
parameter, datatype, op, value, datatype); parameter, datatype, op, value, datatype);
log_verbose(LOG_DEBUG, "guc_set_typed():n%s\n", sqlquery); log_verbose(LOG_DEBUG, "guc_set_typed():\n%s\n", sqlquery);
res = PQexec(conn, sqlquery); res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -538,7 +539,7 @@ get_conninfo_value(const char *conninfo, const char *keyword, char *output)
conninfo_options = PQconninfoParse(conninfo, NULL); conninfo_options = PQconninfoParse(conninfo, NULL);
if (conninfo_options == false) if (conninfo_options == NULL)
{ {
log_err(_("Unable to parse provided conninfo string \"%s\""), conninfo); log_err(_("Unable to parse provided conninfo string \"%s\""), conninfo);
return false; return false;
@@ -587,7 +588,7 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
upstream_conninfo = upstream_conninfo_out; upstream_conninfo = upstream_conninfo_out;
sqlquery_snprintf(sqlquery, sqlquery_snprintf(sqlquery,
" SELECT un.conninfo, un.name, un.id " " SELECT un.conninfo, un.id "
" FROM %s.repl_nodes un " " FROM %s.repl_nodes un "
"INNER JOIN %s.repl_nodes n " "INNER JOIN %s.repl_nodes n "
" ON (un.id = n.upstream_node_id AND un.cluster = n.cluster)" " ON (un.id = n.upstream_node_id AND un.cluster = n.cluster)"
@@ -604,7 +605,7 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
log_err(_("unable to get conninfo for upstream server\n%s\n"), log_err(_("error when attempting to find upstream server\n%s\n"),
PQerrorMessage(standby_conn)); PQerrorMessage(standby_conn));
PQclear(res); PQclear(res);
return NULL; return NULL;
@@ -612,9 +613,36 @@ get_upstream_connection(PGconn *standby_conn, char *cluster, int node_id,
if (!PQntuples(res)) if (!PQntuples(res))
{ {
log_notice(_("no record found for upstream server"));
PQclear(res); PQclear(res);
return NULL; 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");
} }
strncpy(upstream_conninfo, PQgetvalue(res, 0, 0), MAXCONNINFO); strncpy(upstream_conninfo, PQgetvalue(res, 0, 0), MAXCONNINFO);
@@ -1654,8 +1682,7 @@ int
get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info) get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info)
{ {
char sqlquery[QUERY_STR_LEN]; char sqlquery[QUERY_STR_LEN];
PGresult *res; int result;
int ntuples;
sqlquery_snprintf( sqlquery_snprintf(
sqlquery, sqlquery,
@@ -1669,6 +1696,51 @@ get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info
log_verbose(LOG_DEBUG, "get_node_record():\n%s\n", sqlquery); 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); res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
@@ -1679,7 +1751,6 @@ get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info
if (ntuples == 0) if (ntuples == 0)
{ {
log_verbose(LOG_DEBUG, "get_node_record(): no record found for node %i\n", node_id);
return 0; return 0;
} }
@@ -1700,6 +1771,9 @@ get_node_record(PGconn *conn, char *cluster, int node_id, t_node_info *node_info
} }
int int
get_node_replication_state(PGconn *conn, char *node_name, char *output) get_node_replication_state(PGconn *conn, char *node_name, char *output)
{ {

View File

@@ -52,18 +52,6 @@ typedef struct s_node_info
} t_node_info; } t_node_info;
/*
* Struct to store replication slot information
*/
typedef struct s_replication_slot
{
char slot_name[MAXLEN];
char slot_type[MAXLEN];
bool active;
} t_replication_slot;
#define T_NODE_INFO_INITIALIZER { \ #define T_NODE_INFO_INITIALIZER { \
NODE_NOT_FOUND, \ NODE_NOT_FOUND, \
NO_UPSTREAM_NODE, \ NO_UPSTREAM_NODE, \
@@ -78,6 +66,19 @@ typedef struct s_replication_slot
InvalidXLogRecPtr \ 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, PGconn *_establish_db_connection(const char *conninfo,
const bool exit_on_error, const bool exit_on_error,
const bool log_notice); const bool log_notice);
@@ -125,6 +126,7 @@ bool witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *c
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 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 delete_node_record(PGconn *conn, int node, char *action); 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(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 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_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 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); bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);

View File

@@ -1,9 +1,9 @@
Package: repmgr-auto Package: repmgr-auto
Version: 3.0.1 Version: 3.1.3
Section: database Section: database
Priority: optional Priority: optional
Architecture: all Architecture: all
Depends: rsync, postgresql-9.3 | postgresql-9.4 Depends: rsync, postgresql-9.3 | postgresql-9.4 | postgresql-9.5
Maintainer: Self built package <user@localhost> Maintainer: Self built package <user@localhost>
Description: PostgreSQL replication setup, magament and monitoring Description: PostgreSQL replication setup, magament and monitoring
has two main executables has two main executables

194
dirmod.c Normal file
View File

@@ -0,0 +1,194 @@
/*
*
* 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;
}

23
dirmod.h Normal file
View File

@@ -0,0 +1,23 @@
/*
* 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

@@ -29,7 +29,6 @@
#define ERR_DB_CON 6 #define ERR_DB_CON 6
#define ERR_DB_QUERY 7 #define ERR_DB_QUERY 7
#define ERR_PROMOTED 8 #define ERR_PROMOTED 8
#define ERR_BAD_PASSWORD 9
#define ERR_STR_OVERFLOW 10 #define ERR_STR_OVERFLOW 10
#define ERR_FAILOVER_FAIL 11 #define ERR_FAILOVER_FAIL 11
#define ERR_BAD_SSH 12 #define ERR_BAD_SSH 12
@@ -38,5 +37,6 @@
#define ERR_INTERNAL 15 #define ERR_INTERNAL 15
#define ERR_MONITORING_FAIL 16 #define ERR_MONITORING_FAIL 16
#define ERR_BAD_BACKUP_LABEL 17 #define ERR_BAD_BACKUP_LABEL 17
#define ERR_SWITCHOVER_FAIL 18
#endif /* _ERRCODE_H_ */ #endif /* _ERRCODE_H_ */

5
log.c
View File

@@ -40,7 +40,8 @@
/* #define REPMGR_DEBUG */ /* #define REPMGR_DEBUG */
static int detect_log_facility(const char *facility); 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); 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)));
int log_type = REPMGR_STDERR; int log_type = REPMGR_STDERR;
int log_level = LOG_NOTICE; int log_level = LOG_NOTICE;
@@ -48,7 +49,7 @@ int last_log_level = LOG_NOTICE;
int verbose_logging = false; int verbose_logging = false;
int terse_logging = false; int terse_logging = false;
void extern void
stderr_log_with_level(const char *level_name, int level, const char *fmt, ...) stderr_log_with_level(const char *level_name, int level, const char *fmt, ...)
{ {
va_list arglist; va_list arglist;

8
log.h
View File

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

722
repmgr.c

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@
# Node ID and name # Node ID and name
# (Note: we recommend to avoid naming nodes after their initial # (Note: we recommend to avoid naming nodes after their initial
# replication funcion, as this will cause confusion when e.g. # replication function, as this will cause confusion when e.g.
# "standby2" is promoted to primary) # "standby2" is promoted to primary)
#node=2 # a unique integer #node=2 # a unique integer
#node_name=node2 # an arbitrary (but unique) string; we recommend using #node_name=node2 # an arbitrary (but unique) string; we recommend using
@@ -28,8 +28,16 @@
# Database connection information as a conninfo string # Database connection information as a conninfo string
# This must be accessible to all servers in the cluster; for details see: # This must be accessible to all servers in the cluster; for details see:
# http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING #
# https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
#
#conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr' #conninfo='host=192.168.204.104 dbname=repmgr_db user=repmgr_usr'
#
# 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
# Optional configuration items # Optional configuration items
# ============================ # ============================
@@ -113,6 +121,10 @@
# #
# tablespace_mapping=/path/to/original/tablespace=/path/to/new/tablespace # 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) # Failover settings (repmgrd)
# --------------------------- # ---------------------------

View File

@@ -28,6 +28,7 @@
#include "dbutils.h" #include "dbutils.h"
#include "errcode.h" #include "errcode.h"
#include "config.h" #include "config.h"
#include "dirmod.h"
#define MIN_SUPPORTED_VERSION "9.3" #define MIN_SUPPORTED_VERSION "9.3"
#define MIN_SUPPORTED_VERSION_NUM 90300 #define MIN_SUPPORTED_VERSION_NUM 90300
@@ -69,7 +70,7 @@ typedef struct
bool rsync_only; bool rsync_only;
bool fast_checkpoint; bool fast_checkpoint;
bool ignore_external_config_files; bool ignore_external_config_files;
char pg_ctl_mode[MAXLEN]; bool csv_mode;
char masterport[MAXLEN]; char masterport[MAXLEN];
/* /*
* configuration file parameters which can be overridden on the * configuration file parameters which can be overridden on the
@@ -80,6 +81,7 @@ typedef struct
/* parameter used by STANDBY SWITCHOVER */ /* parameter used by STANDBY SWITCHOVER */
char remote_config_file[MAXLEN]; char remote_config_file[MAXLEN];
char pg_rewind[MAXPGPATH]; char pg_rewind[MAXPGPATH];
char pg_ctl_mode[MAXLEN];
/* parameter used by STANDBY {ARCHIVE_CONFIG | RESTORE_CONFIG} */ /* parameter used by STANDBY {ARCHIVE_CONFIG | RESTORE_CONFIG} */
char config_archive_dir[MAXLEN]; char config_archive_dir[MAXLEN];
/* parameter used by CLUSTER CLEANUP */ /* parameter used by CLUSTER CLEANUP */
@@ -94,7 +96,7 @@ typedef struct
bool initdb_no_pwprompt; bool initdb_no_pwprompt;
} t_runtime_options; } t_runtime_options;
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, "smart", "", "", "", "", "", 0, "", "", "", false } #define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, false, "", "", "", "", "fast", "", 0, "", "", "", false }
struct BackupLabel struct BackupLabel
{ {

257
repmgrd.c
View File

@@ -44,11 +44,11 @@
/* Local info */ /* Local info */
t_configuration_options local_options; t_configuration_options local_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
PGconn *my_local_conn = NULL; PGconn *my_local_conn = NULL;
/* Master info */ /* Master info */
t_configuration_options master_options; t_configuration_options master_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
PGconn *master_conn = NULL; PGconn *master_conn = NULL;
@@ -61,8 +61,6 @@ bool failover_done = false;
char *pid_file = NULL; char *pid_file = NULL;
t_configuration_options config = T_CONFIGURATION_OPTIONS_INITIALIZER;
static void help(void); static void help(void);
static void usage(void); static void usage(void);
static void check_cluster_configuration(PGconn *conn); static void check_cluster_configuration(PGconn *conn);
@@ -399,7 +397,7 @@ main(int argc, char **argv)
case STANDBY: case STANDBY:
/* We need the node id of the master server as well as a connection to it */ /* We need the node id of the master server as well as a connection to it */
log_info(_("connecting to master node '%s'\n"), log_info(_("connecting to master node of cluster '%s'\n"),
local_options.cluster_name); local_options.cluster_name);
master_conn = get_master_connection(my_local_conn, master_conn = get_master_connection(my_local_conn,
@@ -462,16 +460,16 @@ main(int argc, char **argv)
do do
{ {
log_verbose(LOG_DEBUG, "standby check loop...\n"); if (node_info.type == STANDBY)
if (node_info.type == WITNESS)
{
witness_monitor();
}
else if (node_info.type == STANDBY)
{ {
log_verbose(LOG_DEBUG, "standby check loop...\n");
standby_monitor(); standby_monitor();
} }
else if (node_info.type == WITNESS)
{
log_verbose(LOG_DEBUG, "witness check loop...\n");
witness_monitor();
}
sleep(local_options.monitor_interval_secs); sleep(local_options.monitor_interval_secs);
@@ -667,7 +665,7 @@ witness_monitor(void)
" replication_lag, apply_lag )" " replication_lag, apply_lag )"
" VALUES(%d, %d, " " VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, NULL, " " '%s'::TIMESTAMP WITH TIME ZONE, NULL, "
" pg_current_xlog_location(), NULL, " " pg_catalog.pg_current_xlog_location(), NULL, "
" 0, 0) ", " 0, 0) ",
get_repmgr_schema_quoted(my_local_conn), get_repmgr_schema_quoted(my_local_conn),
master_options.node, master_options.node,
@@ -695,7 +693,7 @@ standby_monitor(void)
{ {
PGresult *res; PGresult *res;
char monitor_standby_timestamp[MAXLEN]; char monitor_standby_timestamp[MAXLEN];
char last_wal_master_location[MAXLEN]; char last_wal_primary_location[MAXLEN];
char last_xlog_receive_location[MAXLEN]; char last_xlog_receive_location[MAXLEN];
char last_xlog_replay_location[MAXLEN]; char last_xlog_replay_location[MAXLEN];
char last_xact_replay_timestamp[MAXLEN]; char last_xact_replay_timestamp[MAXLEN];
@@ -706,6 +704,9 @@ standby_monitor(void)
XLogRecPtr lsn_last_xlog_receive_location; XLogRecPtr lsn_last_xlog_receive_location;
XLogRecPtr lsn_last_xlog_replay_location; XLogRecPtr lsn_last_xlog_replay_location;
long long unsigned int replication_lag;
long long unsigned int apply_lag;
int connection_retries, int connection_retries,
ret; ret;
bool did_retry = false; bool did_retry = false;
@@ -716,8 +717,9 @@ standby_monitor(void)
t_node_info upstream_node; t_node_info upstream_node;
int active_master_id; int active_master_id;
const char *type = NULL; const char *upstream_node_type = NULL;
bool receiving_streamed_wal = true;
/* /*
* Verify that the local node is still available - if not there's * Verify that the local node is still available - if not there's
* no point in doing much else anyway * no point in doing much else anyway
@@ -742,19 +744,19 @@ standby_monitor(void)
upstream_conn = get_upstream_connection(my_local_conn, upstream_conn = get_upstream_connection(my_local_conn,
local_options.cluster_name, local_options.cluster_name,
local_options.node, local_options.node,
&upstream_node_id, upstream_conninfo); &upstream_node_id,
upstream_conninfo);
type = upstream_node_id == master_options.node upstream_node_type = (upstream_node_id == master_options.node)
? "master" ? "master"
: "upstream"; : "upstream";
// ZZZ "5 minutes"?
/* /*
* Check if the upstream node is still available, if after 5 minutes of retries * Check that the upstream node is still available
* we cannot reconnect, try to get a new upstream node. * If not, initiate failover process
*/ */
check_connection(&upstream_conn, type, upstream_conninfo); check_connection(&upstream_conn, upstream_node_type, upstream_conninfo);
/* /*
* This takes up to local_options.reconnect_attempts * * This takes up to local_options.reconnect_attempts *
* local_options.reconnect_interval seconds * local_options.reconnect_interval seconds
@@ -767,7 +769,7 @@ standby_monitor(void)
if (local_options.failover == MANUAL_FAILOVER) if (local_options.failover == MANUAL_FAILOVER)
{ {
log_err(_("Unable to reconnect to %s. Now checking if another node has been promoted.\n"), type); log_err(_("Unable to reconnect to %s. Now checking if another node has been promoted.\n"), upstream_node_type);
for (connection_retries = 0; connection_retries < local_options.reconnect_attempts; connection_retries++) for (connection_retries = 0; connection_retries < local_options.reconnect_attempts; connection_retries++)
{ {
@@ -818,26 +820,24 @@ standby_monitor(void)
else if (local_options.failover == AUTOMATIC_FAILOVER) else if (local_options.failover == AUTOMATIC_FAILOVER)
{ {
/* /*
* When we returns from this function we will have a new master * When we return from this function we will have a new master
* and a new master_conn * and a new master_conn
*/ *
/*
* Failover handling is handled differently depending on whether * Failover handling is handled differently depending on whether
* the failed node is the master or a cascading standby * the failed node is the master or a cascading standby
*/ */
upstream_node = get_node_info(my_local_conn, local_options.cluster_name, node_info.upstream_node_id); upstream_node = get_node_info(my_local_conn, local_options.cluster_name, upstream_node_id);
if (upstream_node.type == MASTER) if (upstream_node.type == MASTER)
{ {
log_debug(_("failure detected on master node (%i); attempting to promote a standby\n"), log_debug(_("failure detected on master node (%i); attempting to promote a standby\n"),
node_info.upstream_node_id); node_info.upstream_node_id);
do_master_failover(); do_master_failover();
} }
else else
{ {
log_debug(_("failure detected on upstream node %i; attempting to reconnect to new upstream node\n"), log_debug(_("failure detected on upstream node %i; attempting to reconnect to new upstream node\n"),
node_info.upstream_node_id); node_info.upstream_node_id);
if (!do_upstream_standby_failover(upstream_node)) if (!do_upstream_standby_failover(upstream_node))
{ {
@@ -845,20 +845,20 @@ standby_monitor(void)
initPQExpBuffer(&errmsg); initPQExpBuffer(&errmsg);
appendPQExpBuffer(&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); log_err("%s\n", errmsg.data);
create_event_record(master_conn, create_event_record(master_conn,
&local_options, &local_options,
local_options.node, local_options.node,
"repmgrd_shutdown", "repmgrd_shutdown",
false, false,
errmsg.data); errmsg.data);
terminate(ERR_DB_CON); terminate(ERR_DB_CON);
} }
} }
return; return;
} }
} }
@@ -929,7 +929,7 @@ standby_monitor(void)
* from the upstream node to write monitoring information * from the upstream node to write monitoring information
*/ */
upstream_node = get_node_info(my_local_conn, local_options.cluster_name, node_info.upstream_node_id); upstream_node = get_node_info(my_local_conn, local_options.cluster_name, upstream_node_id);
sprintf(sqlquery, sprintf(sqlquery,
"SELECT id " "SELECT id "
@@ -961,7 +961,7 @@ standby_monitor(void)
if (active_master_id != master_options.node) 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) if (master_conn != NULL)
{ {
PQfinish(master_conn); PQfinish(master_conn);
@@ -984,9 +984,11 @@ standby_monitor(void)
/* Get local xlog info */ /* Get local xlog info */
sqlquery_snprintf(sqlquery, sqlquery_snprintf(sqlquery,
"SELECT CURRENT_TIMESTAMP, pg_last_xlog_receive_location(), " "SELECT CURRENT_TIMESTAMP, "
"pg_last_xlog_replay_location(), pg_last_xact_replay_timestamp(), " "pg_catalog.pg_last_xlog_receive_location(), "
"pg_last_xlog_receive_location() >= pg_last_xlog_replay_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()");
res = PQexec(my_local_conn, sqlquery); res = PQexec(my_local_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -1001,10 +1003,24 @@ standby_monitor(void)
strncpy(last_xlog_receive_location, PQgetvalue(res, 0, 1), MAXLEN); strncpy(last_xlog_receive_location, PQgetvalue(res, 0, 1), MAXLEN);
strncpy(last_xlog_replay_location, PQgetvalue(res, 0, 2), MAXLEN); strncpy(last_xlog_replay_location, PQgetvalue(res, 0, 2), MAXLEN);
strncpy(last_xact_replay_timestamp, PQgetvalue(res, 0, 3), MAXLEN); strncpy(last_xact_replay_timestamp, PQgetvalue(res, 0, 3), MAXLEN);
last_xlog_receive_location_gte_replayed = (strcmp(PQgetvalue(res, 0, 4), "t") == 0) last_xlog_receive_location_gte_replayed = (strcmp(PQgetvalue(res, 0, 4), "t") == 0)
? true ? true
: false; : 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); PQclear(res);
/* /*
@@ -1016,14 +1032,18 @@ standby_monitor(void)
* PostgreSQL log. In the absence of a better strategy, skip attempting * PostgreSQL log. In the absence of a better strategy, skip attempting
* to insert a monitoring record. * to insert a monitoring record.
*/ */
if (last_xlog_receive_location_gte_replayed == false) if (receiving_streamed_wal == true && last_xlog_receive_location_gte_replayed == false)
{ {
log_verbose(LOG_WARNING, log_verbose(LOG_WARNING,
"Invalid replication_lag value calculated - is this standby connected to its upstream?\n"); "Replayed WAL newer than received WAL - is this standby connected to its upstream?\n");
return;
} }
/* Get master xlog info */ /*
* 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
*/
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()"); sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()");
res = PQexec(master_conn, sqlquery); res = PQexec(master_conn, sqlquery);
@@ -1034,14 +1054,43 @@ standby_monitor(void)
return; return;
} }
strncpy(last_wal_master_location, PQgetvalue(res, 0, 0), MAXLEN); strncpy(last_wal_primary_location, PQgetvalue(res, 0, 0), MAXLEN);
PQclear(res); PQclear(res);
/* Calculate the lag */ lsn_master_current_xlog_location = lsn_to_xlogrecptr(last_wal_primary_location, NULL);
lsn_master_current_xlog_location = lsn_to_xlogrecptr(last_wal_master_location, NULL);
lsn_last_xlog_receive_location = lsn_to_xlogrecptr(last_xlog_receive_location, NULL);
lsn_last_xlog_replay_location = lsn_to_xlogrecptr(last_xlog_replay_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;
}
/* /*
* Build the SQL to execute on master * Build the SQL to execute on master
*/ */
@@ -1068,11 +1117,10 @@ standby_monitor(void)
local_options.node, local_options.node,
monitor_standby_timestamp, monitor_standby_timestamp,
last_xact_replay_timestamp, last_xact_replay_timestamp,
last_wal_master_location, last_wal_primary_location,
last_xlog_receive_location, last_xlog_receive_location,
(long long unsigned int)(lsn_master_current_xlog_location - lsn_last_xlog_receive_location), replication_lag,
(long long unsigned int)(lsn_last_xlog_receive_location - lsn_last_xlog_replay_location)); apply_lag);
/* /*
* Execute the query asynchronously, but don't check for a result. We will * Execute the query asynchronously, but don't check for a result. We will
* check the result next time we pause for a monitor step. * check the result next time we pause for a monitor step.
@@ -1119,8 +1167,8 @@ do_master_failover(void)
*/ */
t_node_info nodes[FAILOVER_NODES_MAX_CHECK]; t_node_info nodes[FAILOVER_NODES_MAX_CHECK];
/* Store details of the failed node here */ /* Store details of the failed node here */
t_node_info failed_master = T_NODE_INFO_INITIALIZER; t_node_info failed_master = T_NODE_INFO_INITIALIZER;
/* Store details of the best candidate for promotion to master here */ /* Store details of the best candidate for promotion to master here */
t_node_info best_candidate = T_NODE_INFO_INITIALIZER; t_node_info best_candidate = T_NODE_INFO_INITIALIZER;
@@ -1130,7 +1178,7 @@ do_master_failover(void)
"SELECT id, conninfo, type, upstream_node_id " "SELECT id, conninfo, type, upstream_node_id "
" FROM %s.repl_nodes " " FROM %s.repl_nodes "
" WHERE cluster = '%s' " " WHERE cluster = '%s' "
" AND active IS TRUE " " AND active IS TRUE "
" AND priority > 0 " " AND priority > 0 "
" ORDER BY priority DESC, id " " ORDER BY priority DESC, id "
" LIMIT %i ", " LIMIT %i ",
@@ -1143,7 +1191,6 @@ do_master_failover(void)
{ {
log_err(_("unable to retrieve node records: %s\n"), PQerrorMessage(my_local_conn)); log_err(_("unable to retrieve node records: %s\n"), PQerrorMessage(my_local_conn));
PQclear(res); PQclear(res);
PQfinish(my_local_conn);
terminate(ERR_DB_QUERY); terminate(ERR_DB_QUERY);
} }
@@ -1423,9 +1470,6 @@ do_master_failover(void)
PQfinish(node_conn); 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 * determine which one is the best candidate to promote to master
@@ -1473,18 +1517,24 @@ do_master_failover(void)
terminate(ERR_FAILOVER_FAIL); 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 local node is the best candidate, promote it */
if (best_candidate.node_id == local_options.node) if (best_candidate.node_id == local_options.node)
{ {
PQExpBufferData event_details; PQExpBufferData event_details;
/* Close the connection to this server */
PQfinish(my_local_conn);
my_local_conn = NULL;
initPQExpBuffer(&event_details); initPQExpBuffer(&event_details);
/* wait */ /* wait */
sleep(5); sleep(5);
log_notice(_("this node is the best candidate to be the new master, promoting...\n")); 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); local_options.promote_command);
if (log_type == REPMGR_STDERR && *local_options.logfile) if (log_type == REPMGR_STDERR && *local_options.logfile)
@@ -1495,6 +1545,33 @@ do_master_failover(void)
r = system(local_options.promote_command); r = system(local_options.promote_command);
if (r != 0) 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")); log_err(_("promote command failed. You could check and try it manually.\n"));
terminate(ERR_DB_QUERY); terminate(ERR_DB_QUERY);
@@ -1526,11 +1603,39 @@ do_master_failover(void)
{ {
PGconn *new_master_conn; PGconn *new_master_conn;
PQExpBufferData event_details; PQExpBufferData event_details;
int master_node_id;
initPQExpBuffer(&event_details); initPQExpBuffer(&event_details);
/* wait */ /* wait */
sleep(10); 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_notice(_("node %d is the best candidate for new master, attempting to follow...\n"),
best_candidate.node_id); best_candidate.node_id);
@@ -1654,7 +1759,7 @@ do_upstream_standby_failover(t_node_info upstream_node)
if (PQntuples(res) == 0) if (PQntuples(res) == 0)
{ {
log_err(_("no node with id %i found"), upstream_node_id); log_err(_("no node with id %i found\n"), upstream_node_id);
PQclear(res); PQclear(res);
return false; return false;
} }
@@ -1819,7 +1924,7 @@ check_connection(PGconn **conn, const char *type, const char *conninfo)
static bool static bool
set_local_node_status(void) set_local_node_status(void)
{ {
PGresult *res; PGresult *res;
char sqlquery[QUERY_STR_LEN]; char sqlquery[QUERY_STR_LEN];
int active_master_node_id = NODE_NOT_FOUND; int active_master_node_id = NODE_NOT_FOUND;
char master_conninfo[MAXLEN]; char master_conninfo[MAXLEN];
@@ -1912,10 +2017,12 @@ check_cluster_configuration(PGconn *conn)
log_info(_("checking cluster configuration with schema '%s'\n"), get_repmgr_schema()); log_info(_("checking cluster configuration with schema '%s'\n"), get_repmgr_schema());
sqlquery_snprintf(sqlquery, sqlquery_snprintf(sqlquery,
"SELECT oid FROM pg_class " "SELECT oid FROM pg_catalog.pg_class "
" WHERE oid = '%s.repl_nodes'::regclass ", " WHERE oid = '%s.repl_nodes'::regclass ",
get_repmgr_schema_quoted(master_conn)); get_repmgr_schema_quoted(master_conn));
res = PQexec(conn, sqlquery); res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
log_err(_("PQexec failed: %s\n"), PQerrorMessage(conn)); log_err(_("PQexec failed: %s\n"), PQerrorMessage(conn));
@@ -2334,12 +2441,14 @@ get_node_info(PGconn *conn, char *cluster, int node_id)
errmsg.data); errmsg.data);
PQfinish(conn); PQfinish(conn);
conn = NULL;
terminate(ERR_DB_QUERY); terminate(ERR_DB_QUERY);
} }
if (res == 0) if (res == 0)
{ {
log_warning(_("No record found record for node %i\n"), node_id); log_warning(_("No record found for node %i\n"), node_id);
} }
return node_info; return node_info;

View File

@@ -63,6 +63,15 @@ UPDATE repl_nodes SET type = 'master' WHERE id = $master_id;
-- UPDATE repl_nodes SET active = FALSE WHERE id IN (...); -- 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 */ /* When you're sure of your changes, commit them */
-- COMMIT; -- COMMIT;

View File

@@ -27,5 +27,6 @@
BEGIN; BEGIN;
ALTER TABLE repl_nodes ALTER CONSTRAINT repl_nodes_upstream_node_id_fkey DEFERRABLE; 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; COMMIT;

View File

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